From 1f42b077c815da05b5ebd2a578c24a617d4e5a55 Mon Sep 17 00:00:00 2001 From: Brent Date: Thu, 15 Feb 2024 15:02:24 +1000 Subject: [PATCH 001/429] Added feature to NugetMetaAnalyzer to exclude pre-release versions. Signed-off-by: Brent --- .../tasks/repositories/NugetMetaAnalyzer.java | 27 +++++++++--- .../repositories/NugetMetaAnalyzerTest.java | 41 +++++++++++++++++++ 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java index cf4343cab5..9f57f5a4f1 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java @@ -111,7 +111,10 @@ private boolean performVersionCheck(final MetaModel meta, final Component compon String responseString = EntityUtils.toString(response.getEntity()); var jsonObject = new JSONObject(responseString); final JSONArray versions = jsonObject.getJSONArray("versions"); - final String latest = findLatestVersion(versions); // get the last version in the array + + final boolean excludePreRelease = component.getPurl().getVersion() != null && component.getPurl().getVersion().contains("-") ==false; // if the version is a pre-release version, we should not exclude pre-release versions + + final String latest = findLatestVersion(versions,excludePreRelease); // get the last version in the array meta.setLatestVersion(latest); } return true; @@ -126,15 +129,17 @@ private boolean performVersionCheck(final MetaModel meta, final Component compon return false; } - private String findLatestVersion(JSONArray versions) { - if (versions.length() < 1) { + private String findLatestVersion(JSONArray versions, boolean excludePreRelease) { + JSONArray filteredVersions = excludePreRelease ? filterPreReleaseVersions(versions) : versions; + + if (filteredVersions.length() < 1) { return null; } - ComparableVersion latestVersion = new ComparableVersion(versions.getString(0)); + ComparableVersion latestVersion = new ComparableVersion(filteredVersions.getString(0)); - for (int i = 1; i < versions.length(); i++) { - ComparableVersion version = new ComparableVersion(versions.getString(i)); + for (int i = 1; i < filteredVersions.length(); i++) { + ComparableVersion version = new ComparableVersion(filteredVersions.getString(i)); if (version.compareTo(latestVersion) > 0) { latestVersion = version; } @@ -143,6 +148,16 @@ private String findLatestVersion(JSONArray versions) { return latestVersion.toString(); } + private JSONArray filterPreReleaseVersions(JSONArray versions) { + JSONArray filteredVersions = new JSONArray(); + for (int i = 0; i < versions.length(); i++) { + if (!versions.getString(i).contains("-")) { + filteredVersions.put(versions.getString(i)); + } + } + return filteredVersions; + } + private boolean performLastPublishedCheck(final MetaModel meta, final Component component) { final String url = String.format(registrationUrl, component.getPurl().getName().toLowerCase(), meta.getLatestVersion()); try (final CloseableHttpResponse response = processHttpRequest(url)) { diff --git a/src/test/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzerTest.java b/src/test/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzerTest.java index e01f2d55ed..a43da84281 100644 --- a/src/test/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzerTest.java +++ b/src/test/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzerTest.java @@ -64,6 +64,47 @@ public void testAnalyzer() throws Exception { Assert.assertNotNull(metaModel.getPublishedTimestamp()); } + + // This test is to check if the analyzer is excluding pre-release versions + // The test is transitent depending on the current version of the package + // retrieved from the repository at the time of running. + // When it was created, the latest release version was 9.0.0-preview.1.24080.9 + //@Test + public void testAnalyzerExcludingPreRelease() throws Exception { + Component component = new Component(); + component.setPurl(new PackageURL("pkg:nuget/Microsoft.Extensions.DependencyInjection@8.0.0")); + NugetMetaAnalyzer analyzer = new NugetMetaAnalyzer(); + + analyzer.setRepositoryBaseUrl("https://api.nuget.org"); + MetaModel metaModel = analyzer.analyze(component); + + Assert.assertTrue(analyzer.isApplicable(component)); + Assert.assertEquals(RepositoryType.NUGET, analyzer.supportedRepositoryType()); + Assert.assertNotNull(metaModel.getLatestVersion()); + + Assert.assertFalse(metaModel.getLatestVersion().contains("-")); + } + + // This test is to check if the analyzer is including pre-release versions + // The test is transitent depending on the current version of the package + // retrieved from the repository at the time of running. + // When it was created, the latest release version was 9.0.0-preview.1.24080.9 + //@Test + public void testAnalyzerIncludingPreRelease() throws Exception { + Component component = new Component(); + component.setPurl(new PackageURL("pkg:nuget/Microsoft.Extensions.DependencyInjection@8.0.0-beta.21301.5")); + NugetMetaAnalyzer analyzer = new NugetMetaAnalyzer(); + + analyzer.setRepositoryBaseUrl("https://api.nuget.org"); + MetaModel metaModel = analyzer.analyze(component); + + Assert.assertTrue(analyzer.isApplicable(component)); + Assert.assertEquals(RepositoryType.NUGET, analyzer.supportedRepositoryType()); + Assert.assertNotNull(metaModel.getLatestVersion()); + + Assert.assertTrue(metaModel.getLatestVersion().contains("-")); + } + @Test public void testAnalyzerWithPrivatePackageRepository() throws Exception { String mockIndexResponse = readResourceFileToString("/unit/tasks/repositories/https---localhost-1080-v3-index.json"); From 7a64a9d76a43a6686f6d40ad1eb261bfea170f02 Mon Sep 17 00:00:00 2001 From: Brent Date: Fri, 1 Mar 2024 09:25:25 +1000 Subject: [PATCH 002/429] Refactored NugetMetaAnalyzer to be inline with Java standards Signed-off-by: Brent --- .../dependencytrack/tasks/repositories/NugetMetaAnalyzer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java index 9f57f5a4f1..96f41b2284 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java @@ -112,7 +112,8 @@ private boolean performVersionCheck(final MetaModel meta, final Component compon var jsonObject = new JSONObject(responseString); final JSONArray versions = jsonObject.getJSONArray("versions"); - final boolean excludePreRelease = component.getPurl().getVersion() != null && component.getPurl().getVersion().contains("-") ==false; // if the version is a pre-release version, we should not exclude pre-release versions + // if the version is a pre-release version, we should not exclude pre-release versions + final boolean excludePreRelease = component.getPurl().getVersion() != null && !component.getPurl().getVersion().contains("-"); final String latest = findLatestVersion(versions,excludePreRelease); // get the last version in the array meta.setLatestVersion(latest); From 138691db074612a8052a885031e4debfca595fa3 Mon Sep 17 00:00:00 2001 From: RBickert Date: Tue, 14 Feb 2023 10:11:57 +0100 Subject: [PATCH 003/429] Global Audit View: Policy Violations Enhances the `getViolations` method in `PolicyViolationResource` to filter the result by ACL and to also allow the use of additional filters, so that a user can get more specific results. Signed-off-by: RBickert --- .../persistence/PolicyQueryManager.java | 253 +++++++++++++++++- .../persistence/QueryManager.java | 4 +- .../resources/v1/PolicyViolationResource.java | 33 ++- 3 files changed, 280 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index 8ec4aabea8..a11096a851 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -18,9 +18,16 @@ */ package org.dependencytrack.persistence; +import alpine.common.logging.Logger; +import alpine.model.ApiKey; +import alpine.model.Team; +import alpine.model.UserPrincipal; import alpine.persistence.PaginatedResult; import alpine.resources.AlpineRequest; +import alpine.server.util.DbUtil; +import org.apache.commons.lang3.StringUtils; import org.dependencytrack.model.Component; +import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.License; import org.dependencytrack.model.LicenseGroup; import org.dependencytrack.model.Policy; @@ -30,15 +37,40 @@ import org.dependencytrack.model.ViolationAnalysis; import org.dependencytrack.model.ViolationAnalysisComment; import org.dependencytrack.model.ViolationAnalysisState; +import org.dependencytrack.util.DateUtil; import javax.jdo.PersistenceManager; import javax.jdo.Query; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.UUID; final class PolicyQueryManager extends QueryManager implements IQueryManager { + public static final String QUERY_ACL_1 = """ + "DESCENDANTS" ("ID", "NAME") AS + (SELECT "PROJECT"."ID", + "PROJECT"."NAME" + FROM "PROJECT" + """; + + public static final String QUERY_ACL_2 = """ + UNION ALL + SELECT "CHILD"."ID", + "CHILD"."NAME" + FROM "PROJECT" "CHILD" + JOIN "DESCENDANTS" + ON "DESCENDANTS"."ID" = "CHILD"."PARENT_PROJECT_ID") + SELECT "DESCENDANTS"."ID", "DESCENDANTS"."NAME" FROM "DESCENDANTS" + """; + private static final Logger LOGGER = Logger.getLogger(PolicyQueryManager.class); + /** * Constructs a new QueryManager. * @param pm a PersistenceManager object @@ -346,27 +378,167 @@ public PaginatedResult getPolicyViolations(final Component component, boolean in } /** - * Returns a List of all Policy violations for the entire portfolio. + * Returns a List of all Policy violations for the entire portfolio filtered by ACL and other optional filters. * @return a List of all Policy violations */ @SuppressWarnings("unchecked") - public PaginatedResult getPolicyViolations(boolean includeSuppressed) { + public PaginatedResult getPolicyViolations(boolean includeSuppressed, boolean showInactive, Map filters) { + final PaginatedResult result; final Query query = pm.newQuery(PolicyViolation.class); + final Map params = new HashMap<>(); + final List filterCriteria = new ArrayList<>(); if (!includeSuppressed) { - query.setFilter("analysis.suppressed == false || analysis.suppressed == null"); + filterCriteria.add("(analysis.suppressed == false || analysis.suppressed == null)"); + } + if (!showInactive) { + filterCriteria.add("(project.active == true || project.active == null)"); } + processViolationsFilters(filters, params, filterCriteria); if (orderBy == null) { query.setOrdering("timestamp desc, project.name, project.version, component.name, component.version"); } - final PaginatedResult result = execute(query); + final String queryFilter = String.join(" && ", filterCriteria); + preprocessACLs(query, queryFilter, params, false); + result = execute(query, params); for (final PolicyViolation violation: result.getList(PolicyViolation.class)) { - violation.getPolicyCondition().getPolicy(); // force policy to ne included since its not the default - violation.getComponent().getResolvedLicense(); // force resolved license to ne included since its not the default + violation.getPolicyCondition().getPolicy(); // force policy to be included since it's not the default + violation.getComponent().getResolvedLicense(); // force resolved license to be included since it's not the default violation.setAnalysis(getViolationAnalysis(violation.getComponent(), violation)); // Include the violation analysis by default } return result; } + private void preprocessACLs(final Query query, final String inputFilter, final Map params, final boolean bypass) { + if (super.principal != null && isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED) && !bypass) { + final List teams; + if (super.principal instanceof UserPrincipal) { + final UserPrincipal userPrincipal = ((UserPrincipal) super.principal); + teams = userPrincipal.getTeams(); + if (super.hasAccessManagementPermission(userPrincipal)) { + query.setFilter(inputFilter); + return; + } + } else { + final ApiKey apiKey = ((ApiKey) super.principal); + teams = apiKey.getTeams(); + if (super.hasAccessManagementPermission(apiKey)) { + query.setFilter(inputFilter); + return; + } + } + + // Query every project that the teams have access to + final Map tempParams = new HashMap<>(); + final Query queryAclProjects = pm.newQuery(Project.class); + if (teams != null && teams.size() > 0) { + final StringBuilder stringBuilderAclProjects = new StringBuilder(); + for (int i = 0, teamsSize = teams.size(); i < teamsSize; i++) { + final Team team = super.getObjectById(Team.class, teams.get(i).getId()); + stringBuilderAclProjects.append(" accessTeams.contains(:team").append(i).append(") "); + tempParams.put("team" + i, team); + if (i < teamsSize-1) { + stringBuilderAclProjects.append(" || "); + } + } + queryAclProjects.setFilter(stringBuilderAclProjects.toString()); + } else { + if (inputFilter != null && !inputFilter.isEmpty()) { + query.setFilter(inputFilter + " && :false"); + } else { + query.setFilter(":false"); + } + params.put("false", false); + return; + } + List result = (List) queryAclProjects.executeWithMap(tempParams); + // Query the descendants of the projects that the teams have access to + if (result != null && !result.isEmpty()) { + final StringBuilder stringBuilderDescendants = new StringBuilder(); + final List parameters = new ArrayList<>(); + stringBuilderDescendants.append("WHERE"); + int i = 0, teamSize = result.size(); + for (Project project : result) { + stringBuilderDescendants.append(" \"ID\" = ?").append(" "); + parameters.add(project.getId()); + if (i < teamSize-1) { + stringBuilderDescendants.append(" OR"); + } + i++; + } + stringBuilderDescendants.append("\n"); + final List results = new ArrayList<>(); + + // Querying the descendants of projects requires a CTE (Common Table Expression), which needs to be at the top-level of the query for Microsoft SQL Server. + // Because of JDO, queries are only allowed to start with "SELECT", so the "WITH" clause for the CTE in MSSQL cannot be at top level. + // Activating the JDO property that queries don't have to start with "SELECT" does not help in this case, because JDO queries that do not start with "SELECT" only return "true", so no data can be fetched this way. + // To circumvent this problem, the query is executed via the direct connection to the database and not via JDO. + Connection connection = null; + PreparedStatement preparedStatement = null; + ResultSet rs = null; + try { + connection = (Connection) pm.getDataStoreConnection(); + if (DbUtil.isMssql() || DbUtil.isOracle()) { // Microsoft SQL Server and Oracle DB already imply the "RECURSIVE" keyword in the "WITH" clause, therefore it is not needed in the query + preparedStatement = connection.prepareStatement("WITH " + QUERY_ACL_1 + stringBuilderDescendants + QUERY_ACL_2); + } else { // Other Databases need the "RECURSIVE" keyword in the "WITH" clause to correctly execute the query + preparedStatement = connection.prepareStatement("WITH RECURSIVE " + QUERY_ACL_1 + stringBuilderDescendants + QUERY_ACL_2); + } + int j = 1; + for (Long id : parameters) { + preparedStatement.setLong(j, id); + j++; + } + preparedStatement.execute(); + rs = preparedStatement.getResultSet(); + while (rs.next()) { + results.add(rs.getLong(1)); + } + } catch (Exception e) { + LOGGER.error(e.getMessage()); + if (inputFilter != null && !inputFilter.isEmpty()) { + query.setFilter(inputFilter + " && :false"); + } else { + query.setFilter(":false"); + } + params.put("false", false); + return; + } finally { + DbUtil.close(rs); + DbUtil.close(preparedStatement); + DbUtil.close(connection); + } + + // Add queried projects and descendants to the input filter of the query + if (results != null && !results.isEmpty()) { + final StringBuilder stringBuilderInputFilter = new StringBuilder(); + int j = 0; + int resultSize = results.size(); + for (Long id : results) { + stringBuilderInputFilter.append(" project.id == :id").append(j); + params.put("id" + j, id); + if (j < resultSize-1) { + stringBuilderInputFilter.append(" || "); + } + j++; + } + if (inputFilter != null && !inputFilter.isEmpty()) { + query.setFilter(inputFilter + " && (" + stringBuilderInputFilter.toString() + ")"); + } else { + query.setFilter(stringBuilderInputFilter.toString()); + } + } + } else { + if (inputFilter != null && !inputFilter.isEmpty()) { + query.setFilter(inputFilter + " && :false"); + } else { + query.setFilter(":false"); + } + params.put("false", false); + } + } else if (StringUtils.trimToNull(inputFilter) != null) { + query.setFilter(inputFilter); + } + } + /** * clones a ViolationAnalysis * @param destinationComponent the destinationComponent @@ -611,4 +783,73 @@ public long getAuditedCount(final Component component, final PolicyViolation.Typ return getCount(query, component, type, ViolationAnalysisState.NOT_SET); } + private void processViolationsFilters(Map filters, Map params, List filterCriteria) { + for (Map.Entry filter : filters.entrySet()) { + switch (filter.getKey()) { + case "violationState" -> processArrayFilter(params, filterCriteria, "violationState", filter.getValue(), "policyCondition.policy.violationState"); + case "riskType" -> processArrayFilter(params, filterCriteria, "riskType", filter.getValue(), "type"); + case "policy" -> processArrayFilter(params, filterCriteria, "policy", filter.getValue(), "policyCondition.policy.uuid"); + case "analysisState" -> processArrayFilter(params, filterCriteria, "analysisState", filter.getValue(), "analysis.analysisState"); + case "occurredOnDateFrom" -> processDateFilter(params, filterCriteria, "occuredOnDateFrom", filter.getValue(), true); + case "occurredOnDateTo" -> processDateFilter(params, filterCriteria, "occuredOnDateTo", filter.getValue(), false); + case "textSearchField" -> processInputFilter(params, filterCriteria, "textInput", filter.getValue(), filters.get("textSearchInput")); + } + } + } + + private void processArrayFilter(Map params, List filterCriteria, String paramName, String filter, String column) { + if (filter != null && !filter.isEmpty()) { + StringBuilder filterBuilder = new StringBuilder("("); + String[] arrayFilter = filter.split(","); + for (int i = 0, arrayFilterLength = arrayFilter.length; i < arrayFilterLength; i++) { + filterBuilder.append(column).append(" == :").append(paramName).append(i); + switch (paramName) { + case "violationState" -> params.put(paramName + i, Policy.ViolationState.valueOf(arrayFilter[i])); + case "riskType" -> params.put(paramName + i, PolicyViolation.Type.valueOf(arrayFilter[i])); + case "policy" -> params.put(paramName + i, UUID.fromString(arrayFilter[i])); + case "analysisState" -> { + if (arrayFilter[i].equals("NOT_SET")) { + filterBuilder.append(" || ").append(column).append(" == null"); + } + params.put(paramName + i, ViolationAnalysisState.valueOf(arrayFilter[i])); + } + } + if (i < arrayFilterLength - 1) { + filterBuilder.append(" || "); + } + } + filterBuilder.append(")"); + filterCriteria.add(filterBuilder.toString()); + } + } + + private void processDateFilter(Map params, List filterCriteria, String paramName, String filter, boolean fromValue) { + if (filter != null && !filter.isEmpty()) { + params.put(paramName, DateUtil.fromISO8601(filter + (fromValue ? "T00:00:00" : "T23:59:59"))); + filterCriteria.add("(timestamp " + (fromValue ? ">= :" : "<= :") + paramName + ")"); + } + } + + private void processInputFilter(Map params, List filterCriteria, String paramName, String filter, String input) { + if (filter != null && !filter.isEmpty() && input != null && !input.isEmpty()) { + StringBuilder filterBuilder = new StringBuilder("("); + String[] inputFilter = filter.split(","); + for (int i = 0, inputFilterLength = inputFilter.length; i < inputFilterLength; i++) { + switch (inputFilter[i].toLowerCase()) { + case "policy_name" -> filterBuilder.append("policyCondition.policy.name"); + case "component" -> filterBuilder.append("component.name"); + case "license" -> filterBuilder.append("component.resolvedLicense.licenseId"); + case "project_name" -> filterBuilder.append("project.name.toLowerCase().matches(:").append(paramName).append(") || project.version"); + } + filterBuilder.append(".toLowerCase().matches(:").append(paramName).append(")"); + if (i < inputFilterLength - 1) { + filterBuilder.append(" || "); + } + } + params.put(paramName, ".*" + input.toLowerCase() + ".*"); + filterBuilder.append(")"); + filterCriteria.add(filterBuilder.toString()); + } + } + } diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index 4c129e5875..12a9fcd20d 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -655,8 +655,8 @@ public PaginatedResult getPolicyViolations(final Component component, boolean in return getPolicyQueryManager().getPolicyViolations(component, includeSuppressed); } - public PaginatedResult getPolicyViolations(boolean includeSuppressed) { - return getPolicyQueryManager().getPolicyViolations(includeSuppressed); + public PaginatedResult getPolicyViolations(boolean includeSuppressed, boolean showInactive, Map filters) { + return getPolicyQueryManager().getPolicyViolations(includeSuppressed, showInactive, filters); } public ViolationAnalysis getViolationAnalysis(Component component, PolicyViolation policyViolation) { diff --git a/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java b/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java index 84d03a10d1..69bb9eeb20 100644 --- a/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java @@ -44,6 +44,8 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; /** * JAX-RS resources for processing policy violations. @@ -68,9 +70,36 @@ public class PolicyViolationResource extends AlpineResource { }) @PermissionRequired(Permissions.Constants.VIEW_POLICY_VIOLATION) public Response getViolations(@ApiParam(value = "Optionally includes suppressed violations") - @QueryParam("suppressed") boolean suppressed) { + @QueryParam("suppressed") boolean suppressed, + @ApiParam(value = "Optionally includes inactive projects") + @QueryParam("showInactive") boolean showInactive, + @ApiParam(value = "Filter by violation state") + @QueryParam("violationState") String violationState, + @ApiParam(value = "Filter by risk type") + @QueryParam("riskType") String riskType, + @ApiParam(value = "Filter by policy") + @QueryParam("policy") String policy, + @ApiParam(value = "Filter by analysis state") + @QueryParam("analysisState") String analysisState, + @ApiParam(value = "Filter occurred on from") + @QueryParam("occurredOnDateFrom") String occurredOnDateFrom, + @ApiParam(value = "Filter occurred on to") + @QueryParam("occurredOnDateTo") String occurredOnDateTo, + @ApiParam(value = "Filter the text input in these fields") + @QueryParam("textSearchField") String textSearchField, + @ApiParam(value = "Filter by this text input") + @QueryParam("textSearchInput") String textSearchInput) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { - final PaginatedResult result = qm.getPolicyViolations(suppressed); + Map filters = new HashMap<>(); + filters.put("violationState", violationState); + filters.put("riskType", riskType); + filters.put("policy", policy); + filters.put("analysisState", analysisState); + filters.put("occurredOnDateFrom", occurredOnDateFrom); + filters.put("occurredOnDateTo", occurredOnDateTo); + filters.put("textSearchField", textSearchField); + filters.put("textSearchInput", textSearchInput); + final PaginatedResult result = qm.getPolicyViolations(suppressed, showInactive, filters); return Response.ok(detachViolations(qm, result.getList(PolicyViolation.class))) .header(TOTAL_COUNT_HEADER, result.getTotal()) .build(); From dcf10006a751e8c02a3051f4099191c8733f1a0d Mon Sep 17 00:00:00 2001 From: RBickert Date: Tue, 14 Feb 2023 10:13:03 +0100 Subject: [PATCH 004/429] Add test for PolicyViolationResource Adds a new test for `PolicyViolationResource` which tests the filtering by ACL of the newly enhanced `getViolations` method Signed-off-by: RBickert --- .../persistence/ComponentQueryManager.java | 1 + .../persistence/PolicyQueryManager.java | 263 +++++++++--------- .../persistence/ProjectQueryManager.java | 1 + .../v1/PolicyViolationResourceTest.java | 136 +++++++++ 4 files changed, 270 insertions(+), 131 deletions(-) diff --git a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java index 0abc1b08c8..6e2f4e2273 100644 --- a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java @@ -688,6 +688,7 @@ public void reconcileComponents(Project project, List existingProject /** * A similar method exists in ProjectQueryManager */ + // TODO: Move redundant method `preprocessACLs` into own utility class (AclUtil) once #2407 and #2472 are merged. private void preprocessACLs(final Query query, final String inputFilter, final Map params, final boolean bypass) { if (super.principal != null && isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED) && !bypass) { final List teams; diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index a11096a851..6c6ec0c5cc 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -408,137 +408,6 @@ public PaginatedResult getPolicyViolations(boolean includeSuppressed, boolean sh return result; } - private void preprocessACLs(final Query query, final String inputFilter, final Map params, final boolean bypass) { - if (super.principal != null && isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED) && !bypass) { - final List teams; - if (super.principal instanceof UserPrincipal) { - final UserPrincipal userPrincipal = ((UserPrincipal) super.principal); - teams = userPrincipal.getTeams(); - if (super.hasAccessManagementPermission(userPrincipal)) { - query.setFilter(inputFilter); - return; - } - } else { - final ApiKey apiKey = ((ApiKey) super.principal); - teams = apiKey.getTeams(); - if (super.hasAccessManagementPermission(apiKey)) { - query.setFilter(inputFilter); - return; - } - } - - // Query every project that the teams have access to - final Map tempParams = new HashMap<>(); - final Query queryAclProjects = pm.newQuery(Project.class); - if (teams != null && teams.size() > 0) { - final StringBuilder stringBuilderAclProjects = new StringBuilder(); - for (int i = 0, teamsSize = teams.size(); i < teamsSize; i++) { - final Team team = super.getObjectById(Team.class, teams.get(i).getId()); - stringBuilderAclProjects.append(" accessTeams.contains(:team").append(i).append(") "); - tempParams.put("team" + i, team); - if (i < teamsSize-1) { - stringBuilderAclProjects.append(" || "); - } - } - queryAclProjects.setFilter(stringBuilderAclProjects.toString()); - } else { - if (inputFilter != null && !inputFilter.isEmpty()) { - query.setFilter(inputFilter + " && :false"); - } else { - query.setFilter(":false"); - } - params.put("false", false); - return; - } - List result = (List) queryAclProjects.executeWithMap(tempParams); - // Query the descendants of the projects that the teams have access to - if (result != null && !result.isEmpty()) { - final StringBuilder stringBuilderDescendants = new StringBuilder(); - final List parameters = new ArrayList<>(); - stringBuilderDescendants.append("WHERE"); - int i = 0, teamSize = result.size(); - for (Project project : result) { - stringBuilderDescendants.append(" \"ID\" = ?").append(" "); - parameters.add(project.getId()); - if (i < teamSize-1) { - stringBuilderDescendants.append(" OR"); - } - i++; - } - stringBuilderDescendants.append("\n"); - final List results = new ArrayList<>(); - - // Querying the descendants of projects requires a CTE (Common Table Expression), which needs to be at the top-level of the query for Microsoft SQL Server. - // Because of JDO, queries are only allowed to start with "SELECT", so the "WITH" clause for the CTE in MSSQL cannot be at top level. - // Activating the JDO property that queries don't have to start with "SELECT" does not help in this case, because JDO queries that do not start with "SELECT" only return "true", so no data can be fetched this way. - // To circumvent this problem, the query is executed via the direct connection to the database and not via JDO. - Connection connection = null; - PreparedStatement preparedStatement = null; - ResultSet rs = null; - try { - connection = (Connection) pm.getDataStoreConnection(); - if (DbUtil.isMssql() || DbUtil.isOracle()) { // Microsoft SQL Server and Oracle DB already imply the "RECURSIVE" keyword in the "WITH" clause, therefore it is not needed in the query - preparedStatement = connection.prepareStatement("WITH " + QUERY_ACL_1 + stringBuilderDescendants + QUERY_ACL_2); - } else { // Other Databases need the "RECURSIVE" keyword in the "WITH" clause to correctly execute the query - preparedStatement = connection.prepareStatement("WITH RECURSIVE " + QUERY_ACL_1 + stringBuilderDescendants + QUERY_ACL_2); - } - int j = 1; - for (Long id : parameters) { - preparedStatement.setLong(j, id); - j++; - } - preparedStatement.execute(); - rs = preparedStatement.getResultSet(); - while (rs.next()) { - results.add(rs.getLong(1)); - } - } catch (Exception e) { - LOGGER.error(e.getMessage()); - if (inputFilter != null && !inputFilter.isEmpty()) { - query.setFilter(inputFilter + " && :false"); - } else { - query.setFilter(":false"); - } - params.put("false", false); - return; - } finally { - DbUtil.close(rs); - DbUtil.close(preparedStatement); - DbUtil.close(connection); - } - - // Add queried projects and descendants to the input filter of the query - if (results != null && !results.isEmpty()) { - final StringBuilder stringBuilderInputFilter = new StringBuilder(); - int j = 0; - int resultSize = results.size(); - for (Long id : results) { - stringBuilderInputFilter.append(" project.id == :id").append(j); - params.put("id" + j, id); - if (j < resultSize-1) { - stringBuilderInputFilter.append(" || "); - } - j++; - } - if (inputFilter != null && !inputFilter.isEmpty()) { - query.setFilter(inputFilter + " && (" + stringBuilderInputFilter.toString() + ")"); - } else { - query.setFilter(stringBuilderInputFilter.toString()); - } - } - } else { - if (inputFilter != null && !inputFilter.isEmpty()) { - query.setFilter(inputFilter + " && :false"); - } else { - query.setFilter(":false"); - } - params.put("false", false); - } - } else if (StringUtils.trimToNull(inputFilter) != null) { - query.setFilter(inputFilter); - } - } - /** * clones a ViolationAnalysis * @param destinationComponent the destinationComponent @@ -852,4 +721,136 @@ private void processInputFilter(Map params, List filterC } } + // TODO: Move redundant method `preprocessACLs` into own utility class (AclUtil) once #2407 and #2472 are merged. + private void preprocessACLs(final Query query, final String inputFilter, final Map params, final boolean bypass) { + if (super.principal != null && isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED) && !bypass) { + final List teams; + if (super.principal instanceof UserPrincipal) { + final UserPrincipal userPrincipal = ((UserPrincipal) super.principal); + teams = userPrincipal.getTeams(); + if (super.hasAccessManagementPermission(userPrincipal)) { + query.setFilter(inputFilter); + return; + } + } else { + final ApiKey apiKey = ((ApiKey) super.principal); + teams = apiKey.getTeams(); + if (super.hasAccessManagementPermission(apiKey)) { + query.setFilter(inputFilter); + return; + } + } + + // Query every project that the teams have access to + final Map tempParams = new HashMap<>(); + final Query queryAclProjects = pm.newQuery(Project.class); + if (teams != null && teams.size() > 0) { + final StringBuilder stringBuilderAclProjects = new StringBuilder(); + for (int i = 0, teamsSize = teams.size(); i < teamsSize; i++) { + final Team team = super.getObjectById(Team.class, teams.get(i).getId()); + stringBuilderAclProjects.append(" accessTeams.contains(:team").append(i).append(") "); + tempParams.put("team" + i, team); + if (i < teamsSize-1) { + stringBuilderAclProjects.append(" || "); + } + } + queryAclProjects.setFilter(stringBuilderAclProjects.toString()); + } else { + if (inputFilter != null && !inputFilter.isEmpty()) { + query.setFilter(inputFilter + " && :false"); + } else { + query.setFilter(":false"); + } + params.put("false", false); + return; + } + List result = (List) queryAclProjects.executeWithMap(tempParams); + // Query the descendants of the projects that the teams have access to + if (result != null && !result.isEmpty()) { + final StringBuilder stringBuilderDescendants = new StringBuilder(); + final List parameters = new ArrayList<>(); + stringBuilderDescendants.append("WHERE"); + int i = 0, teamSize = result.size(); + for (Project project : result) { + stringBuilderDescendants.append(" \"ID\" = ?").append(" "); + parameters.add(project.getId()); + if (i < teamSize-1) { + stringBuilderDescendants.append(" OR"); + } + i++; + } + stringBuilderDescendants.append("\n"); + final List results = new ArrayList<>(); + + // Querying the descendants of projects requires a CTE (Common Table Expression), which needs to be at the top-level of the query for Microsoft SQL Server. + // Because of JDO, queries are only allowed to start with "SELECT", so the "WITH" clause for the CTE in MSSQL cannot be at top level. + // Activating the JDO property that queries don't have to start with "SELECT" does not help in this case, because JDO queries that do not start with "SELECT" only return "true", so no data can be fetched this way. + // To circumvent this problem, the query is executed via the direct connection to the database and not via JDO. + Connection connection = null; + PreparedStatement preparedStatement = null; + ResultSet rs = null; + try { + connection = (Connection) pm.getDataStoreConnection(); + if (DbUtil.isMssql() || DbUtil.isOracle()) { // Microsoft SQL Server and Oracle DB already imply the "RECURSIVE" keyword in the "WITH" clause, therefore it is not needed in the query + preparedStatement = connection.prepareStatement("WITH " + QUERY_ACL_1 + stringBuilderDescendants + QUERY_ACL_2); + } else { // Other Databases need the "RECURSIVE" keyword in the "WITH" clause to correctly execute the query + preparedStatement = connection.prepareStatement("WITH RECURSIVE " + QUERY_ACL_1 + stringBuilderDescendants + QUERY_ACL_2); + } + int j = 1; + for (Long id : parameters) { + preparedStatement.setLong(j, id); + j++; + } + preparedStatement.execute(); + rs = preparedStatement.getResultSet(); + while (rs.next()) { + results.add(rs.getLong(1)); + } + } catch (Exception e) { + LOGGER.error(e.getMessage()); + if (inputFilter != null && !inputFilter.isEmpty()) { + query.setFilter(inputFilter + " && :false"); + } else { + query.setFilter(":false"); + } + params.put("false", false); + return; + } finally { + DbUtil.close(rs); + DbUtil.close(preparedStatement); + DbUtil.close(connection); + } + + // Add queried projects and descendants to the input filter of the query + if (results != null && !results.isEmpty()) { + final StringBuilder stringBuilderInputFilter = new StringBuilder(); + int j = 0; + int resultSize = results.size(); + for (Long id : results) { + stringBuilderInputFilter.append(" project.id == :id").append(j); + params.put("id" + j, id); + if (j < resultSize-1) { + stringBuilderInputFilter.append(" || "); + } + j++; + } + if (inputFilter != null && !inputFilter.isEmpty()) { + query.setFilter(inputFilter + " && (" + stringBuilderInputFilter.toString() + ")"); + } else { + query.setFilter(stringBuilderInputFilter.toString()); + } + } + } else { + if (inputFilter != null && !inputFilter.isEmpty()) { + query.setFilter(inputFilter + " && :false"); + } else { + query.setFilter(":false"); + } + params.put("false", false); + } + } else if (StringUtils.trimToNull(inputFilter) != null) { + query.setFilter(inputFilter); + } + } + } diff --git a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java index 1cfbfaf7c8..563a616811 100644 --- a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java @@ -893,6 +893,7 @@ public boolean hasAccess(final Principal principal, final Project project) { /** * A similar method exists in ComponentQueryManager */ + // TODO: Move redundant method `preprocessACLs` into own utility class (AclUtil) once #2407 and #2472 are merged. private void preprocessACLs(final Query query, final String inputFilter, final Map params, final boolean bypass) { if (super.principal != null && isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED) && !bypass) { final List teams; diff --git a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java index c5df56ed33..526018c917 100644 --- a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java @@ -18,12 +18,14 @@ */ package org.dependencytrack.resources.v1; +import alpine.model.ConfigProperty; import alpine.server.filters.ApiFilter; import alpine.server.filters.AuthenticationFilter; import alpine.server.filters.AuthorizationFilter; import org.dependencytrack.ResourceTest; import org.dependencytrack.auth.Permissions; import org.dependencytrack.model.Component; +import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.Policy; import org.dependencytrack.model.PolicyCondition; import org.dependencytrack.model.PolicyViolation; @@ -311,4 +313,138 @@ public void getViolationsByComponentNotFoundTest() { assertThat(getPlainTextBody(response)).contains("component could not be found"); } + @Test + public void getViolationsWithAclEnabledTest() { + initializeWithPermissions(Permissions.VIEW_POLICY_VIOLATION); + + final Project project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); + final Project child = qm.createProject("Acme Example - Child", null, "1.0", null, null, null, true, false); + final Project grandchild = qm.createProject("Acme Example - Grandchild", null, "1.0", null, null, null, true, false); + final Project noAccess = qm.createProject("Acme Example - No Access", null, "1.0", null, null, null, true, false); + + var component = new Component(); + component.setProject(project); + component.setName("Acme Component"); + component.setVersion("1.0"); + component = qm.createComponent(component, false); + + var component1 = new Component(); + component1.setProject(child); + component1.setName("Acme Component"); + component1.setVersion("1.0"); + component1 = qm.createComponent(component1, false); + + var component2 = new Component(); + component2.setProject(grandchild); + component2.setName("Acme Component"); + component2.setVersion("1.0"); + component2 = qm.createComponent(component2, false); + + var component3 = new Component(); + component3.setProject(noAccess); + component3.setName("Acme Component"); + component3.setVersion("1.0"); + component3 = qm.createComponent(component3, false); + + final Policy policy = qm.createPolicy("Blacklisted Version", Policy.Operator.ALL, Policy.ViolationState.FAIL); + final PolicyCondition condition = qm.createPolicyCondition(policy, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + + var violation = new PolicyViolation(); + violation.setType(PolicyViolation.Type.OPERATIONAL); + violation.setComponent(component); + violation.setPolicyCondition(condition); + violation.setTimestamp(new Date()); + violation = qm.persist(violation); + + var violation1 = new PolicyViolation(); + violation1.setType(PolicyViolation.Type.OPERATIONAL); + violation1.setComponent(component1); + violation1.setPolicyCondition(condition); + violation1.setTimestamp(new Date()); + violation1 = qm.persist(violation1); + + var violation2 = new PolicyViolation(); + violation2.setType(PolicyViolation.Type.OPERATIONAL); + violation2.setComponent(component2); + violation2.setPolicyCondition(condition); + violation2.setTimestamp(new Date()); + violation2 = qm.persist(violation2); + + var violation3 = new PolicyViolation(); + violation3.setType(PolicyViolation.Type.OPERATIONAL); + violation3.setComponent(component3); + violation3.setPolicyCondition(condition); + violation3.setTimestamp(new Date()); + violation3 = qm.persist(violation3); + + ConfigProperty aclToggle = qm.getConfigProperty(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName()); + if (aclToggle == null) { + qm.createConfigProperty(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), "true", ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + } else { + aclToggle.setPropertyValue("true"); + qm.persist(aclToggle); + } + + project.addAccessTeam(team); + + final Response response = target(V1_POLICY_VIOLATION) + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + + final JsonArray jsonArray = parseJsonArray(response); + assertThat(jsonArray).hasSize(1); + + final JsonObject jsonObject = jsonArray.getJsonObject(0); + assertThat(jsonObject.getString("uuid")).isEqualTo(violation.getUuid().toString()); + assertThat(jsonObject.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); + assertThat(jsonObject.getJsonObject("policyCondition")).isNotNull(); + assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); + assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); + assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); + assertThat(jsonObject.getJsonObject("project").getString("uuid")).isEqualTo(project.getUuid().toString()); + + child.setParent(project); + grandchild.setParent(child); + + final Response response1 = target(V1_POLICY_VIOLATION) + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response1.getStatus()).isEqualTo(200); + assertThat(response1.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("3"); + + final JsonArray jsonArray1 = parseJsonArray(response1); + assertThat(jsonArray1).hasSize(3); + + final JsonObject jsonObject1 = jsonArray1.getJsonObject(0); + assertThat(jsonObject1.getString("uuid")).isEqualTo(violation2.getUuid().toString()); + assertThat(jsonObject1.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); + assertThat(jsonObject1.getJsonObject("policyCondition")).isNotNull(); + assertThat(jsonObject1.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); + assertThat(jsonObject1.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); + assertThat(jsonObject1.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); + assertThat(jsonObject1.getJsonObject("project").getString("uuid")).isEqualTo(grandchild.getUuid().toString()); + + final JsonObject jsonObject2 = jsonArray1.getJsonObject(1); + assertThat(jsonObject2.getString("uuid")).isEqualTo(violation1.getUuid().toString()); + assertThat(jsonObject2.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); + assertThat(jsonObject2.getJsonObject("policyCondition")).isNotNull(); + assertThat(jsonObject2.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); + assertThat(jsonObject2.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); + assertThat(jsonObject2.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); + assertThat(jsonObject2.getJsonObject("project").getString("uuid")).isEqualTo(child.getUuid().toString()); + + final JsonObject jsonObject3 = jsonArray1.getJsonObject(2); + assertThat(jsonObject3.getString("uuid")).isEqualTo(violation.getUuid().toString()); + assertThat(jsonObject3.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); + assertThat(jsonObject3.getJsonObject("policyCondition")).isNotNull(); + assertThat(jsonObject3.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); + assertThat(jsonObject3.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); + assertThat(jsonObject3.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); + assertThat(jsonObject3.getJsonObject("project").getString("uuid")).isEqualTo(project.getUuid().toString()); + } + } \ No newline at end of file From 9269a993161ebe64a01a23a74f6dcd43f2c1a528 Mon Sep 17 00:00:00 2001 From: RBickert Date: Tue, 14 Feb 2023 14:59:01 +0100 Subject: [PATCH 005/429] Include unresolved license in filter Signed-off-by: RBickert --- .../org/dependencytrack/persistence/PolicyQueryManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index 6c6ec0c5cc..a6ca8e1368 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -707,7 +707,7 @@ private void processInputFilter(Map params, List filterC switch (inputFilter[i].toLowerCase()) { case "policy_name" -> filterBuilder.append("policyCondition.policy.name"); case "component" -> filterBuilder.append("component.name"); - case "license" -> filterBuilder.append("component.resolvedLicense.licenseId"); + case "license" -> filterBuilder.append("component.resolvedLicense.licenseId.toLowerCase().matches(:").append(paramName).append(") || component.license"); case "project_name" -> filterBuilder.append("project.name.toLowerCase().matches(:").append(paramName).append(") || project.version"); } filterBuilder.append(".toLowerCase().matches(:").append(paramName).append(")"); From 57c785b248e2d0142a9454fdcfffad9aaca5cb7d Mon Sep 17 00:00:00 2001 From: RBickert Date: Tue, 12 Mar 2024 14:13:38 +0100 Subject: [PATCH 006/429] Revert changes to `preprocessACLs` Signed-off-by: RBickert --- .../persistence/ComponentQueryManager.java | 1 - .../persistence/PolicyQueryManager.java | 133 +---------- .../v1/PolicyViolationResourceTest.java | 206 ++++++++---------- 3 files changed, 99 insertions(+), 241 deletions(-) diff --git a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java index 6e2f4e2273..0abc1b08c8 100644 --- a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java @@ -688,7 +688,6 @@ public void reconcileComponents(Project project, List existingProject /** * A similar method exists in ProjectQueryManager */ - // TODO: Move redundant method `preprocessACLs` into own utility class (AclUtil) once #2407 and #2472 are merged. private void preprocessACLs(final Query query, final String inputFilter, final Map params, final boolean bypass) { if (super.principal != null && isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED) && !bypass) { final List teams; diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index a6ca8e1368..6ea6ed6f16 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -18,14 +18,11 @@ */ package org.dependencytrack.persistence; -import alpine.common.logging.Logger; import alpine.model.ApiKey; import alpine.model.Team; import alpine.model.UserPrincipal; import alpine.persistence.PaginatedResult; import alpine.resources.AlpineRequest; -import alpine.server.util.DbUtil; -import org.apache.commons.lang3.StringUtils; import org.dependencytrack.model.Component; import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.License; @@ -41,9 +38,6 @@ import javax.jdo.PersistenceManager; import javax.jdo.Query; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -53,24 +47,6 @@ final class PolicyQueryManager extends QueryManager implements IQueryManager { - public static final String QUERY_ACL_1 = """ - "DESCENDANTS" ("ID", "NAME") AS - (SELECT "PROJECT"."ID", - "PROJECT"."NAME" - FROM "PROJECT" - """; - - public static final String QUERY_ACL_2 = """ - UNION ALL - SELECT "CHILD"."ID", - "CHILD"."NAME" - FROM "PROJECT" "CHILD" - JOIN "DESCENDANTS" - ON "DESCENDANTS"."ID" = "CHILD"."PARENT_PROJECT_ID") - SELECT "DESCENDANTS"."ID", "DESCENDANTS"."NAME" FROM "DESCENDANTS" - """; - private static final Logger LOGGER = Logger.getLogger(PolicyQueryManager.class); - /** * Constructs a new QueryManager. * @param pm a PersistenceManager object @@ -721,7 +697,6 @@ private void processInputFilter(Map params, List filterC } } - // TODO: Move redundant method `preprocessACLs` into own utility class (AclUtil) once #2407 and #2472 are merged. private void preprocessACLs(final Query query, final String inputFilter, final Map params, final boolean bypass) { if (super.principal != null && isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED) && !bypass) { final List teams; @@ -740,115 +715,23 @@ private void preprocessACLs(final Query query, final String inp return; } } - - // Query every project that the teams have access to - final Map tempParams = new HashMap<>(); - final Query queryAclProjects = pm.newQuery(Project.class); if (teams != null && teams.size() > 0) { - final StringBuilder stringBuilderAclProjects = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); for (int i = 0, teamsSize = teams.size(); i < teamsSize; i++) { final Team team = super.getObjectById(Team.class, teams.get(i).getId()); - stringBuilderAclProjects.append(" accessTeams.contains(:team").append(i).append(") "); - tempParams.put("team" + i, team); + sb.append(" project.accessTeams.contains(:team").append(i).append(") "); + params.put("team" + i, team); if (i < teamsSize-1) { - stringBuilderAclProjects.append(" || "); + sb.append(" || "); } } - queryAclProjects.setFilter(stringBuilderAclProjects.toString()); - } else { - if (inputFilter != null && !inputFilter.isEmpty()) { - query.setFilter(inputFilter + " && :false"); + if (inputFilter != null) { + query.setFilter(inputFilter + " && (" + sb.toString() + ")"); } else { - query.setFilter(":false"); + query.setFilter(sb.toString()); } - params.put("false", false); - return; } - List result = (List) queryAclProjects.executeWithMap(tempParams); - // Query the descendants of the projects that the teams have access to - if (result != null && !result.isEmpty()) { - final StringBuilder stringBuilderDescendants = new StringBuilder(); - final List parameters = new ArrayList<>(); - stringBuilderDescendants.append("WHERE"); - int i = 0, teamSize = result.size(); - for (Project project : result) { - stringBuilderDescendants.append(" \"ID\" = ?").append(" "); - parameters.add(project.getId()); - if (i < teamSize-1) { - stringBuilderDescendants.append(" OR"); - } - i++; - } - stringBuilderDescendants.append("\n"); - final List results = new ArrayList<>(); - - // Querying the descendants of projects requires a CTE (Common Table Expression), which needs to be at the top-level of the query for Microsoft SQL Server. - // Because of JDO, queries are only allowed to start with "SELECT", so the "WITH" clause for the CTE in MSSQL cannot be at top level. - // Activating the JDO property that queries don't have to start with "SELECT" does not help in this case, because JDO queries that do not start with "SELECT" only return "true", so no data can be fetched this way. - // To circumvent this problem, the query is executed via the direct connection to the database and not via JDO. - Connection connection = null; - PreparedStatement preparedStatement = null; - ResultSet rs = null; - try { - connection = (Connection) pm.getDataStoreConnection(); - if (DbUtil.isMssql() || DbUtil.isOracle()) { // Microsoft SQL Server and Oracle DB already imply the "RECURSIVE" keyword in the "WITH" clause, therefore it is not needed in the query - preparedStatement = connection.prepareStatement("WITH " + QUERY_ACL_1 + stringBuilderDescendants + QUERY_ACL_2); - } else { // Other Databases need the "RECURSIVE" keyword in the "WITH" clause to correctly execute the query - preparedStatement = connection.prepareStatement("WITH RECURSIVE " + QUERY_ACL_1 + stringBuilderDescendants + QUERY_ACL_2); - } - int j = 1; - for (Long id : parameters) { - preparedStatement.setLong(j, id); - j++; - } - preparedStatement.execute(); - rs = preparedStatement.getResultSet(); - while (rs.next()) { - results.add(rs.getLong(1)); - } - } catch (Exception e) { - LOGGER.error(e.getMessage()); - if (inputFilter != null && !inputFilter.isEmpty()) { - query.setFilter(inputFilter + " && :false"); - } else { - query.setFilter(":false"); - } - params.put("false", false); - return; - } finally { - DbUtil.close(rs); - DbUtil.close(preparedStatement); - DbUtil.close(connection); - } - - // Add queried projects and descendants to the input filter of the query - if (results != null && !results.isEmpty()) { - final StringBuilder stringBuilderInputFilter = new StringBuilder(); - int j = 0; - int resultSize = results.size(); - for (Long id : results) { - stringBuilderInputFilter.append(" project.id == :id").append(j); - params.put("id" + j, id); - if (j < resultSize-1) { - stringBuilderInputFilter.append(" || "); - } - j++; - } - if (inputFilter != null && !inputFilter.isEmpty()) { - query.setFilter(inputFilter + " && (" + stringBuilderInputFilter.toString() + ")"); - } else { - query.setFilter(stringBuilderInputFilter.toString()); - } - } - } else { - if (inputFilter != null && !inputFilter.isEmpty()) { - query.setFilter(inputFilter + " && :false"); - } else { - query.setFilter(":false"); - } - params.put("false", false); - } - } else if (StringUtils.trimToNull(inputFilter) != null) { + } else { query.setFilter(inputFilter); } } diff --git a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java index 526018c917..09dc157775 100644 --- a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java @@ -121,11 +121,11 @@ public void getViolationsByProjectTest() { component0.setVersion("1.0"); component0 = qm.createComponent(component0, false); - var component1 = new Component(); - component1.setProject(project); - component1.setName("Acme Component 1"); - component1.setVersion("1.0"); - component1 = qm.createComponent(component1, false); + var componentA = new Component(); + componentA.setProject(project); + componentA.setName("Acme Component 1"); + componentA.setVersion("1.0"); + componentA = qm.createComponent(componentA, false); final Policy policy0 = qm.createPolicy("Blacklisted Version 0", Policy.Operator.ALL, Policy.ViolationState.FAIL); final PolicyCondition condition0 = qm.createPolicyCondition(policy0, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); @@ -140,7 +140,7 @@ public void getViolationsByProjectTest() { var violation = new PolicyViolation(); violation.setType(PolicyViolation.Type.OPERATIONAL); - violation.setComponent(componentFilter ? component0 : component1); + violation.setComponent(componentFilter ? component0 : componentA); violation.setPolicyCondition(conditionFilter ? condition0 : condition1); violation.setTimestamp(new Date()); violation = qm.persist(violation); @@ -317,65 +317,74 @@ public void getViolationsByComponentNotFoundTest() { public void getViolationsWithAclEnabledTest() { initializeWithPermissions(Permissions.VIEW_POLICY_VIOLATION); - final Project project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); - final Project child = qm.createProject("Acme Example - Child", null, "1.0", null, null, null, true, false); - final Project grandchild = qm.createProject("Acme Example - Grandchild", null, "1.0", null, null, null, true, false); - final Project noAccess = qm.createProject("Acme Example - No Access", null, "1.0", null, null, null, true, false); - - var component = new Component(); - component.setProject(project); - component.setName("Acme Component"); - component.setVersion("1.0"); - component = qm.createComponent(component, false); - - var component1 = new Component(); - component1.setProject(child); - component1.setName("Acme Component"); - component1.setVersion("1.0"); - component1 = qm.createComponent(component1, false); + final Project projectA = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); + final Project projectA_child = qm.createProject("Acme Example - Child", null, "1.0", null, projectA, null, true, false); + final Project projectB = qm.createProject("Acme Example - Grandchild", null, "1.0", null, null, null, true, false); - var component2 = new Component(); - component2.setProject(grandchild); - component2.setName("Acme Component"); - component2.setVersion("1.0"); - component2 = qm.createComponent(component2, false); + projectA.addAccessTeam(team); - var component3 = new Component(); - component3.setProject(noAccess); - component3.setName("Acme Component"); - component3.setVersion("1.0"); - component3 = qm.createComponent(component3, false); + var componentA = new Component(); + componentA.setProject(projectA); + componentA.setName("Acme Component"); + componentA.setVersion("1.0"); + componentA = qm.createComponent(componentA, false); + + var componentB = new Component(); + componentB.setProject(projectA_child); + componentB.setName("Acme Component"); + componentB.setVersion("1.0"); + componentB = qm.createComponent(componentB, false); + + var componentC = new Component(); + componentC.setProject(projectB); + componentC.setName("Acme Component"); + componentC.setVersion("1.0"); + componentC = qm.createComponent(componentC, false); + + var componentD = new Component(); + componentD.setProject(projectA); + componentD.setName("Acme Component"); + componentD.setVersion("1.0"); + componentD = qm.createComponent(componentA, false); final Policy policy = qm.createPolicy("Blacklisted Version", Policy.Operator.ALL, Policy.ViolationState.FAIL); final PolicyCondition condition = qm.createPolicyCondition(policy, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); - var violation = new PolicyViolation(); - violation.setType(PolicyViolation.Type.OPERATIONAL); - violation.setComponent(component); - violation.setPolicyCondition(condition); - violation.setTimestamp(new Date()); - violation = qm.persist(violation); - - var violation1 = new PolicyViolation(); - violation1.setType(PolicyViolation.Type.OPERATIONAL); - violation1.setComponent(component1); - violation1.setPolicyCondition(condition); - violation1.setTimestamp(new Date()); - violation1 = qm.persist(violation1); - - var violation2 = new PolicyViolation(); - violation2.setType(PolicyViolation.Type.OPERATIONAL); - violation2.setComponent(component2); - violation2.setPolicyCondition(condition); - violation2.setTimestamp(new Date()); - violation2 = qm.persist(violation2); - - var violation3 = new PolicyViolation(); - violation3.setType(PolicyViolation.Type.OPERATIONAL); - violation3.setComponent(component3); - violation3.setPolicyCondition(condition); - violation3.setTimestamp(new Date()); - violation3 = qm.persist(violation3); + var violationA = new PolicyViolation(); + violationA.setType(PolicyViolation.Type.OPERATIONAL); + violationA.setComponent(componentA); + violationA.setPolicyCondition(condition); + violationA.setTimestamp(new Date()); + violationA = qm.persist(violationA); + + var violationB = new PolicyViolation(); + violationB.setType(PolicyViolation.Type.OPERATIONAL); + violationB.setComponent(componentB); + violationB.setPolicyCondition(condition); + violationB.setTimestamp(new Date()); + violationB = qm.persist(violationB); + + var violationC = new PolicyViolation(); + violationC.setType(PolicyViolation.Type.OPERATIONAL); + violationC.setComponent(componentC); + violationC.setPolicyCondition(condition); + violationC.setTimestamp(new Date()); + violationC = qm.persist(violationC); + + var violationD = new PolicyViolation(); + violationD.setType(PolicyViolation.Type.OPERATIONAL); + violationD.setComponent(componentD); + violationD.setPolicyCondition(condition); + violationD.setTimestamp(new Date()); + violationD = qm.persist(violationD); + + final Response responseA = target(V1_POLICY_VIOLATION) + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseA.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseA.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("4"); + assertThat(parseJsonArray(responseA)).hasSize(4); ConfigProperty aclToggle = qm.getConfigProperty(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName()); if (aclToggle == null) { @@ -385,66 +394,33 @@ public void getViolationsWithAclEnabledTest() { qm.persist(aclToggle); } - project.addAccessTeam(team); - - final Response response = target(V1_POLICY_VIOLATION) + final Response responseB = target(V1_POLICY_VIOLATION) .request() - .header(X_API_KEY, apiKey) + .header(X_API_KEY, team.getApiKeys().get(0).getKey()) .get(); - assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); - assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); - - final JsonArray jsonArray = parseJsonArray(response); - assertThat(jsonArray).hasSize(1); - - final JsonObject jsonObject = jsonArray.getJsonObject(0); - assertThat(jsonObject.getString("uuid")).isEqualTo(violation.getUuid().toString()); - assertThat(jsonObject.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); - assertThat(jsonObject.getJsonObject("policyCondition")).isNotNull(); - assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); - assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); - assertThat(jsonObject.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); - assertThat(jsonObject.getJsonObject("project").getString("uuid")).isEqualTo(project.getUuid().toString()); - - child.setParent(project); - grandchild.setParent(child); - - final Response response1 = target(V1_POLICY_VIOLATION) - .request() - .header(X_API_KEY, apiKey) - .get(); - assertThat(response1.getStatus()).isEqualTo(200); - assertThat(response1.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("3"); + assertThat(responseB.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseB.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("2"); - final JsonArray jsonArray1 = parseJsonArray(response1); - assertThat(jsonArray1).hasSize(3); + final JsonArray jsonArray = parseJsonArray(responseB); + assertThat(jsonArray).hasSize(2); - final JsonObject jsonObject1 = jsonArray1.getJsonObject(0); - assertThat(jsonObject1.getString("uuid")).isEqualTo(violation2.getUuid().toString()); - assertThat(jsonObject1.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); - assertThat(jsonObject1.getJsonObject("policyCondition")).isNotNull(); - assertThat(jsonObject1.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); - assertThat(jsonObject1.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); - assertThat(jsonObject1.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); - assertThat(jsonObject1.getJsonObject("project").getString("uuid")).isEqualTo(grandchild.getUuid().toString()); - - final JsonObject jsonObject2 = jsonArray1.getJsonObject(1); - assertThat(jsonObject2.getString("uuid")).isEqualTo(violation1.getUuid().toString()); - assertThat(jsonObject2.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); - assertThat(jsonObject2.getJsonObject("policyCondition")).isNotNull(); - assertThat(jsonObject2.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); - assertThat(jsonObject2.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); - assertThat(jsonObject2.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); - assertThat(jsonObject2.getJsonObject("project").getString("uuid")).isEqualTo(child.getUuid().toString()); - - final JsonObject jsonObject3 = jsonArray1.getJsonObject(2); - assertThat(jsonObject3.getString("uuid")).isEqualTo(violation.getUuid().toString()); - assertThat(jsonObject3.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); - assertThat(jsonObject3.getJsonObject("policyCondition")).isNotNull(); - assertThat(jsonObject3.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); - assertThat(jsonObject3.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); - assertThat(jsonObject3.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); - assertThat(jsonObject3.getJsonObject("project").getString("uuid")).isEqualTo(project.getUuid().toString()); + final JsonObject jsonObjectA = jsonArray.getJsonObject(0); + assertThat(jsonObjectA.getString("uuid")).isEqualTo(violationD.getUuid().toString()); + assertThat(jsonObjectA.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); + assertThat(jsonObjectA.getJsonObject("policyCondition")).isNotNull(); + assertThat(jsonObjectA.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); + assertThat(jsonObjectA.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); + assertThat(jsonObjectA.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); + assertThat(jsonObjectA.getJsonObject("project").getString("uuid")).isEqualTo(projectA.getUuid().toString()); + + final JsonObject jsonObjectB = jsonArray.getJsonObject(1); + assertThat(jsonObjectB.getString("uuid")).isEqualTo(violationA.getUuid().toString()); + assertThat(jsonObjectB.getString("type")).isEqualTo(PolicyViolation.Type.OPERATIONAL.name()); + assertThat(jsonObjectB.getJsonObject("policyCondition")).isNotNull(); + assertThat(jsonObjectB.getJsonObject("policyCondition").getJsonObject("policy")).isNotNull(); + assertThat(jsonObjectB.getJsonObject("policyCondition").getJsonObject("policy").getString("name")).isEqualTo("Blacklisted Version"); + assertThat(jsonObjectB.getJsonObject("policyCondition").getJsonObject("policy").getString("violationState")).isEqualTo("FAIL"); + assertThat(jsonObjectB.getJsonObject("project").getString("uuid")).isEqualTo(projectA.getUuid().toString()); } } \ No newline at end of file From ee69982b99657da2b6f7c97a680209eb99cd637a Mon Sep 17 00:00:00 2001 From: RBickert Date: Tue, 12 Mar 2024 16:53:46 +0100 Subject: [PATCH 007/429] Remove leftover comment Signed-off-by: RBickert --- .../org/dependencytrack/persistence/ProjectQueryManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java index 563a616811..1cfbfaf7c8 100644 --- a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java @@ -893,7 +893,6 @@ public boolean hasAccess(final Principal principal, final Project project) { /** * A similar method exists in ComponentQueryManager */ - // TODO: Move redundant method `preprocessACLs` into own utility class (AclUtil) once #2407 and #2472 are merged. private void preprocessACLs(final Query query, final String inputFilter, final Map params, final boolean bypass) { if (super.principal != null && isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED) && !bypass) { final List teams; From 51382764c8b1ef6204df989565889546dfa1d160 Mon Sep 17 00:00:00 2001 From: RBickert Date: Wed, 13 Mar 2024 11:22:08 +0100 Subject: [PATCH 008/429] Add tests for policy violation filters Signed-off-by: RBickert --- .../v1/PolicyViolationResourceTest.java | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) diff --git a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java index 09dc157775..c3c7ac10a2 100644 --- a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java @@ -22,6 +22,7 @@ import alpine.server.filters.ApiFilter; import alpine.server.filters.AuthenticationFilter; import alpine.server.filters.AuthorizationFilter; + import org.dependencytrack.ResourceTest; import org.dependencytrack.auth.Permissions; import org.dependencytrack.model.Component; @@ -30,6 +31,8 @@ import org.dependencytrack.model.PolicyCondition; import org.dependencytrack.model.PolicyViolation; import org.dependencytrack.model.Project; +import org.dependencytrack.model.ViolationAnalysis; +import org.dependencytrack.model.ViolationAnalysisState; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.servlet.ServletContainer; import org.glassfish.jersey.test.DeploymentContext; @@ -39,6 +42,7 @@ import javax.json.JsonArray; import javax.json.JsonObject; import javax.ws.rs.core.Response; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.UUID; @@ -423,4 +427,238 @@ public void getViolationsWithAclEnabledTest() { assertThat(jsonObjectB.getJsonObject("project").getString("uuid")).isEqualTo(projectA.getUuid().toString()); } + @Test + public void getViolationsWithArrayFilter() { + initializeWithPermissions(Permissions.VIEW_POLICY_VIOLATION); + + final Project project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); + + var component = new Component(); + component.setProject(project); + component.setName("Acme Component"); + component.setVersion("1.0"); + component = qm.createComponent(component, false); + + final Policy policyA = qm.createPolicy("Policy A", Policy.Operator.ALL, Policy.ViolationState.FAIL); + final PolicyCondition conditionA = qm.createPolicyCondition(policyA, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + var violationA = new PolicyViolation(); + violationA.setType(PolicyViolation.Type.OPERATIONAL); + violationA.setComponent(component); + violationA.setPolicyCondition(conditionA); + violationA.setTimestamp(new Date()); + violationA = qm.persist(violationA); + + final Policy policyB = qm.createPolicy("Policy B", Policy.Operator.ALL, Policy.ViolationState.INFO); + final PolicyCondition conditionB = qm.createPolicyCondition(policyB, PolicyCondition.Subject.LICENSE, PolicyCondition.Operator.IS, "unresolved"); + var violationB = new PolicyViolation(); + violationB.setType(PolicyViolation.Type.LICENSE); + violationB.setComponent(component); + violationB.setPolicyCondition(conditionB); + violationB.setTimestamp(new Date()); + violationB = qm.persist(violationB); + + final Policy policyC = qm.createPolicy("Policy C", Policy.Operator.ALL, Policy.ViolationState.INFO); + final PolicyCondition conditionC = qm.createPolicyCondition(policyC, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + ViolationAnalysis violationAnalysis = new ViolationAnalysis(); + violationAnalysis.setViolationAnalysisState(ViolationAnalysisState.REJECTED); + var violationC = new PolicyViolation(); + violationC.setType(PolicyViolation.Type.OPERATIONAL); + violationC.setComponent(component); + violationC.setPolicyCondition(conditionC); + violationC.setTimestamp(new Date()); + violationC.setAnalysis(violationAnalysis); + violationAnalysis.setPolicyViolation(violationC); + violationC = qm.persist(violationC); + + final Policy policyD = qm.createPolicy("Policy D", Policy.Operator.ALL, Policy.ViolationState.INFO); + final PolicyCondition conditionD = qm.createPolicyCondition(policyD, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + var violationD = new PolicyViolation(); + violationD.setType(PolicyViolation.Type.OPERATIONAL); + violationD.setComponent(component); + violationD.setPolicyCondition(conditionD); + violationD.setTimestamp(new Date()); + violationD = qm.persist(violationD); + + final Response response = target(V1_POLICY_VIOLATION) + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("4"); + assertThat(parseJsonArray(response)).hasSize(4); + + final Response responseA = target(V1_POLICY_VIOLATION).queryParam("violationState", "FAIL") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseA.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseA.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayA = parseJsonArray(responseA); + assertThat(jsonArrayA).hasSize(1); + assertThat(jsonArrayA.getJsonObject(0).getString("uuid")).isEqualTo(violationA.getUuid().toString()); + + + final Response responseB = target(V1_POLICY_VIOLATION).queryParam("riskType", "LICENSE") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseB.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseB.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayB = parseJsonArray(responseB); + assertThat(jsonArrayB).hasSize(1); + assertThat(jsonArrayB.getJsonObject(0).getString("uuid")).isEqualTo(violationB.getUuid().toString()); + assertThat(jsonArrayB.getJsonObject(0).getString("uuid")).isEqualTo(violationB.getUuid().toString()); + + final Response responseC = target(V1_POLICY_VIOLATION).queryParam("analysisState", "REJECTED") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseC.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseC.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayC = parseJsonArray(responseC); + assertThat(jsonArrayC).hasSize(1); + assertThat(jsonArrayC.getJsonObject(0).getString("uuid")).isEqualTo(violationC.getUuid().toString()); + assertThat(jsonArrayC.getJsonObject(0).getString("uuid")).isEqualTo(violationC.getUuid().toString()); + + final Response responseD = target(V1_POLICY_VIOLATION).queryParam("policy", policyD.getUuid().toString()) + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseD.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseD.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayD = parseJsonArray(responseD); + assertThat(jsonArrayD).hasSize(1); + assertThat(jsonArrayD.getJsonObject(0).getString("uuid")).isEqualTo(violationD.getUuid().toString()); + assertThat(jsonArrayD.getJsonObject(0).getString("uuid")).isEqualTo(violationD.getUuid().toString()); + } + + @Test + public void getViolationsWithInputFilter() { + initializeWithPermissions(Permissions.VIEW_POLICY_VIOLATION); + + final Project projectA = qm.createProject("Project A", null, "1.0", null, null, null, true, false); + final Project projectB = qm.createProject("Project B", null, "1.0", null, null, null, true, false); + final Project projectC = qm.createProject("Project C", null, "1.0", null, null, null, true, false); + final Project projectD = qm.createProject("Project D", null, "1.0", null, null, null, true, false); + + var componentA = new Component(); + componentA.setProject(projectA); + componentA.setName("Component A"); + componentA.setVersion("1.0"); + componentA.setLicense("License A"); + componentA = qm.createComponent(componentA, false); + + var componentB = new Component(); + componentB.setProject(projectB); + componentB.setName("Component B"); + componentB.setVersion("1.0"); + componentB.setLicense("License B"); + componentB = qm.createComponent(componentB, false); + + var componentC = new Component(); + componentC.setProject(projectC); + componentC.setName("Component C"); + componentC.setVersion("1.0"); + componentC.setLicense("License C"); + componentC = qm.createComponent(componentC, false); + + var componentD = new Component(); + componentD.setProject(projectD); + componentD.setName("Component D"); + componentD.setVersion("1.0"); + componentD.setLicense("License D"); + componentD = qm.createComponent(componentD, false); + + final Policy policyA = qm.createPolicy("Policy A", Policy.Operator.ALL, Policy.ViolationState.FAIL); + final PolicyCondition conditionA = qm.createPolicyCondition(policyA, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + var violationA = new PolicyViolation(); + violationA.setType(PolicyViolation.Type.OPERATIONAL); + violationA.setComponent(componentA); + violationA.setPolicyCondition(conditionA); + violationA.setTimestamp(new Date()); + violationA = qm.persist(violationA); + + final Policy policyB = qm.createPolicy("Policy B", Policy.Operator.ALL, Policy.ViolationState.FAIL); + final PolicyCondition conditionB = qm.createPolicyCondition(policyB, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + var violationB = new PolicyViolation(); + violationB.setType(PolicyViolation.Type.OPERATIONAL); + violationB.setComponent(componentB); + violationB.setPolicyCondition(conditionB); + violationB.setTimestamp(new Date()); + violationB = qm.persist(violationB); + + final Policy policyC = qm.createPolicy("Policy C", Policy.Operator.ALL, Policy.ViolationState.FAIL); + final PolicyCondition conditionC = qm.createPolicyCondition(policyC, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + var violationC = new PolicyViolation(); + violationC.setType(PolicyViolation.Type.OPERATIONAL); + violationC.setComponent(componentC); + violationC.setPolicyCondition(conditionC); + violationC.setTimestamp(new Date()); + violationC = qm.persist(violationC); + + final Policy policyD = qm.createPolicy("Policy D", Policy.Operator.ALL, Policy.ViolationState.FAIL); + final PolicyCondition conditionD = qm.createPolicyCondition(policyD, PolicyCondition.Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.0"); + var violationD = new PolicyViolation(); + violationD.setType(PolicyViolation.Type.OPERATIONAL); + violationD.setComponent(componentD); + violationD.setPolicyCondition(conditionD); + violationD.setTimestamp(new Date()); + violationD = qm.persist(violationD); + + final Response response = target(V1_POLICY_VIOLATION) + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("4"); + assertThat(parseJsonArray(response)).hasSize(4); + + final Response responseA = target(V1_POLICY_VIOLATION) + .queryParam("textSearchField", "policy_name") + .queryParam("textSearchInput", "Policy A") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseA.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseA.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayA = parseJsonArray(responseA); + assertThat(jsonArrayA).hasSize(1); + assertThat(jsonArrayA.getJsonObject(0).getString("uuid")).isEqualTo(violationA.getUuid().toString()); + + final Response responseB = target(V1_POLICY_VIOLATION) + .queryParam("textSearchField", "component") + .queryParam("textSearchInput", "Component B") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseB.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseB.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayB = parseJsonArray(responseB); + assertThat(jsonArrayB).hasSize(1); + assertThat(jsonArrayB.getJsonObject(0).getString("uuid")).isEqualTo(violationB.getUuid().toString()); + + final Response responseC = target(V1_POLICY_VIOLATION) + .queryParam("textSearchField", "license") + .queryParam("textSearchInput", "License C") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseC.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseC.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayC = parseJsonArray(responseC); + assertThat(jsonArrayC).hasSize(1); + assertThat(jsonArrayC.getJsonObject(0).getString("uuid")).isEqualTo(violationC.getUuid().toString()); + + final Response responseD = target(V1_POLICY_VIOLATION) + .queryParam("textSearchField", "project_name") + .queryParam("textSearchInput", "Project D") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(responseD.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(responseD.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + final JsonArray jsonArrayD = parseJsonArray(responseD); + assertThat(jsonArrayD).hasSize(1); + assertThat(jsonArrayD.getJsonObject(0).getString("uuid")).isEqualTo(violationD.getUuid().toString()); + } } \ No newline at end of file From 4064fafaf6623e6dca5ff43854637e96fa3c8374 Mon Sep 17 00:00:00 2001 From: RBickert Date: Wed, 13 Mar 2024 11:28:25 +0100 Subject: [PATCH 009/429] Remove unused import Signed-off-by: RBickert --- .../resources/v1/PolicyViolationResourceTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java index c3c7ac10a2..b5d091138d 100644 --- a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java @@ -42,7 +42,6 @@ import javax.json.JsonArray; import javax.json.JsonObject; import javax.ws.rs.core.Response; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.UUID; From f1d14d53e9f3425d82acc6c91c961fe2d6a69c40 Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Thu, 11 Jul 2024 11:55:02 +0100 Subject: [PATCH 010/429] add support for authors field Signed-off-by: Ross Murphy --- .../org/dependencytrack/model/Component.java | 14 +++++ .../org/dependencytrack/model/Project.java | 16 ++++++ .../parser/cyclonedx/util/ModelConverter.java | 5 ++ .../persistence/ComponentQueryManager.java | 2 + .../persistence/ProjectQueryManager.java | 2 + .../resources/v1/ComponentResource.java | 3 + .../resources/v1/ProjectResource.java | 1 + .../tasks/BomUploadProcessingTask.java | 2 + .../resources/v1/BomResourceTest.java | 56 +++++++++++++++++++ .../unit/cyclonedx/valid-bom-1.6.json | 12 +++- 10 files changed, 111 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/Component.java b/src/main/java/org/dependencytrack/model/Component.java index 17830de61b..7cf40c6261 100644 --- a/src/main/java/org/dependencytrack/model/Component.java +++ b/src/main/java/org/dependencytrack/model/Component.java @@ -30,6 +30,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import org.apache.commons.lang3.StringUtils; import org.dependencytrack.model.validation.ValidSpdxExpression; +import org.dependencytrack.persistence.converter.OrganizationalContactsJsonConverter; import org.dependencytrack.persistence.converter.OrganizationalEntityJsonConverter; import org.dependencytrack.resources.v1.serializers.CustomPackageURLSerializer; @@ -114,6 +115,11 @@ public enum FetchGroup { @Pattern(regexp = RegexSequence.Definition.PRINTABLE_CHARS, message = "The author may only contain printable characters") private String author; + @Persistent(defaultFetchGroup = "true") + @Convert(OrganizationalContactsJsonConverter.class) + @Column(name = "AUTHORS", jdbcType = "CLOB", allowsNull = "true") + private List authors; + @Persistent @Column(name = "PUBLISHER", jdbcType = "VARCHAR") @Size(max = 255) @@ -394,6 +400,14 @@ public void setAuthor(String author) { this.author = author; } + public List getAuthors() { + return authors; + } + + public void setAuthors(List authors) { + this.authors = authors; + } + public String getPublisher() { return publisher; } diff --git a/src/main/java/org/dependencytrack/model/Project.java b/src/main/java/org/dependencytrack/model/Project.java index ed12500da7..59c804eaf8 100644 --- a/src/main/java/org/dependencytrack/model/Project.java +++ b/src/main/java/org/dependencytrack/model/Project.java @@ -32,6 +32,8 @@ import com.github.packageurl.MalformedPackageURLException; import com.github.packageurl.PackageURL; import io.swagger.v3.oas.annotations.media.Schema; + +import org.dependencytrack.persistence.converter.OrganizationalContactsJsonConverter; import org.dependencytrack.persistence.converter.OrganizationalEntityJsonConverter; import org.dependencytrack.resources.v1.serializers.CustomPackageURLSerializer; @@ -75,6 +77,7 @@ @FetchGroup(name = "ALL", members = { @Persistent(name = "name"), @Persistent(name = "author"), + @Persistent(name = "authors"), @Persistent(name = "publisher"), @Persistent(name = "supplier"), @Persistent(name = "group"), @@ -132,6 +135,11 @@ public enum FetchGroup { @Pattern(regexp = RegexSequence.Definition.PRINTABLE_CHARS, message = "The author may only contain printable characters") private String author; + @Persistent(defaultFetchGroup = "true") + @Convert(OrganizationalContactsJsonConverter.class) + @Column(name = "AUTHORS", jdbcType = "CLOB", allowsNull = "true") + private List authors; + @Persistent @Column(name = "PUBLISHER", jdbcType = "VARCHAR") @Size(max = 255) @@ -305,6 +313,14 @@ public void setAuthor(String author) { this.author = author; } + public List getAuthors() { + return authors; + } + + public void setAuthors(List authors) { + this.authors = authors; + } + public String getPublisher() { return publisher; } diff --git a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java index 9f275ec165..294b23ffb7 100644 --- a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java +++ b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java @@ -117,6 +117,7 @@ public static Project convertToProject(final org.cyclonedx.model.Component cdxCo final var project = new Project(); project.setBomRef(useOrGenerateRandomBomRef(cdxComponent.getBomRef())); project.setAuthor(trimToNull(cdxComponent.getAuthor())); + project.setAuthors(convertCdxContacts(cdxComponent.getAuthors())); project.setPublisher(trimToNull(cdxComponent.getPublisher())); project.setSupplier(convert(cdxComponent.getSupplier())); project.setClassifier(convertClassifier(cdxComponent.getType()).orElse(Classifier.APPLICATION)); @@ -154,6 +155,7 @@ public static Component convertComponent(final org.cyclonedx.model.Component cdx final var component = new Component(); component.setBomRef(useOrGenerateRandomBomRef(cdxComponent.getBomRef())); component.setAuthor(trimToNull(cdxComponent.getAuthor())); + component.setAuthors(convertCdxContacts(cdxComponent.getAuthors())); component.setPublisher(trimToNull(cdxComponent.getPublisher())); component.setSupplier(convert(cdxComponent.getSupplier())); component.setClassifier(convertClassifier(cdxComponent.getType()).orElse(Classifier.LIBRARY)); @@ -526,6 +528,7 @@ public static org.cyclonedx.model.Component convert(final QueryManager qm, final cycloneComponent.setCopyright(StringUtils.trimToNull(component.getCopyright())); cycloneComponent.setCpe(StringUtils.trimToNull(component.getCpe())); cycloneComponent.setAuthor(StringUtils.trimToNull(component.getAuthor())); + cycloneComponent.setAuthors(convertContacts(component.getAuthors())); cycloneComponent.setSupplier(convert(component.getSupplier())); cycloneComponent.setProperties(convert(component.getProperties())); @@ -667,6 +670,7 @@ public static org.cyclonedx.model.Metadata createMetadata(final Project project) final org.cyclonedx.model.Component cycloneComponent = new org.cyclonedx.model.Component(); cycloneComponent.setBomRef(project.getUuid().toString()); cycloneComponent.setAuthor(StringUtils.trimToNull(project.getAuthor())); + cycloneComponent.setAuthors(convertContacts(project.getAuthors())); cycloneComponent.setPublisher(StringUtils.trimToNull(project.getPublisher())); cycloneComponent.setGroup(StringUtils.trimToNull(project.getGroup())); cycloneComponent.setName(StringUtils.trimToNull(project.getName())); @@ -704,6 +708,7 @@ public static org.cyclonedx.model.Metadata createMetadata(final Project project) cycloneComponent.setExternalReferences(references); } cycloneComponent.setSupplier(convert(project.getSupplier())); + cycloneComponent.setAuthors(convertContacts(project.getAuthors())); // NB: Project properties are currently used to configure integrations // such as Defect Dojo. They can also contain encrypted values that most diff --git a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java index 6115286ffa..78401e17e5 100644 --- a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java @@ -378,6 +378,7 @@ public Component cloneComponent(Component sourceComponent, Project destinationPr component.setLicenseUrl(sourceComponent.getLicenseUrl()); component.setResolvedLicense(sourceComponent.getResolvedLicense()); component.setAuthor(sourceComponent.getAuthor()); + component.setAuthors(sourceComponent.getAuthors()); component.setSupplier(sourceComponent.getSupplier()); // TODO Add support for parent component and children components component.setProject(destinationProject); @@ -413,6 +414,7 @@ public Component updateComponent(Component transientComponent, boolean commitInd component.setPurl(transientComponent.getPurl()); component.setInternal(transientComponent.isInternal()); component.setAuthor(transientComponent.getAuthor()); + component.setAuthors(transientComponent.getAuthors()); component.setSupplier(transientComponent.getSupplier()); component.setExternalReferences(transientComponent.getExternalReferences()); final Component result = persist(component); diff --git a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java index d6f81cdd2a..a25cef6acc 100644 --- a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java @@ -533,6 +533,7 @@ public Project createProject(final Project project, List tags, boolean comm public Project updateProject(Project transientProject, boolean commitIndex) { final Project project = getObjectByUuid(Project.class, transientProject.getUuid()); project.setAuthor(transientProject.getAuthor()); + project.setAuthors(transientProject.getAuthors()); project.setPublisher(transientProject.getPublisher()); project.setManufacturer(transientProject.getManufacturer()); project.setSupplier(transientProject.getSupplier()); @@ -596,6 +597,7 @@ public Project clone(UUID from, String newVersion, boolean includeTags, boolean } Project project = new Project(); project.setAuthor(source.getAuthor()); + project.setAuthors(source.getAuthors()); project.setManufacturer(source.getManufacturer()); project.setSupplier(source.getSupplier()); project.setPublisher(source.getPublisher()); diff --git a/src/main/java/org/dependencytrack/resources/v1/ComponentResource.java b/src/main/java/org/dependencytrack/resources/v1/ComponentResource.java index 84b928216b..ca4a340942 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ComponentResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ComponentResource.java @@ -285,6 +285,7 @@ public Response createComponent(@Parameter(description = "The UUID of the projec final Validator validator = super.getValidator(); failOnValidationError( validator.validateProperty(jsonComponent, "author"), + validator.validateProperty(jsonComponent, "authors"), validator.validateProperty(jsonComponent, "publisher"), validator.validateProperty(jsonComponent, "name"), validator.validateProperty(jsonComponent, "version"), @@ -324,6 +325,7 @@ public Response createComponent(@Parameter(description = "The UUID of the projec Component component = new Component(); component.setProject(project); component.setAuthor(StringUtils.trimToNull(jsonComponent.getAuthor())); + component.setAuthors(jsonComponent.getAuthors()); component.setPublisher(StringUtils.trimToNull(jsonComponent.getPublisher())); component.setName(StringUtils.trimToNull(jsonComponent.getName())); component.setVersion(StringUtils.trimToNull(jsonComponent.getVersion())); @@ -427,6 +429,7 @@ public Response updateComponent(Component jsonComponent) { component.setName(name); } component.setAuthor(StringUtils.trimToNull(jsonComponent.getAuthor())); + component.setAuthors(jsonComponent.getAuthors()); component.setPublisher(StringUtils.trimToNull(jsonComponent.getPublisher())); component.setVersion(StringUtils.trimToNull(jsonComponent.getVersion())); component.setGroup(StringUtils.trimToNull(jsonComponent.getGroup())); diff --git a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java index f2806deb00..847fb74aba 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java @@ -456,6 +456,7 @@ public Response patchProject( return Response.status(Response.Status.CONFLICT).entity("A project with the specified name and version already exists.").build(); } modified |= setIfDifferent(jsonProject, project, Project::getAuthor, Project::setAuthor); + modified |= setIfDifferent(jsonProject, project, Project::getAuthors, Project::setAuthors); modified |= setIfDifferent(jsonProject, project, Project::getPublisher, Project::setPublisher); modified |= setIfDifferent(jsonProject, project, Project::getGroup, Project::setGroup); modified |= setIfDifferent(jsonProject, project, Project::getDescription, Project::setDescription); diff --git a/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java b/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java index 3d72208567..22c2f598d8 100644 --- a/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java +++ b/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java @@ -329,6 +329,7 @@ private Project processProject(final Context ctx, final QueryManager qm, if (project != null) { persistentProject.setBomRef(project.getBomRef()); // Transient hasChanged |= applyIfChanged(persistentProject, project, Project::getAuthor, persistentProject::setAuthor); + hasChanged |= applyIfChanged(persistentProject, project, Project::getAuthors, persistentProject::setAuthors); hasChanged |= applyIfChanged(persistentProject, project, Project::getPublisher, persistentProject::setPublisher); hasChanged |= applyIfChanged(persistentProject, project, Project::getManufacturer, persistentProject::setManufacturer); hasChanged |= applyIfChanged(persistentProject, project, Project::getSupplier, persistentProject::setSupplier); @@ -410,6 +411,7 @@ private Map processComponents(final QueryManager q } else { persistentComponent.setBomRef(component.getBomRef()); // Transient applyIfChanged(persistentComponent, component, Component::getAuthor, persistentComponent::setAuthor); + applyIfChanged(persistentComponent, component, Component::getAuthors, persistentComponent::setAuthors); applyIfChanged(persistentComponent, component, Component::getPublisher, persistentComponent::setPublisher); applyIfChanged(persistentComponent, component, Component::getSupplier, persistentComponent::setSupplier); applyIfChanged(persistentComponent, component, Component::getClassifier, persistentComponent::setClassifier); diff --git a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java index 425763ca78..8daebb947a 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java @@ -1006,6 +1006,62 @@ public void uploadBomInvalidParentTest() throws Exception { Assert.assertEquals("The parent component could not be found.", body); } + @Test + public void uploadBomAuthorsTest() { + //CycloneDxValidator.getInstance().validate() + initializeWithPermissions(Permissions.BOM_UPLOAD); + + qm.createConfigProperty( + BOM_VALIDATION_ENABLED.getGroupName(), + BOM_VALIDATION_ENABLED.getPropertyName(), + "true", + BOM_VALIDATION_ENABLED.getPropertyType(), + null + ); + + final var project = new Project(); + project.setName("acme-app"); + project.setVersion("1.0.0"); + qm.persist(project); + + final String bomJson = """ + { + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "application", + "name": "acme-library", + "version": "1.0.0", + "authors" : [ + { + "name" : "bomAuthor" + } + ] + } + ] + } + """; + + final String encodedBom = Base64.getEncoder().encodeToString(bomJson.getBytes(StandardCharsets.UTF_8)); + + assertThatNoException().isThrownBy(() -> CycloneDxValidator.getInstance().validate(bomJson.getBytes(StandardCharsets.UTF_8))); + + final Response response = jersey.target(V1_BOM).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(""" + { + "projectName": "acme-app", + "projectVersion": "1.0.0", + "bom": "%s" + } + """.formatted(encodedBom), MediaType.APPLICATION_JSON)); + + assertThat(response.getStatus()).isEqualTo(200); + } + @Test public void uploadBomInvalidJsonTest() { initializeWithPermissions(Permissions.BOM_UPLOAD); diff --git a/src/test/resources/unit/cyclonedx/valid-bom-1.6.json b/src/test/resources/unit/cyclonedx/valid-bom-1.6.json index c07ab7b0c4..fe93cf4af1 100644 --- a/src/test/resources/unit/cyclonedx/valid-bom-1.6.json +++ b/src/test/resources/unit/cyclonedx/valid-bom-1.6.json @@ -32,7 +32,11 @@ ], "component": { "type": "application", - "author": "Acme Super Heros", + "authors" : [ + { + "name" : "Acme Super Heros" + } + ], "name": "Acme Application", "version": "9.1.1", "swid": { @@ -75,7 +79,11 @@ { "bom-ref": "pkg:npm/acme/component@1.0.0", "type": "library", - "author": "Joane Doe et al.", + "authors" : [ + { + "name" : "Joane Doe et al." + } + ], "publisher": "Acme Inc", "group": "com.acme", "name": "tomcat-catalina", From 5961e287863adb8b2c164c715dee14038cda5fdb Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Thu, 11 Jul 2024 12:30:30 +0100 Subject: [PATCH 011/429] remove comment Signed-off-by: Ross Murphy --- .../java/org/dependencytrack/resources/v1/BomResourceTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java index 8daebb947a..9ab156d916 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java @@ -1008,7 +1008,6 @@ public void uploadBomInvalidParentTest() throws Exception { @Test public void uploadBomAuthorsTest() { - //CycloneDxValidator.getInstance().validate() initializeWithPermissions(Permissions.BOM_UPLOAD); qm.createConfigProperty( From 13a57ec9a2dae1d30c39010b168b79ae29ca29db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 08:09:17 +0000 Subject: [PATCH 012/429] Bump aquasecurity/trivy-action from 0.23.0 to 0.24.0 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.23.0 to 0.24.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/7c2007bcb556501da015201bcba5aa14069b74e2...6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 550991c841..4a45535ab3 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -135,7 +135,7 @@ jobs: - name: Run Trivy Vulnerability Scanner if: ${{ inputs.publish-container }} - uses: aquasecurity/trivy-action@7c2007bcb556501da015201bcba5aa14069b74e2 # tag=0.23.0 + uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # tag=0.24.0 with: image-ref: docker.io/dependencytrack/${{ matrix.distribution }}:${{ inputs.app-version }} format: 'sarif' From 94966d57e97d947d5fb1a0eff42c03eee7deae16 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 08:09:36 +0000 Subject: [PATCH 013/429] Bump actions/download-artifact from 4.1.7 to 4.1.8 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.1.7 to 4.1.8. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/65a9edc5881444af0b9093a5e628f2fe47ea3b2e...fa0a91b85d4f404e444e00e005971372dc801d16) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- .github/workflows/ci-publish.yaml | 2 +- .github/workflows/ci-test-pr-coverage.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 550991c841..df13855839 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -81,7 +81,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 - name: Download Artifacts - uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # tag=v4.1.7 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # tag=v4.1.8 with: name: assembled-wars path: target diff --git a/.github/workflows/ci-publish.yaml b/.github/workflows/ci-publish.yaml index 6283d1a198..b885a7fdf0 100644 --- a/.github/workflows/ci-publish.yaml +++ b/.github/workflows/ci-publish.yaml @@ -55,7 +55,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 - name: Download Artifacts - uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # tag=v4.1.7 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # tag=v4.1.8 with: name: assembled-wars path: target diff --git a/.github/workflows/ci-test-pr-coverage.yml b/.github/workflows/ci-test-pr-coverage.yml index c133f1809b..99eed37553 100644 --- a/.github/workflows/ci-test-pr-coverage.yml +++ b/.github/workflows/ci-test-pr-coverage.yml @@ -18,7 +18,7 @@ jobs: && github.event.workflow_run.conclusion == 'success' steps: - name: Download PR test coverage report - uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # tag=v4.1.7 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # tag=v4.1.8 with: name: pr-test-coverage-report github-token: ${{ secrets.GITHUB_TOKEN }} From 164847a87ad747c3128793c7b0fb01d4b06067cf Mon Sep 17 00:00:00 2001 From: nscuro Date: Mon, 15 Jul 2024 20:54:27 +0200 Subject: [PATCH 014/429] Ensure no unique constraint violation for `ProjectMetadata` Adds regression test for #3895. The behavior itself does not reproduce on `master`, but does with `4.11.5`. Wraps the project cloning in a transaction to make it more reliable. Adds MDC variables for `CloneProjectTask` (#3234). Fixes #3895 Signed-off-by: nscuro --- .../org/dependencytrack/common/MdcKeys.java | 1 + .../persistence/ProjectQueryManager.java | 255 +++++++++--------- .../tasks/CloneProjectTask.java | 31 ++- .../tasks/BomUploadProcessingTaskTest.java | 77 ++++++ 4 files changed, 233 insertions(+), 131 deletions(-) diff --git a/src/main/java/org/dependencytrack/common/MdcKeys.java b/src/main/java/org/dependencytrack/common/MdcKeys.java index 7c7254baae..2dd46bb231 100644 --- a/src/main/java/org/dependencytrack/common/MdcKeys.java +++ b/src/main/java/org/dependencytrack/common/MdcKeys.java @@ -30,6 +30,7 @@ public final class MdcKeys { public static final String MDC_BOM_SPEC_VERSION = "bomSpecVersion"; public static final String MDC_BOM_UPLOAD_TOKEN = "bomUploadToken"; public static final String MDC_BOM_VERSION = "bomVersion"; + public static final String MDC_EVENT_TOKEN = "eventToken"; public static final String MDC_PROJECT_NAME = "projectName"; public static final String MDC_PROJECT_UUID = "projectUuid"; public static final String MDC_PROJECT_VERSION = "projectVersion"; diff --git a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java index d6f81cdd2a..1dfdb5ab27 100644 --- a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java @@ -578,151 +578,160 @@ public Project updateProject(Project transientProject, boolean commitIndex) { } @Override - public Project clone(UUID from, String newVersion, boolean includeTags, boolean includeProperties, - boolean includeComponents, boolean includeServices, boolean includeAuditHistory, - boolean includeACL, boolean includePolicyViolations) { - final Project source = getObjectByUuid(Project.class, from, Project.FetchGroup.ALL.name()); - if (source == null) { - LOGGER.warn("Project with UUID %s was supposed to be cloned, but it does not exist anymore".formatted(from)); - return null; - } - if (doesProjectExist(source.getName(), newVersion)) { - // Project cloning is an asynchronous process. When receiving the clone request, we already perform - // this check. It is possible though that a project with the new version is created synchronously - // between the clone event being dispatched, and it being processed. - LOGGER.warn("Project %s was supposed to be cloned to version %s, but that version already exists" - .formatted(source, newVersion)); - return null; - } - Project project = new Project(); - project.setAuthor(source.getAuthor()); - project.setManufacturer(source.getManufacturer()); - project.setSupplier(source.getSupplier()); - project.setPublisher(source.getPublisher()); - project.setGroup(source.getGroup()); - project.setName(source.getName()); - project.setDescription(source.getDescription()); - project.setVersion(newVersion); - project.setClassifier(source.getClassifier()); - project.setActive(source.isActive()); - project.setCpe(source.getCpe()); - project.setPurl(source.getPurl()); - project.setSwidTagId(source.getSwidTagId()); - if (includeComponents && includeServices) { - project.setDirectDependencies(source.getDirectDependencies()); - } - project.setParent(source.getParent()); - project = persist(project); - - if (source.getMetadata() != null) { - final var metadata = new ProjectMetadata(); - metadata.setProject(project); - metadata.setAuthors(source.getMetadata().getAuthors()); - metadata.setSupplier(source.getMetadata().getSupplier()); - persist(metadata); - } + public Project clone( + final UUID from, + final String newVersion, + final boolean includeTags, + final boolean includeProperties, + final boolean includeComponents, + final boolean includeServices, + final boolean includeAuditHistory, + final boolean includeACL, + final boolean includePolicyViolations + ) { + final Project clonedProject = callInTransaction(() -> { + final Project source = getObjectByUuid(Project.class, from, Project.FetchGroup.ALL.name()); + if (source == null) { + LOGGER.warn("Project was supposed to be cloned, but it does not exist anymore"); + return null; + } + if (doesProjectExist(source.getName(), newVersion)) { + // Project cloning is an asynchronous process. When receiving the clone request, we already perform + // this check. It is possible though that a project with the new version is created synchronously + // between the clone event being dispatched, and it being processed. + LOGGER.warn("Project was supposed to be cloned to version %s, but that version already exists".formatted(newVersion)); + return null; + } + Project project = new Project(); + project.setAuthor(source.getAuthor()); + project.setManufacturer(source.getManufacturer()); + project.setSupplier(source.getSupplier()); + project.setPublisher(source.getPublisher()); + project.setGroup(source.getGroup()); + project.setName(source.getName()); + project.setDescription(source.getDescription()); + project.setVersion(newVersion); + project.setClassifier(source.getClassifier()); + project.setActive(source.isActive()); + project.setCpe(source.getCpe()); + project.setPurl(source.getPurl()); + project.setSwidTagId(source.getSwidTagId()); + if (includeComponents && includeServices) { + project.setDirectDependencies(source.getDirectDependencies()); + } + project.setParent(source.getParent()); + project = persist(project); + + if (source.getMetadata() != null) { + final var metadata = new ProjectMetadata(); + metadata.setProject(project); + metadata.setAuthors(source.getMetadata().getAuthors()); + metadata.setSupplier(source.getMetadata().getSupplier()); + persist(metadata); + } - if (includeTags) { - for (final Tag tag: source.getTags()) { - tag.getProjects().add(project); - persist(tag); + if (includeTags) { + for (final Tag tag : source.getTags()) { + tag.getProjects().add(project); + persist(tag); + } } - } - if (includeProperties && source.getProperties() != null) { - for (final ProjectProperty sourceProperty: source.getProperties()) { - final ProjectProperty property = new ProjectProperty(); - property.setProject(project); - property.setPropertyType(sourceProperty.getPropertyType()); - property.setGroupName(sourceProperty.getGroupName()); - property.setPropertyName(sourceProperty.getPropertyName()); - property.setPropertyValue(sourceProperty.getPropertyValue()); - property.setDescription(sourceProperty.getDescription()); - persist(property); + if (includeProperties && source.getProperties() != null) { + for (final ProjectProperty sourceProperty : source.getProperties()) { + final ProjectProperty property = new ProjectProperty(); + property.setProject(project); + property.setPropertyType(sourceProperty.getPropertyType()); + property.setGroupName(sourceProperty.getGroupName()); + property.setPropertyName(sourceProperty.getPropertyName()); + property.setPropertyValue(sourceProperty.getPropertyValue()); + property.setDescription(sourceProperty.getDescription()); + persist(property); + } } - } - final Map clonedComponents = new HashMap<>(); - if (includeComponents) { - final List sourceComponents = getAllComponents(source); - if (sourceComponents != null) { - for (final Component sourceComponent: sourceComponents) { - final Component clonedComponent = cloneComponent(sourceComponent, project, false); - // Add vulnerabilties and finding attribution from the source component to the cloned component - for (Vulnerability vuln: sourceComponent.getVulnerabilities()) { - final FindingAttribution sourceAttribution = this.getFindingAttribution(vuln, sourceComponent); - this.addVulnerability(vuln, clonedComponent, sourceAttribution.getAnalyzerIdentity(), sourceAttribution.getAlternateIdentifier(), sourceAttribution.getReferenceUrl(), sourceAttribution.getAttributedOn()); + final Map clonedComponents = new HashMap<>(); + if (includeComponents) { + final List sourceComponents = getAllComponents(source); + if (sourceComponents != null) { + for (final Component sourceComponent : sourceComponents) { + final Component clonedComponent = cloneComponent(sourceComponent, project, false); + // Add vulnerabilties and finding attribution from the source component to the cloned component + for (Vulnerability vuln : sourceComponent.getVulnerabilities()) { + final FindingAttribution sourceAttribution = this.getFindingAttribution(vuln, sourceComponent); + this.addVulnerability(vuln, clonedComponent, sourceAttribution.getAnalyzerIdentity(), sourceAttribution.getAlternateIdentifier(), sourceAttribution.getReferenceUrl(), sourceAttribution.getAttributedOn()); + } + clonedComponents.put(sourceComponent.getId(), clonedComponent); } - clonedComponents.put(sourceComponent.getId(), clonedComponent); } } - } - if (includeServices) { - final List sourceServices = getAllServiceComponents(source); - if (sourceServices != null) { - for (final ServiceComponent sourceService : sourceServices) { - cloneServiceComponent(sourceService, project, false); + if (includeServices) { + final List sourceServices = getAllServiceComponents(source); + if (sourceServices != null) { + for (final ServiceComponent sourceService : sourceServices) { + cloneServiceComponent(sourceService, project, false); + } } } - } - if (includeAuditHistory && includeComponents) { - final List analyses = super.getAnalyses(source); - if (analyses != null) { - for (final Analysis sourceAnalysis: analyses) { - Analysis analysis = new Analysis(); - analysis.setAnalysisState(sourceAnalysis.getAnalysisState()); - final Component clonedComponent = clonedComponents.get(sourceAnalysis.getComponent().getId()); - if (clonedComponent == null) { - break; - } - analysis.setComponent(clonedComponent); - analysis.setVulnerability(sourceAnalysis.getVulnerability()); - analysis.setSuppressed(sourceAnalysis.isSuppressed()); - analysis.setAnalysisResponse(sourceAnalysis.getAnalysisResponse()); - analysis.setAnalysisJustification(sourceAnalysis.getAnalysisJustification()); - analysis.setAnalysisState(sourceAnalysis.getAnalysisState()); - analysis.setAnalysisDetails(sourceAnalysis.getAnalysisDetails()); - analysis = persist(analysis); - if (sourceAnalysis.getAnalysisComments() != null) { - for (final AnalysisComment sourceComment: sourceAnalysis.getAnalysisComments()) { - final AnalysisComment analysisComment = new AnalysisComment(); - analysisComment.setAnalysis(analysis); - analysisComment.setTimestamp(sourceComment.getTimestamp()); - analysisComment.setComment(sourceComment.getComment()); - analysisComment.setCommenter(sourceComment.getCommenter()); - persist(analysisComment); + if (includeAuditHistory && includeComponents) { + final List analyses = super.getAnalyses(source); + if (analyses != null) { + for (final Analysis sourceAnalysis : analyses) { + Analysis analysis = new Analysis(); + analysis.setAnalysisState(sourceAnalysis.getAnalysisState()); + final Component clonedComponent = clonedComponents.get(sourceAnalysis.getComponent().getId()); + if (clonedComponent == null) { + break; + } + analysis.setComponent(clonedComponent); + analysis.setVulnerability(sourceAnalysis.getVulnerability()); + analysis.setSuppressed(sourceAnalysis.isSuppressed()); + analysis.setAnalysisResponse(sourceAnalysis.getAnalysisResponse()); + analysis.setAnalysisJustification(sourceAnalysis.getAnalysisJustification()); + analysis.setAnalysisState(sourceAnalysis.getAnalysisState()); + analysis.setAnalysisDetails(sourceAnalysis.getAnalysisDetails()); + analysis = persist(analysis); + if (sourceAnalysis.getAnalysisComments() != null) { + for (final AnalysisComment sourceComment : sourceAnalysis.getAnalysisComments()) { + final AnalysisComment analysisComment = new AnalysisComment(); + analysisComment.setAnalysis(analysis); + analysisComment.setTimestamp(sourceComment.getTimestamp()); + analysisComment.setComment(sourceComment.getComment()); + analysisComment.setCommenter(sourceComment.getCommenter()); + persist(analysisComment); + } } } } } - } - if (includeACL) { - List accessTeams = source.getAccessTeams(); - if (!CollectionUtils.isEmpty(accessTeams)) { - project.setAccessTeams(new ArrayList<>(accessTeams)); + if (includeACL) { + List accessTeams = source.getAccessTeams(); + if (!CollectionUtils.isEmpty(accessTeams)) { + project.setAccessTeams(new ArrayList<>(accessTeams)); + } } - } - - if(includeComponents && includePolicyViolations){ - final List sourcePolicyViolations = getAllPolicyViolations(source); - if(sourcePolicyViolations != null){ - for(final PolicyViolation policyViolation: sourcePolicyViolations){ - final Component destinationComponent = clonedComponents.get(policyViolation.getComponent().getId()); - final PolicyViolation clonedPolicyViolation = clonePolicyViolation(policyViolation, destinationComponent); - persist(clonedPolicyViolation); - } + + if (includeComponents && includePolicyViolations) { + final List sourcePolicyViolations = getAllPolicyViolations(source); + if (sourcePolicyViolations != null) { + for (final PolicyViolation policyViolation : sourcePolicyViolations) { + final Component destinationComponent = clonedComponents.get(policyViolation.getComponent().getId()); + final PolicyViolation clonedPolicyViolation = clonePolicyViolation(policyViolation, destinationComponent); + persist(clonedPolicyViolation); + } + } } - } - - project = getObjectById(Project.class, project.getId()); - Event.dispatch(new IndexEvent(IndexEvent.Action.CREATE, project)); + return project; + }); + + Event.dispatch(new IndexEvent(IndexEvent.Action.CREATE, clonedProject)); commitSearchIndex(true, Project.class); - return project; + return clonedProject; } /** diff --git a/src/main/java/org/dependencytrack/tasks/CloneProjectTask.java b/src/main/java/org/dependencytrack/tasks/CloneProjectTask.java index 37e2b0f46c..863c46ed58 100644 --- a/src/main/java/org/dependencytrack/tasks/CloneProjectTask.java +++ b/src/main/java/org/dependencytrack/tasks/CloneProjectTask.java @@ -25,9 +25,13 @@ import org.dependencytrack.model.Project; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.resources.v1.vo.CloneProjectRequest; +import org.slf4j.MDC; import java.util.UUID; +import static org.dependencytrack.common.MdcKeys.MDC_EVENT_TOKEN; +import static org.dependencytrack.common.MdcKeys.MDC_PROJECT_UUID; + public class CloneProjectTask implements Subscriber { private static final Logger LOGGER = Logger.getLogger(CloneProjectTask.class); @@ -36,15 +40,26 @@ public class CloneProjectTask implements Subscriber { * {@inheritDoc} */ public void inform(final Event e) { - if (e instanceof CloneProjectEvent) { - final CloneProjectEvent event = (CloneProjectEvent)e; + if (e instanceof final CloneProjectEvent event) { final CloneProjectRequest request = event.getRequest(); - LOGGER.info("Cloning project: " + request.getProject()); - try (QueryManager qm = new QueryManager()) { - final Project project = qm.clone(UUID.fromString(request.getProject()), - request.getVersion(), request.includeTags(), request.includeProperties(), - request.includeComponents(), request.includeServices(), request.includeAuditHistory(), request.includeACL(), request.includePolicyViolations()); - LOGGER.info("Cloned project: " + request.getProject() + " to " + project.getUuid()); + try (var ignoredMdcProjectUuid = MDC.putCloseable(MDC_PROJECT_UUID, request.getProject()); + var ignoredMdcEventToken = MDC.putCloseable(MDC_EVENT_TOKEN, event.getChainIdentifier().toString()); + final var qm = new QueryManager()) { + LOGGER.info("Cloning project for version %s".formatted(request.getVersion())); + final Project project = qm.clone( + UUID.fromString(request.getProject()), + request.getVersion(), + request.includeTags(), + request.includeProperties(), + request.includeComponents(), + request.includeServices(), + request.includeAuditHistory(), + request.includeACL(), + request.includePolicyViolations() + ); + LOGGER.info("Cloned project for version %s into project %s".formatted(project.getVersion(), project.getUuid())); + } catch (RuntimeException ex) { + LOGGER.error("Failed to clone project", ex); } } } diff --git a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java index 32628e3081..4703e41ae7 100644 --- a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java @@ -1216,6 +1216,83 @@ public void informIssue3957Test() { }); } + @Test + public void informIssue3981Test() { + final var project = new Project(); + project.setName("acme-license-app"); + project.setVersion("1.2.3"); + qm.persist(project); + + byte[] bomBytes = """ + { + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b80", + "version": 1, + "metadata": { + "authors": [ + { + "name": "foo", + "email": "foo@example.com" + } + ] + }, + "components": [ + { + "type": "library", + "name": "acme-lib-x" + } + ] + } + """.getBytes(StandardCharsets.UTF_8); + + var bomUploadEvent = new BomUploadEvent(qm.detach(Project.class, project.getId()), bomBytes); + new BomUploadProcessingTask().inform(bomUploadEvent); + awaitBomProcessedNotification(bomUploadEvent); + NOTIFICATIONS.clear(); + + final Project clonedProject = qm.clone(project.getUuid(), "3.2.1", true, true, true, true, true, true, true); + + bomBytes = """ + { + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b80", + "version": 1, + "metadata": { + "authors": [ + { + "name": "bar", + "email": "bar@example.com" + } + ] + }, + "components": [ + { + "type": "library", + "name": "acme-lib-x" + } + ] + } + """.getBytes(StandardCharsets.UTF_8); + + bomUploadEvent = new BomUploadEvent(qm.detach(Project.class, clonedProject.getId()), bomBytes); + new BomUploadProcessingTask().inform(bomUploadEvent); + awaitBomProcessedNotification(bomUploadEvent); + + qm.getPersistenceManager().evictAll(); + + assertThat(project.getMetadata().getAuthors()).satisfiesExactly(author -> { + assertThat(author.getName()).isEqualTo("foo"); + assertThat(author.getEmail()).isEqualTo("foo@example.com"); + }); + + assertThat(clonedProject.getMetadata().getAuthors()).satisfiesExactly(author -> { + assertThat(author.getName()).isEqualTo("bar"); + assertThat(author.getEmail()).isEqualTo("bar@example.com"); + }); + } + private void awaitBomProcessedNotification(final BomUploadEvent bomUploadEvent) { try { await("BOM Processed Notification") From 20cf73e30a66482b4946e014f734c652fabc041b Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Tue, 16 Jul 2024 17:21:16 +0100 Subject: [PATCH 015/429] add option to test notification publisher Signed-off-by: Ross Murphy --- .../v1/NotificationPublisherResource.java | 63 +++++++++++++++---- .../v1/NotificationPublisherResourceTest.java | 22 ++++--- 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java index 01381671c7..f56ca4c253 100644 --- a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java @@ -34,6 +34,8 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.security.SecurityRequirements; import io.swagger.v3.oas.annotations.tags.Tag; + +import org.apache.commons.text.WordUtils; import org.dependencytrack.auth.Permissions; import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.NotificationPublisher; @@ -41,9 +43,16 @@ import org.dependencytrack.notification.NotificationConstants; import org.dependencytrack.notification.NotificationGroup; import org.dependencytrack.notification.NotificationScope; +import org.dependencytrack.notification.publisher.ConsolePublisher; +import org.dependencytrack.notification.publisher.CsWebexPublisher; +import org.dependencytrack.notification.publisher.JiraPublisher; +import org.dependencytrack.notification.publisher.MattermostPublisher; +import org.dependencytrack.notification.publisher.MsTeamsPublisher; import org.dependencytrack.notification.publisher.PublishContext; import org.dependencytrack.notification.publisher.Publisher; import org.dependencytrack.notification.publisher.SendMailPublisher; +import org.dependencytrack.notification.publisher.SlackPublisher; +import org.dependencytrack.notification.publisher.WebhookPublisher; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.util.NotificationUtil; @@ -286,7 +295,7 @@ public Response restoreDefaultTemplates() { } @POST - @Path("/test/smtp") + @Path("/test/{publisher}") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces(MediaType.APPLICATION_JSON) @Operation( @@ -298,28 +307,56 @@ public Response restoreDefaultTemplates() { @ApiResponse(responseCode = "401", description = "Unauthorized") }) @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) - public Response testSmtpPublisherConfig(@FormParam("destination") String destination) { - try(QueryManager qm = new QueryManager()) { - Class defaultEmailPublisherClass = SendMailPublisher.class; - NotificationPublisher emailNotificationPublisher = qm.getDefaultNotificationPublisher(defaultEmailPublisherClass); - final Publisher emailPublisher = defaultEmailPublisherClass.getDeclaredConstructor().newInstance(); + public Response testSlackPublisherConfig(@FormParam("destination") String destination, @PathParam("publisher") String publisherType) { + try(QueryManager qm = new QueryManager()){ + Class defaultPublisherClass; + switch (publisherType) { + case "email": + defaultPublisherClass = SendMailPublisher.class; + break; + case "cisco_webex": + defaultPublisherClass = CsWebexPublisher.class; + break; + case "slack": + defaultPublisherClass = SlackPublisher.class; + break; + case "microsoft_teams": + defaultPublisherClass = MsTeamsPublisher.class; + break; + case "jira": + defaultPublisherClass = JiraPublisher.class; + break; + case "mattermost": + defaultPublisherClass = MattermostPublisher.class; + break; + case "outbound_webhook": + defaultPublisherClass = WebhookPublisher.class; + break; + case "console": + defaultPublisherClass = ConsolePublisher.class; + break; + default: + return Response.status(Response.Status.BAD_REQUEST).entity("Invalid publisher was provided.").build(); + } + NotificationPublisher notificationPublisher = qm.getDefaultNotificationPublisher(defaultPublisherClass); + final Publisher publisher = defaultPublisherClass.getDeclaredConstructor().newInstance(); final JsonObject config = Json.createObjectBuilder() .add(Publisher.CONFIG_DESTINATION, destination) - .add(Publisher.CONFIG_TEMPLATE_KEY, emailNotificationPublisher.getTemplate()) - .add(Publisher.CONFIG_TEMPLATE_MIME_TYPE_KEY, emailNotificationPublisher.getTemplateMimeType()) + .add(Publisher.CONFIG_TEMPLATE_KEY, notificationPublisher.getTemplate()) + .add(Publisher.CONFIG_TEMPLATE_MIME_TYPE_KEY, notificationPublisher.getTemplateMimeType()) .build(); final Notification notification = new Notification() .scope(NotificationScope.SYSTEM) .group(NotificationGroup.CONFIGURATION) .title(NotificationConstants.Title.NOTIFICATION_TEST) - .content("SMTP configuration test") + .content(WordUtils.capitalize(publisherType.replaceAll("_", " ")) + " Configuration test") .level(NotificationLevel.INFORMATIONAL); - // Bypass Notification.dispatch() and go directly to the publisher itself - emailPublisher.inform(PublishContext.from(notification), notification, config); + publisher.inform(PublishContext.from(notification), notification, config); return Response.ok().build(); - } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + } + catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { LOGGER.error(e.getMessage(), e); - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Exception occured while sending test mail notification.").build(); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Exception occured while sending " + publisherType + " test notification.").build(); } } } diff --git a/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java index 293c3e6cc2..e027fd22d9 100644 --- a/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java @@ -44,6 +44,9 @@ import jakarta.ws.rs.core.Form; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; + +import java.util.Arrays; +import java.util.List; import java.util.UUID; public class NotificationPublisherResourceTest extends ResourceTest { @@ -330,13 +333,16 @@ public void deleteDefaultNotificationPublisherTest() { } @Test - public void testSmtpPublisherConfigTest() { - Form form = new Form(); - form.param("destination", "test@example.com"); - Response response = jersey.target(V1_NOTIFICATION_PUBLISHER + "/test/smtp").request() - .header(X_API_KEY, apiKey) - .post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); - Assert.assertEquals(200, response.getStatus(), 0); + public void testPublishersConfigTest() { + List publishers = Arrays.asList("slack", "email", "cisco_webex", "microsoft_teams", "jira", "mattermost", "outbound_webhook", "console"); + for(String publisher : publishers){ + Form form = new Form(); + form.param("destination", "http://example.com"); + Response response = jersey.target(V1_NOTIFICATION_PUBLISHER + "/test/" + publisher).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + Assert.assertEquals(200, response.getStatus(), 0); + } } @Test @@ -361,4 +367,6 @@ public void restoreDefaultTemplatesTest() { slackPublisher = qm.getDefaultNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherClass()); Assert.assertEquals(DefaultNotificationPublishers.SLACK.getPublisherName(), slackPublisher.getName()); } + + } From 66aeac3bf7c5007ddfb09808bb1f637771004832 Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Tue, 16 Jul 2024 17:33:56 +0100 Subject: [PATCH 016/429] whitespaces Signed-off-by: Ross Murphy --- .../resources/v1/NotificationPublisherResource.java | 3 +-- .../resources/v1/NotificationPublisherResourceTest.java | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java index f56ca4c253..34b3ecbfaf 100644 --- a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java @@ -353,8 +353,7 @@ public Response testSlackPublisherConfig(@FormParam("destination") String destin .level(NotificationLevel.INFORMATIONAL); publisher.inform(PublishContext.from(notification), notification, config); return Response.ok().build(); - } - catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { LOGGER.error(e.getMessage(), e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Exception occured while sending " + publisherType + " test notification.").build(); } diff --git a/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java index e027fd22d9..d968dd5757 100644 --- a/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java @@ -367,6 +367,4 @@ public void restoreDefaultTemplatesTest() { slackPublisher = qm.getDefaultNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherClass()); Assert.assertEquals(DefaultNotificationPublishers.SLACK.getPublisherName(), slackPublisher.getName()); } - - } From 28ed9022d470f87609d5569ad2cea1f6367c039a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 08:07:00 +0000 Subject: [PATCH 017/429] Bump io.github.jeremylong:open-vulnerability-clients from 6.1.6 to 6.1.7 Bumps [io.github.jeremylong:open-vulnerability-clients](https://github.com/jeremylong/vuln-tools) from 6.1.6 to 6.1.7. - [Release notes](https://github.com/jeremylong/vuln-tools/releases) - [Commits](https://github.com/jeremylong/vuln-tools/compare/v6.1.6...v6.1.7) --- updated-dependencies: - dependency-name: io.github.jeremylong:open-vulnerability-clients dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c33d028590..4683ce0aae 100644 --- a/pom.xml +++ b/pom.xml @@ -107,7 +107,7 @@ 8.11.3 3.9.8 5.15.0 - 6.1.6 + 6.1.7 1.5.0 3.2.2 2.2.0 From b008b77654c5cba469695cb7ec379beb95adcf0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 08:08:09 +0000 Subject: [PATCH 018/429] Bump net.javacrumbs.json-unit:json-unit-assertj from 3.4.0 to 3.4.1 Bumps [net.javacrumbs.json-unit:json-unit-assertj](https://github.com/lukas-krecan/JsonUnit) from 3.4.0 to 3.4.1. - [Commits](https://github.com/lukas-krecan/JsonUnit/compare/json-unit-parent-3.4.0...json-unit-parent-3.4.1) --- updated-dependencies: - dependency-name: net.javacrumbs.json-unit:json-unit-assertj dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c33d028590..9be9cfd5b7 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ 2.17.1 2.17.1 20240303 - 3.4.0 + 3.4.1 8.11.3 3.9.8 5.15.0 From 6d9df5751ec46febb18758335abf3e8dadf3b514 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 08:34:38 +0000 Subject: [PATCH 019/429] Bump org.testcontainers:testcontainers from 1.19.8 to 1.20.0 Bumps [org.testcontainers:testcontainers](https://github.com/testcontainers/testcontainers-java) from 1.19.8 to 1.20.0. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.19.8...1.20.0) --- updated-dependencies: - dependency-name: org.testcontainers:testcontainers dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c33d028590..8eae2e3f42 100644 --- a/pom.xml +++ b/pom.xml @@ -113,7 +113,7 @@ 2.2.0 2.1.22 1.19.0 - 1.19.8 + 1.20.0 2.35.2 6.6.2 1.1.1 From 2c9dd87f20da6941996215ef773f4536b2798d74 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sat, 20 Jul 2024 12:23:00 +0200 Subject: [PATCH 020/429] Log warning when dependency graph is missing the root node Also clean up code that is no longer necessary since Alpine v3, and apply consistent formatting for long method signatures. Fixes #3989 Signed-off-by: nscuro --- .../tasks/BomUploadProcessingTask.java | 109 +++++++++--------- 1 file changed, 56 insertions(+), 53 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java b/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java index 3d72208567..4802eae16f 100644 --- a/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java +++ b/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java @@ -85,7 +85,6 @@ import static org.apache.commons.lang3.time.DurationFormatUtils.formatDurationHMS; import static org.datanucleus.PropertyNames.PROPERTY_FLUSH_MODE; import static org.datanucleus.PropertyNames.PROPERTY_PERSISTENCE_BY_REACHABILITY_AT_COMMIT; -import static org.datanucleus.PropertyNames.PROPERTY_RETAIN_VALUES; import static org.dependencytrack.common.MdcKeys.MDC_BOM_FORMAT; import static org.dependencytrack.common.MdcKeys.MDC_BOM_SERIAL_NUMBER; import static org.dependencytrack.common.MdcKeys.MDC_BOM_SPEC_VERSION; @@ -259,14 +258,6 @@ private void processBom(final Context ctx, final org.cyclonedx.model.Bom cdxBom) // BomUploadProcessingTaskTest#informWithBloatedBomTest can be used to profile the impact on large BOMs. qm.getPersistenceManager().setProperty(PROPERTY_FLUSH_MODE, FlushMode.MANUAL.name()); - // Prevent object fields from being unloaded upon commit. - // - // DataNucleus transitions objects into the "hollow" state after the transaction is committed. - // In hollow state, all fields except the ID are unloaded. Accessing fields afterward triggers - // one or more database queries to load them again. - // See https://www.datanucleus.org/products/accessplatform_6_0/jdo/persistence.html#lifecycle - qm.getPersistenceManager().setProperty(PROPERTY_RETAIN_VALUES, "true"); - qm.getPersistenceManager().addInstanceLifecycleListener(new IndexingInstanceLifecycleListener(eventsToDispatch::add), Component.class, Project.class, ProjectMetadata.class, ServiceComponent.class); qm.getPersistenceManager().addInstanceLifecycleListener(new L2CacheEvictingInstanceLifecycleListener(qm), @@ -310,8 +301,12 @@ private void processBom(final Context ctx, final org.cyclonedx.model.Bom cdxBom) dispatchBomProcessedNotification(ctx); } - private Project processProject(final Context ctx, final QueryManager qm, - final Project project, final ProjectMetadata projectMetadata) { + private Project processProject( + final Context ctx, + final QueryManager qm, + final Project project, + final ProjectMetadata projectMetadata + ) { final Query query = qm.getPersistenceManager().newQuery(Project.class); query.setFilter("uuid == :uuid"); query.setParameters(ctx.project.getUuid()); @@ -347,7 +342,7 @@ private Project processProject(final Context ctx, final QueryManager qm, if (projectMetadata != null) { if (persistentProject.getMetadata() == null) { projectMetadata.setProject(persistentProject); - qm.getPersistenceManager().makePersistent(projectMetadata); + qm.persist(projectMetadata); hasChanged = true; } else { hasChanged |= applyIfChanged(persistentProject.getMetadata(), projectMetadata, ProjectMetadata::getAuthors, persistentProject.getMetadata()::setAuthors); @@ -362,11 +357,13 @@ private Project processProject(final Context ctx, final QueryManager qm, return persistentProject; } - private Map processComponents(final QueryManager qm, - final Project project, - final List components, - final Map identitiesByBomRef, - final MultiValuedMap bomRefsByIdentity) { + private Map processComponents( + final QueryManager qm, + final Project project, + final List components, + final Map identitiesByBomRef, + final MultiValuedMap bomRefsByIdentity + ) { assertPersistent(project, "Project mut be persistent"); // Fetch IDs of all components that exist in the project already. @@ -405,7 +402,7 @@ private Map processComponents(final QueryManager q } if (persistentComponent == null) { component.setProject(project); - persistentComponent = qm.getPersistenceManager().makePersistent(component); + persistentComponent = qm.persist(component); component.setNew(true); // Transient } else { persistentComponent.setBomRef(component.getBomRef()); // Transient @@ -464,11 +461,13 @@ private Map processComponents(final QueryManager q return persistentComponents; } - private Map processServices(final QueryManager qm, - final Project project, - final List services, - final Map identitiesByBomRef, - final MultiValuedMap bomRefsByIdentity) { + private Map processServices( + final QueryManager qm, + final Project project, + final List services, + final Map identitiesByBomRef, + final MultiValuedMap bomRefsByIdentity + ) { assertPersistent(project, "Project must be persistent"); final PersistenceManager pm = qm.getPersistenceManager(); @@ -484,7 +483,7 @@ private Map processServices(final QueryMana ServiceComponent persistentService = qm.matchServiceIdentity(project, componentIdentity); if (persistentService == null) { service.setProject(project); - persistentService = pm.makePersistent(service); + persistentService = qm.persist(service); } else { persistentService.setBomRef(service.getBomRef()); // Transient applyIfChanged(persistentService, service, ServiceComponent::getGroup, persistentService::setGroup); @@ -534,22 +533,30 @@ private void recordBomImport(final Context ctx, final QueryManager qm, final Pro bom.setSerialNumber(ctx.bomSerialNumber); bom.setBomVersion(ctx.bomVersion); bom.setImported(bomImportDate); - qm.getPersistenceManager().makePersistent(bom); + qm.persist(bom); project.setLastBomImport(bomImportDate); project.setLastBomImportFormat("%s %s".formatted(ctx.bomFormat.getFormatShortName(), ctx.bomSpecVersion)); qm.getPersistenceManager().flush(); } - private void processDependencyGraph(final QueryManager qm, - final Project project, - final MultiValuedMap dependencyGraph, - final Map componentsByIdentity, - final Map identitiesByBomRef) { + private void processDependencyGraph( + final QueryManager qm, + final Project project, + final MultiValuedMap dependencyGraph, + final Map componentsByIdentity, + final Map identitiesByBomRef + ) { assertPersistent(project, "Project must be persistent"); if (project.getBomRef() != null) { final Collection directDependencyBomRefs = dependencyGraph.get(project.getBomRef()); + if (directDependencyBomRefs == null || directDependencyBomRefs.isEmpty()) { + LOGGER.warn(""" + The dependency graph has %d entries, but the project (metadata.component node of the BOM) \ + is not one of them; Graph will be incomplete because it is not possible to determine its root\ + """.formatted(dependencyGraph.size())); + } final String directDependenciesJson = resolveDirectDependenciesJson(project.getBomRef(), directDependencyBomRefs, identitiesByBomRef); if (!Objects.equals(directDependenciesJson, project.getDirectDependencies())) { project.setDirectDependencies(directDependenciesJson); @@ -588,8 +595,10 @@ private void processDependencyGraph(final QueryManager qm, qm.getPersistenceManager().flush(); } - private static Predicate distinctComponentsByIdentity(final Map identitiesByBomRef, - final MultiValuedMap bomRefsByIdentity) { + private static Predicate distinctComponentsByIdentity( + final Map identitiesByBomRef, + final MultiValuedMap bomRefsByIdentity + ) { final var identitiesSeen = new HashSet(); return component -> { final var componentIdentity = new ComponentIdentity(component); @@ -614,8 +623,10 @@ private static Predicate distinctComponentsByIdentity(final Map distinctServicesByIdentity(final Map identitiesByBomRef, - final MultiValuedMap bomRefsByIdentity) { + private static Predicate distinctServicesByIdentity( + final Map identitiesByBomRef, + final MultiValuedMap bomRefsByIdentity + ) { final var identitiesSeen = new HashSet(); return service -> { final var componentIdentity = new ComponentIdentity(service); @@ -631,9 +642,11 @@ private static Predicate distinctServicesByIdentity(final Map< }; } - private String resolveDirectDependenciesJson(final String dependencyBomRef, - final Collection directDependencyBomRefs, - final Map identitiesByBomRef) { + private String resolveDirectDependenciesJson( + final String dependencyBomRef, + final Collection directDependencyBomRefs, + final Map identitiesByBomRef + ) { if (directDependencyBomRefs == null || directDependencyBomRefs.isEmpty()) { return null; } @@ -654,10 +667,12 @@ private String resolveDirectDependenciesJson(final String dependencyBomRef, return jsonDependencies.isEmpty() ? null : jsonDependencies.toString(); } - private static void resolveAndApplyLicense(final QueryManager qm, - final Component component, - final Map licenseCache, - final Map customLicenseCache) { + private static void resolveAndApplyLicense( + final QueryManager qm, + final Component component, + final Map licenseCache, + final Map customLicenseCache + ) { // CycloneDX components can declare multiple licenses, but we currently // only support one. We assume that the licenseCandidates list is ordered // by priority, and simply take the first resolvable candidate. @@ -701,18 +716,6 @@ private static void resolveAndApplyLicense(final QueryManager qm, } } - private static License resolveCustomLicense(final QueryManager qm, final String licenseName) { - final Query query = qm.getPersistenceManager().newQuery(License.class); - query.setFilter("name == :name && customLicense == true"); - query.setParameters(licenseName); - try { - final License license = query.executeUnique(); - return license != null ? license : License.UNRESOLVED; - } finally { - query.closeAll(); - } - } - private static Set getAllComponentIds(final QueryManager qm, final Project project, final Class clazz) { final Query query = qm.getPersistenceManager().newQuery(clazz); query.setFilter("project == :project"); From 1c71e24df096a53b978328c56104718dd8a3d60f Mon Sep 17 00:00:00 2001 From: JCHacking Date: Sun, 21 Jul 2024 18:27:29 +0200 Subject: [PATCH 021/429] fix: fix anchors in changelog documenation Refs: 2094 Signed-off-by: JCHacking --- docs/_layouts/default.html | 2 +- docs/_posts/2018-03-27-v3.0.0.md | 1 - docs/_posts/2018-03-30-v3.0.2.md | 2 +- docs/_posts/2018-10-02-v3.2.2.md | 6 +++--- docs/_posts/2018-10-25-v3.3.0.md | 6 +++--- docs/_posts/2018-11-13-v3.3.1.md | 6 +++--- docs/_posts/2018-12-22-v3.4.0.md | 6 +++--- docs/_posts/2019-04-16-v3.4.1.md | 6 +++--- docs/_posts/2019-06-07-v3.5.0.md | 6 +++--- docs/_posts/2019-07-17-v3.5.1.md | 6 +++--- docs/_posts/2019-09-28-v3.6.0.md | 6 +++--- docs/_posts/2019-10-01-v3.6.1.md | 6 +++--- docs/_posts/2019-12-16-v3.7.0.md | 6 +++--- docs/_posts/2020-01-07-v3.7.1.md | 6 +++--- docs/_posts/2020-03-22-v3.8.0.md | 6 +++--- docs/_posts/2021-01-03-v4.0.0.md | 8 ++++---- docs/_posts/2021-01-12-v4.0.1.md | 8 ++++---- docs/_posts/2021-02-09-v4.1.0.md | 8 ++++---- docs/_posts/2021-03-17-v4.2.0.md | 8 ++++---- docs/_posts/2021-03-20-v4.2.1.md | 8 ++++---- docs/_posts/2021-05-07-v4.2.2.md | 8 ++++---- docs/_posts/2021-08-02-v4.3.0.md | 6 +++--- docs/_posts/2021-08-03-v4.3.1.md | 6 +++--- docs/_posts/2021-08-07-v4.3.2.md | 6 +++--- docs/_posts/2021-08-20-v4.3.3.md | 6 +++--- docs/_posts/2021-08-31-v4.3.4.md | 6 +++--- docs/_posts/2021-09-20-v4.3.5.md | 6 +++--- docs/_posts/2021-09-20-v4.3.6.md | 6 +++--- docs/_posts/2022-02-17-v4.4.0.md | 6 +++--- docs/_posts/2022-02-18-v4.4.1.md | 6 +++--- docs/_posts/2022-03-04-v4.4.2.md | 6 +++--- docs/_posts/2022-05-18-v4.5.0.md | 6 +++--- docs/_posts/2022-10-11-v4.6.0.md | 6 +++--- docs/_posts/2022-10-13-v4.6.1.md | 6 +++--- docs/_posts/2022-10-24-v4.6.2.md | 8 ++++---- docs/_posts/2022-11-18-v4.6.3.md | 6 +++--- docs/_posts/2022-12-16-v4.7.0.md | 8 ++++---- docs/_posts/2023-01-31-v4.7.1.md | 6 +++--- docs/_posts/2023-04-18-v4.8.0.md | 8 ++++---- docs/_posts/2023-05-16-v4.8.1.md | 6 +++--- docs/_posts/2023-05-17-v4.8.2.md | 6 +++--- docs/_posts/2023-10-16-v4.9.0.md | 8 ++++---- docs/_posts/2023-10-30-v4.9.1.md | 6 +++--- docs/_posts/2023-12-08-v4.10.0.md | 8 ++++---- docs/_posts/2023-12-19-v4.10.1.md | 6 +++--- docs/_posts/2024-05-07-v4.11.0.md | 6 +++--- docs/_posts/2024-05-19-v4.11.1.md | 8 ++++---- docs/_posts/2024-06-01-v4.11.2.md | 6 +++--- docs/_posts/2024-06-03-v4.11.3.md | 8 ++++---- docs/_posts/2024-06-24-v4.11.4.md | 8 ++++---- docs/_posts/2024-07-08-v4.11.5.md | 8 ++++---- 51 files changed, 161 insertions(+), 162 deletions(-) diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html index d14be053a9..29b660225b 100755 --- a/docs/_layouts/default.html +++ b/docs/_layouts/default.html @@ -66,7 +66,7 @@

{% if page.category %}{{ page.category }}{% else %}{{ site.title }}{% endif

{{ page.title }}

- {% include anchor_headings.html html=content anchorBody="#" anchorClass="anchor" %} + {% include anchor_headings.html html=content anchorBody="#" anchorClass="anchor" generateId=true h_max=3 %}
diff --git a/docs/_posts/2018-03-27-v3.0.0.md b/docs/_posts/2018-03-27-v3.0.0.md index 5dcc26f36f..2a12cf0b1d 100644 --- a/docs/_posts/2018-03-27-v3.0.0.md +++ b/docs/_posts/2018-03-27-v3.0.0.md @@ -22,4 +22,3 @@ Project Reboot Successful! This is the first release after being developed from * Simple to install and configure. Get up and running in just a few minutes **Fixes:** - diff --git a/docs/_posts/2018-03-30-v3.0.2.md b/docs/_posts/2018-03-30-v3.0.2.md index 3c8b88756c..49fddb12a4 100644 --- a/docs/_posts/2018-03-30-v3.0.2.md +++ b/docs/_posts/2018-03-30-v3.0.2.md @@ -5,4 +5,4 @@ type: minor **Fixes:** -* Responded to changes in NVD data feed URLs by correcting the XML 1.2 and 2.0 URLs used for mirroring. +* Responded to changes in NVD data feed URLs by correcting the XML 1.2 and 2.0 URLs used for mirroring. \ No newline at end of file diff --git a/docs/_posts/2018-10-02-v3.2.2.md b/docs/_posts/2018-10-02-v3.2.2.md index c0aa0b8c93..24e138aa50 100644 --- a/docs/_posts/2018-10-02-v3.2.2.md +++ b/docs/_posts/2018-10-02-v3.2.2.md @@ -11,14 +11,14 @@ type: minor * Added checksums.xml including SHA-1, SHA-256, SHA-512 checksums for traditional and embedded wars to GitHub releases. -###### dependency-track-embedded.war +###### [dependency-track-embedded.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.2.2/dependency-track-embedded.war) | Algorithm | Checksum | | SHA-1 | fead4ed834b4738b8c19c427ae57653f7af4a3b8 | | SHA-256 | ee53ceacb07b0b0b4dfa88e2bdc2e905668f0dd6d42ca1000b3204d0a2ee1842 | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.2.2/dependency-track.war) | Algorithm | Checksum | | SHA-1 | defbb7a40bb12c3beacdeb43fb5fd325d226da50 | -| SHA-256 | c154f0f07c9875d602d3e1df93d93d617e83f350ef683bdb16eb193d03a86ea5 | +| SHA-256 | c154f0f07c9875d602d3e1df93d93d617e83f350ef683bdb16eb193d03a86ea5 | \ No newline at end of file diff --git a/docs/_posts/2018-10-25-v3.3.0.md b/docs/_posts/2018-10-25-v3.3.0.md index f4965b8a59..ff08901be6 100644 --- a/docs/_posts/2018-10-25-v3.3.0.md +++ b/docs/_posts/2018-10-25-v3.3.0.md @@ -56,14 +56,14 @@ alpine.ldap.team.synchronization * [Configuration]({{ site.baseurl }}{% link _docs/getting-started/configuration.md %}) (updated) * [LDAP Configuration]({{ site.baseurl }}{% link _docs/getting-started/ldap-configuration.md %}) (examples) -###### dependency-track-embedded.war +###### [dependency-track-embedded.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.3.0/dependency-track-embedded.war) | Algorithm | Checksum | | SHA-1 | 413b47068dd1272f0ea6c4af67dc1465fcf10674 | | SHA-256 | 5632d6efa8c6ea2633bb767d09c73c4ee68e9319b638ce00da4c422f5123c906 | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.3.0/dependency-track.war) | Algorithm | Checksum | | SHA-1 | 1a8dc64a7535375fdd4ff789eeb9d3635dcba019 | -| SHA-256 | 96e20c0b72e3d8c460dfe3ce2b9aca72c6114492747db9afffca9784c64d23b9 | +| SHA-256 | 96e20c0b72e3d8c460dfe3ce2b9aca72c6114492747db9afffca9784c64d23b9 | \ No newline at end of file diff --git a/docs/_posts/2018-11-13-v3.3.1.md b/docs/_posts/2018-11-13-v3.3.1.md index 3e8d7ac4f5..213c991b62 100644 --- a/docs/_posts/2018-11-13-v3.3.1.md +++ b/docs/_posts/2018-11-13-v3.3.1.md @@ -19,14 +19,14 @@ type: minor The format of the findings API has changed and will not be versioned. This API is used to present findings from the audit tab in the UI. If this API was being used outside the UI, please note that the response format has changed. -###### dependency-track-embedded.war +###### [dependency-track-embedded.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.3.1/dependency-track-embedded.war) | Algorithm | Checksum | | SHA-1 | f7a0fcf9568a765b9bb3cdf3465f475810c333e8 | | SHA-256 | f5693cab665932c80e7056c37ed93bf61a1638e252e48e9c0717b8d0c4740ea4 | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.3.1/dependency-track.war) | Algorithm | Checksum | | SHA-1 | bfcf20a5cb87d562b781419f7b989c35ff67e390 | -| SHA-256 | 91156bc404ab84a09e912302888ef06c52813764e88ad73039550a9ff2e82b91 | +| SHA-256 | 91156bc404ab84a09e912302888ef06c52813764e88ad73039550a9ff2e82b91 | \ No newline at end of file diff --git a/docs/_posts/2018-12-22-v3.4.0.md b/docs/_posts/2018-12-22-v3.4.0.md index cfc3666705..934b63bd77 100644 --- a/docs/_posts/2018-12-22-v3.4.0.md +++ b/docs/_posts/2018-12-22-v3.4.0.md @@ -27,14 +27,14 @@ type: major * Updated OpenUnirest which addressed configuration issue with library not honoring proxy server settings -###### dependency-track-embedded.war +###### [dependency-track-embedded.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.4.0/dependency-track-embedded.war) | Algorithm | Checksum | | SHA-1 | 676e04e0ef002e371da3b5eab239b0ab55dffe57 | | SHA-256 | 006801f124d190e929ab7e6352adcc0bf89047259eff5a15cf4d54a01d7b402d | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.4.0/dependency-track.war) | Algorithm | Checksum | | SHA-1 | 15309c0818034ac99f603b52f242748b255818b9 | -| SHA-256 | 624fa3e7f458b163a0bbb8f05ee7cb1cf052d6d4ea53ff2b43686dd55bb83135 | +| SHA-256 | 624fa3e7f458b163a0bbb8f05ee7cb1cf052d6d4ea53ff2b43686dd55bb83135 | \ No newline at end of file diff --git a/docs/_posts/2019-04-16-v3.4.1.md b/docs/_posts/2019-04-16-v3.4.1.md index 1fe022eb38..4925710cd3 100644 --- a/docs/_posts/2019-04-16-v3.4.1.md +++ b/docs/_posts/2019-04-16-v3.4.1.md @@ -8,14 +8,14 @@ type: minor * Fixed defect that caused high CPU consumption (via thread exhaustion) in some cases when NPM Audit was enabled. -###### dependency-track-embedded.war +###### [dependency-track-embedded.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.4.1/dependency-track-embedded.war) | Algorithm | Checksum | | SHA-1 | f8da8e34a3cabcf72b721488f5294710ff632bf6 | | SHA-256 | 72391cc636c2159ffc0c516f2001688129a3b6424164c98ce9045c0fd5c3219b | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.4.1/dependency-track.war) | Algorithm | Checksum | | SHA-1 | 1cdb5b6c5698229b21acbc610df77ec819ad5180 | -| SHA-256 | 619e9ae00feb9f9723bef68981d32932d2d5cdf808b192619bf072d525224f5e | +| SHA-256 | 619e9ae00feb9f9723bef68981d32932d2d5cdf808b192619bf072d525224f5e | \ No newline at end of file diff --git a/docs/_posts/2019-06-07-v3.5.0.md b/docs/_posts/2019-06-07-v3.5.0.md index 73c7121e75..882e35b735 100644 --- a/docs/_posts/2019-06-07-v3.5.0.md +++ b/docs/_posts/2019-06-07-v3.5.0.md @@ -35,14 +35,14 @@ Under most situations, changing these values is not recommended and may introduc One important change introduced in this release is the default value of `alpine.database.pool.max.lifetime` has changed from 30 minutes (in previous releases) to 10 minutes. -###### dependency-track-embedded.war +###### [dependency-track-embedded.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.5.0/dependency-track-embedded.war) | Algorithm | Checksum | | SHA-1 | 7d66f0530d74ff9bc0de628d5e76b5ee6ed6ead7 | | SHA-256 | 8bbf820fde7843a680fd51eed831aeddd61507f5420abb68b46859168cc98919 | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.5.0/dependency-track.war) | Algorithm | Checksum | | SHA-1 | 0bb9a0737a36ebbcd88fe91ca595f12957e85583 | -| SHA-256 | 143ed44988419ba84cc3956e602e297f025149f19faa65f32c0e8311b71fed5b | +| SHA-256 | 143ed44988419ba84cc3956e602e297f025149f19faa65f32c0e8311b71fed5b | \ No newline at end of file diff --git a/docs/_posts/2019-07-17-v3.5.1.md b/docs/_posts/2019-07-17-v3.5.1.md index 1c3b0cae3f..a49081f17c 100644 --- a/docs/_posts/2019-07-17-v3.5.1.md +++ b/docs/_posts/2019-07-17-v3.5.1.md @@ -8,14 +8,14 @@ type: minor * [GHSA-jp9v-w6vw-9m5v](https://github.com/DependencyTrack/dependency-track/security/advisories/GHSA-jp9v-w6vw-9m5v) Cross-Site Scripting (XSS): Persistent -###### dependency-track-embedded.war +###### [dependency-track-embedded.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.5.1/dependency-track-embedded.war) | Algorithm | Checksum | | SHA-1 | aafdfa3142dc478b95f1d6ffc268b2a1832ccb29 | | SHA-256 | 73bbe06a22f84ce7b099da3c552e267c980f0f8c58ca6cccdd3eaa210bfe9b6c | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.5.1/dependency-track.war) | Algorithm | Checksum | | SHA-1 | cf71dbf7ae697038d6a42485f14991f343ffdeff | -| SHA-256 | 271705e72e94e9f9fb36159ea110a05ff465c4d1f2572a89570774e57c08a247 | +| SHA-256 | 271705e72e94e9f9fb36159ea110a05ff465c4d1f2572a89570774e57c08a247 | \ No newline at end of file diff --git a/docs/_posts/2019-09-28-v3.6.0.md b/docs/_posts/2019-09-28-v3.6.0.md index f9fd96508a..6df477a38c 100644 --- a/docs/_posts/2019-09-28-v3.6.0.md +++ b/docs/_posts/2019-09-28-v3.6.0.md @@ -36,14 +36,14 @@ type: major * Replaced embedded Dependency-Check library with internal CPE analyzer * Dependency-Track no longer mirrors XML data feeds from the NVD -###### dependency-track-embedded.war +###### [dependency-track-embedded.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.6.0/dependency-track-embedded.war) | Algorithm | Checksum | | SHA-1 | 6cd17d5a31472f7f60e674e2d7fc2e3050085808 | | SHA-256 | bbb72fa3b6246b7afa7c22b103f0c85daf82565a38ae12973043775e6b27fd6e | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.6.0/dependency-track.war) | Algorithm | Checksum | | SHA-1 | f7b88825dbaf8b837977954f5a7e506952ed8361 | -| SHA-256 | a1d0d308a46d30399e9ff9a0334fe3be70345aa12c30c0d1d6bfccdcafe062e2 | +| SHA-256 | a1d0d308a46d30399e9ff9a0334fe3be70345aa12c30c0d1d6bfccdcafe062e2 | \ No newline at end of file diff --git a/docs/_posts/2019-10-01-v3.6.1.md b/docs/_posts/2019-10-01-v3.6.1.md index e54c011112..9c0141dff2 100644 --- a/docs/_posts/2019-10-01-v3.6.1.md +++ b/docs/_posts/2019-10-01-v3.6.1.md @@ -7,14 +7,14 @@ type: minor * Fixed issue that prevented upgrades to 3.6.0 when using Microsoft SQL Server -###### dependency-track-embedded.war +###### [dependency-track-embedded.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.6.1/dependency-track-embedded.war) | Algorithm | Checksum | | SHA-1 | f18f248d2601878b3d437e3c6539311dc4a31c47 | | SHA-256 | b24cc49e8483c4841d6bc3efa9c1f944836a9524028960ee463ae4db7dac7c02 | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.6.1/dependency-track.war) | Algorithm | Checksum | | SHA-1 | b758993e26f812494ca0191e7ad39037f2cd79ea | -| SHA-256 | da128b3602ea4e0214558074abd3df30201e7d858b79a7abb5065d358db19b40 | +| SHA-256 | da128b3602ea4e0214558074abd3df30201e7d858b79a7abb5065d358db19b40 | \ No newline at end of file diff --git a/docs/_posts/2019-12-16-v3.7.0.md b/docs/_posts/2019-12-16-v3.7.0.md index a66cc7fec3..83d9c741db 100644 --- a/docs/_posts/2019-12-16-v3.7.0.md +++ b/docs/_posts/2019-12-16-v3.7.0.md @@ -35,14 +35,14 @@ type: major * Columns: * LAST_SCAN_IMPORTED (in PROJECT table) -###### dependency-track-embedded.war +###### [dependency-track-embedded.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.7.0/dependency-track-embedded.war) | Algorithm | Checksum | | SHA-1 | e946c65ec0ff5ba12e843789b917caab635bfe62 | | SHA-256 | bd02a522a8c9beeb8dd7964f07eb27a7a02ce8bbf6a7c8af3378bb26fc98a087 | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.7.0/dependency-track.war) | Algorithm | Checksum | | SHA-1 | 22da81fb91b5641fcb805c74063c11e521fe0ad4 | -| SHA-256 | 9207e25b19d34b57804f25e9881e663ebb56333520b039c5ccfd93209295b0a1 | +| SHA-256 | 9207e25b19d34b57804f25e9881e663ebb56333520b039c5ccfd93209295b0a1 | \ No newline at end of file diff --git a/docs/_posts/2020-01-07-v3.7.1.md b/docs/_posts/2020-01-07-v3.7.1.md index f62359efa6..72801aa721 100644 --- a/docs/_posts/2020-01-07-v3.7.1.md +++ b/docs/_posts/2020-01-07-v3.7.1.md @@ -11,14 +11,14 @@ type: minor * Fixed portfolio metrics issue that allowed multiple operations to be executed simultaneously leading to performance degradation -###### dependency-track-embedded.war +###### [dependency-track-embedded.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.7.1/dependency-track-embedded.war) | Algorithm | Checksum | | SHA-1 | 5cd02dc5c6ca8aba3cea1ad5ad03d039ecdd757c | | SHA-256 | f80f527d96692a45f3bba86849551debf4b407bd880f104b890912975cc865ca | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.7.1/dependency-track.war) | Algorithm | Checksum | | SHA-1 | 766d5394ce7a5a0e08c96a55930adc3377897d99 | -| SHA-256 | 4e6233013af574585d93dd99586455a810ea434c3bc5da95e53aad45751f5bc2 | +| SHA-256 | 4e6233013af574585d93dd99586455a810ea434c3bc5da95e53aad45751f5bc2 | \ No newline at end of file diff --git a/docs/_posts/2020-03-22-v3.8.0.md b/docs/_posts/2020-03-22-v3.8.0.md index 408f82b574..23f0372230 100644 --- a/docs/_posts/2020-03-22-v3.8.0.md +++ b/docs/_posts/2020-03-22-v3.8.0.md @@ -30,13 +30,13 @@ type: major * The `nist` and `index` directories inside the Dependency-Track data directory will be deleted upon upgrade. This will force the NVD to be downloaded and reprocessed and the indexes to be rebuilt. * The internal vulnerable software dictionary, generated automatically from the NVD, will be wiped upon upgrade. This will take several minutes to complete and should not be interrupted. -###### dependency-track-embedded.war +###### [dependency-track-embedded.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.8.0/dependency-track-embedded.war) | Algorithm | Checksum | | SHA-1 | 091627dfa144a1313bf9090d8f67b4760e635b23 | | SHA-256 | 56674c40da9dc4277b6c8238d0dc6cc28bdf3b4cc51b7b845606b1a2c149070b | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/3.8.0/dependency-track.war) | Algorithm | Checksum | | SHA-1 | 1db04afbc1b66421dd6fe0db816ec14362b895d1 | @@ -44,4 +44,4 @@ type: major ###### Software Bill-of-Materials (SBOM) ###### -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/3.8.0/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/3.8.0/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2021-01-03-v4.0.0.md b/docs/_posts/2021-01-03-v4.0.0.md index 12304ea73c..b76bcfe38a 100644 --- a/docs/_posts/2021-01-03-v4.0.0.md +++ b/docs/_posts/2021-01-03-v4.0.0.md @@ -43,19 +43,19 @@ type: major * The MySQL Connector distributed with the Docker image has been updated to version 8.0.22. When using MySQL, `ALPINE_DATABASE_DRIVER_PATH` has to be set to `/extlib/mysql-connector-java-8.0.22.jar`. Note that `ALPINE_DATABASE_DRIVER` may need to be updated as well. Refer to the [official upgrading instructions](https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-upgrading-to-8.0.html). * The Postgres driver distributed with the Docker image has been updated to version 42.2.18. When using Postgres, `ALPINE_DATABASE_DRIVER_PATH` has to be set to `/extlib/postgresql-42.2.18.jar`. -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.0.0/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | 9124352542544c5662d3ebf34d951e61f08ff231 | | SHA-256 | 6b6b8d608b467da087fb7ebe12fb6bbb2a418d97168baa186b1320fdb3b49a91 | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.0.0/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | 9a4f516e5fcd6eae117465732e3dcaa69227d238 | | SHA-256 | 2e66976b5f890186e64255484f262564e23e8a3ce482769374959c7ddc55c42c | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.0.0/dependency-track.war) | Algorithm | Checksum | | SHA-1 | a489586be032890ec6cddc5ec839da57026837a7 | @@ -64,4 +64,4 @@ type: major ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.0.0/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.0.0/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.0.0/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2021-01-12-v4.0.1.md b/docs/_posts/2021-01-12-v4.0.1.md index 7507287615..24c6c87c30 100644 --- a/docs/_posts/2021-01-12-v4.0.1.md +++ b/docs/_posts/2021-01-12-v4.0.1.md @@ -7,19 +7,19 @@ type: minor * Fixes issue that resulted in policy violations being returned for all projects rather than the project for which the query is made for. -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.0.1/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | 5fb224978c700f5c38d49527669da262a324a9be | | SHA-256 | d46594ec65c0a30b645eb13419bdc36df41cc6d71053b8bb9efdee80d4de7b99 | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.0.1/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | d9275f0b660b54205ec811c0d0cab9f584ba2a91 | | SHA-256 | 89e155529036c5f8eb977f0c611eac2abc9496c55d2c49dd4dec14dbc5acb431 | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.0.1/dependency-track.war) | Algorithm | Checksum | | SHA-1 | 59b571d0b1ee97a12342938d0d3b17b287c86ad4 | @@ -28,4 +28,4 @@ type: minor ###### Software Bill of Materials (SBOM) ###### * [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.0.1/bom.json) -* [bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.0.1/bom.xml) +* [bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.0.1/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2021-02-09-v4.1.0.md b/docs/_posts/2021-02-09-v4.1.0.md index bd5a99475b..26669619df 100644 --- a/docs/_posts/2021-02-09-v4.1.0.md +++ b/docs/_posts/2021-02-09-v4.1.0.md @@ -26,19 +26,19 @@ type: major `application/vnd.cyclonedx+json` -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.1.0/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | ed951e6a1db32b5541b646f7595cce28345c816d | | SHA-256 | e459525d279abef75f0d6cef756636503b1040939778df14decaaca65d284db1 | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.1.0/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | 669955757d9f5fe1e145ac61e761358986697b3d | | SHA-256 | a33f70500087fc6cfa9ffdeba1ac20de474ba28c1572f85337f04765e961f66c | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.1.0/dependency-track.war) | Algorithm | Checksum | | SHA-1 | a2ab12792eebcf420e6f0b07baa4a49bce5e0082 | @@ -47,4 +47,4 @@ type: major ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.1.0/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.1.0/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.1.0/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2021-03-17-v4.2.0.md b/docs/_posts/2021-03-17-v4.2.0.md index def659b2d3..dc6eb077de 100644 --- a/docs/_posts/2021-03-17-v4.2.0.md +++ b/docs/_posts/2021-03-17-v4.2.0.md @@ -23,19 +23,19 @@ type: major * The internal port the frontend container listens on has changed from port 80 to port 8080. docker-compose files may need to be updated to reflect this change. Updated compose files are [available for download](https://dependencytrack.org/docker-compose.yml). * Starting with Dependency-Track v4.2, the API Server and the Frontend now have the same major and minor (semantic) version. Patch versions however, may continue to be unique. -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.0/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | f1776e778405b5f6be2903d317463a74153c5319 | | SHA-256 | a47a3073def269e810d53de781cd7c22620e94ca80df3f781d528a7a5fe4c779 | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.0/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | c3c2f931cc4f835eddd0013a885e13c16f990ea9 | | SHA-256 | 7d61818c281c6540ff4273d4d4c5d9d6e63b86b55f13e92fca7ba2921613800c | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.0/dependency-track.war) | Algorithm | Checksum | | SHA-1 | 1634d6cf94761d3b0839f4b4a4d9fdd53d314ba6 | @@ -44,4 +44,4 @@ type: major ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.0/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.0/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.0/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2021-03-20-v4.2.1.md b/docs/_posts/2021-03-20-v4.2.1.md index 60d522afb2..861119084f 100644 --- a/docs/_posts/2021-03-20-v4.2.1.md +++ b/docs/_posts/2021-03-20-v4.2.1.md @@ -13,19 +13,19 @@ type: patch **Upgrade Notes:** -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.1/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | 92a0e935c7d4309e67fc7eb149191d96a1635c8b | | SHA-256 | 80cc253d05ccb91aa432667bf7d418bc8327f82b1dfe770aec71c434d0ecd308 | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.1/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | 930d89d1a37e85130a6603969f30253fe842a6e0 | | SHA-256 | 2b27c6f1918a897f22b48542010611c67fa137f399521a45c900ee59120b81c5 | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.1/dependency-track.war) | Algorithm | Checksum | | SHA-1 | 7a3061da05f67fd4f98b149eeb6d588389d1b202 | @@ -34,4 +34,4 @@ type: patch ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.1/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.1/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.1/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2021-05-07-v4.2.2.md b/docs/_posts/2021-05-07-v4.2.2.md index 1786bbddcc..b583caa99d 100644 --- a/docs/_posts/2021-05-07-v4.2.2.md +++ b/docs/_posts/2021-05-07-v4.2.2.md @@ -13,19 +13,19 @@ type: patch **Upgrade Notes:** -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.2/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | 60a87ecafd9ba4b0ba119a65e1a041b0c5f576ea | | SHA-256 | bd20dbee794fa0c37c345526204058dbfbdd734acaf257783f9cb47e2cf17c63 | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.2/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | 748b3fbf89efb61d29a468e3cd1c90bfcaeb3c4e | | SHA-256 | 93948be57b0e7864b872a2869c840c50bf9f2b3d1e9cc75794abea4c53038851 | -###### dependency-track.war +###### [dependency-track.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.2/dependency-track.war) | Algorithm | Checksum | | SHA-1 | 35b61e4309303a7ad605c21cfa5eddcbabcfa15f | @@ -34,4 +34,4 @@ type: patch ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.2/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.2/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.2.2/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2021-08-02-v4.3.0.md b/docs/_posts/2021-08-02-v4.3.0.md index 182393a7a5..e6f0b7c67e 100644 --- a/docs/_posts/2021-08-02-v4.3.0.md +++ b/docs/_posts/2021-08-02-v4.3.0.md @@ -47,13 +47,13 @@ The user interface clearly states that Portfolio Access Control is beta. By defa * Removed legacy support for the traditional WAR (was previously deprecated and unsupported) - [#1070](https://github.com/DependencyTrack/dependency-track/issues/1070) -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.0/dependency-track-apiserver.jar) | Algorithm | Checksum | | SHA-1 | 1c19a467705631c3c4449fa3f95c9d4a73d26caa | | SHA-256 | 34e0cc69eb6934d9e25573d29870cefce75d07d97fb06d58e8830f566256e1dc | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.0/dependency-track-bundled.jar) | Algorithm | Checksum | | SHA-1 | 3e3a9edb9a9077fc5e2b2634f5967d1a61b0e1cb | @@ -62,4 +62,4 @@ The user interface clearly states that Portfolio Access Control is beta. By defa ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.0/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.0/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.0/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2021-08-03-v4.3.1.md b/docs/_posts/2021-08-03-v4.3.1.md index 3b5bb179f2..f6e048848e 100644 --- a/docs/_posts/2021-08-03-v4.3.1.md +++ b/docs/_posts/2021-08-03-v4.3.1.md @@ -13,13 +13,13 @@ type: patch **Upgrade Notes:** -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.1/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | 6c188379b93f2b4052bb73649608db69175b0efc | | SHA-256 | 6008b32cc3cf6b13d0e7efaff335290102580bd6b518f50d630b99280a9b5538 | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.1/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | 9ff235da5d4b6fb9e9fe4b6762c5dfa8d83073e9 | @@ -28,4 +28,4 @@ type: patch ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.1/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.1/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.1/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2021-08-07-v4.3.2.md b/docs/_posts/2021-08-07-v4.3.2.md index bcf3efdc43..f3b98c616b 100644 --- a/docs/_posts/2021-08-07-v4.3.2.md +++ b/docs/_posts/2021-08-07-v4.3.2.md @@ -13,13 +13,13 @@ type: patch **Upgrade Notes:** -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.2/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | 9746e03d0bd7dc02ca1d94aa29a6445144fb7589 | | SHA-256 | 283282536ec276bf048428fc02aee119ff9e42f995c67cf169e2bd2a7a92cd31 | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.2/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | 1cb384c6f5fc457cddbb93c55b7188cf5b446f6f | @@ -28,4 +28,4 @@ type: patch ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.2/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.2/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.2/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2021-08-20-v4.3.3.md b/docs/_posts/2021-08-20-v4.3.3.md index 274311d968..8d858a3db3 100644 --- a/docs/_posts/2021-08-20-v4.3.3.md +++ b/docs/_posts/2021-08-20-v4.3.3.md @@ -15,13 +15,13 @@ type: patch **Upgrade Notes:** -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.3/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | e28bc741856904115e54dd5bf2ef09addde011e8 | | SHA-256 | b748e9b43a25068dc5096f5a68d2e21d5450fca1d3805350042a566c4506d2ba | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.3/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | e884e3e32e18ff608837cc2d33b1d1760a00d0c7 | @@ -30,4 +30,4 @@ type: patch ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.3/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.3/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.3/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2021-08-31-v4.3.4.md b/docs/_posts/2021-08-31-v4.3.4.md index db377623ba..9fb5b33314 100644 --- a/docs/_posts/2021-08-31-v4.3.4.md +++ b/docs/_posts/2021-08-31-v4.3.4.md @@ -13,13 +13,13 @@ type: patch **Upgrade Notes:** -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.4/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | 813e3a7207e47a7ee6769a1e74b040942f8995b5 | | SHA-256 | 1f8bae644dc6982933ec080167d90a66d8090055d75aad7e924a91a9cb8783c8 | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.4/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | 11db7cb3cf83b4e0d6ac121061b42d3f7e3c2c4e | @@ -28,4 +28,4 @@ type: patch ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.4/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.4/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.4/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2021-09-20-v4.3.5.md b/docs/_posts/2021-09-20-v4.3.5.md index d27350d282..448a67f3d1 100644 --- a/docs/_posts/2021-09-20-v4.3.5.md +++ b/docs/_posts/2021-09-20-v4.3.5.md @@ -6,13 +6,13 @@ type: patch No changes in this release. -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.5/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | d13ea84585009e70da2745690f4580b8db2a6e75 | | SHA-256 | 5334a13a5cc0662986d1643463c22bd6a7f3875165ad89296e2f9704b51acec5 | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.5/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | 2aee316ac07c5941a7ba734c30bec4f517cc2df1 | @@ -21,4 +21,4 @@ No changes in this release. ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.5/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.5/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.5/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2021-09-20-v4.3.6.md b/docs/_posts/2021-09-20-v4.3.6.md index 2767cea7c7..62b1cb0143 100644 --- a/docs/_posts/2021-09-20-v4.3.6.md +++ b/docs/_posts/2021-09-20-v4.3.6.md @@ -14,13 +14,13 @@ type: patch **Upgrade Notes:** -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.6/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | d41721f52bfb17c9ba507a1ac01532071643d8ac | | SHA-256 | 83f0bc7199677e3f6f84a76673b936ca73a6b8f54d5cb7cf181f77d548d47a6b | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.6/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | 31fb39d8fecb6ec1e5c02d0fdede7a3e7e1cd952 | @@ -29,4 +29,4 @@ type: patch ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.6/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.6/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.3.6/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2022-02-17-v4.4.0.md b/docs/_posts/2022-02-17-v4.4.0.md index 9789d36ad4..2246b47dd8 100644 --- a/docs/_posts/2022-02-17-v4.4.0.md +++ b/docs/_posts/2022-02-17-v4.4.0.md @@ -43,13 +43,13 @@ The permission also grants access to the findings API. **Upgrade Notes:** * Users and teams with `VULNERABILITY_ANALYSIS` permission are automatically granted the `VIEW_VULNERABILITY` permission during the automatic upgrade. -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.0/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | c81d753ce4376cee1ae4d2a8cf9710a9b8ceee45 | | SHA-256 | 31e685e79b658f661ce28f8c5cbc96906d23d408a2ade70ff7e7a8e20f054972 | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.0/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | 2b15b51c64938997ec9fbcf66054436064d9ef23 | @@ -58,4 +58,4 @@ The permission also grants access to the findings API. ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.0/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.0/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.0/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2022-02-18-v4.4.1.md b/docs/_posts/2022-02-18-v4.4.1.md index dd46c352da..99a4a8ad39 100644 --- a/docs/_posts/2022-02-18-v4.4.1.md +++ b/docs/_posts/2022-02-18-v4.4.1.md @@ -14,13 +14,13 @@ type: patch * For MSSQL users only: If an upgrade to v4.4.0 was previously attempted and no rollback was performed yet, the following SQL statement must be executed before launching v4.4.1: `DELETE FROM "PERMISSION" WHERE "NAME" = 'VIEW_VULNERABILITY'` -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.1/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | 9d6f20709009193540c4c152f0c0757d3b26bd5e | | SHA-256 | c3eaeee440bfd1a734fb009983c97792407b107d64d4e9035a179b9b27c8ca49 | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.1/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | ebadb4576ea419eb42807f5ef2bedb572de02df0 | @@ -29,4 +29,4 @@ type: patch ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.1/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.1/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.1/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2022-03-04-v4.4.2.md b/docs/_posts/2022-03-04-v4.4.2.md index aecc90fdcd..e5c8ab63bf 100644 --- a/docs/_posts/2022-03-04-v4.4.2.md +++ b/docs/_posts/2022-03-04-v4.4.2.md @@ -13,13 +13,13 @@ type: patch **Upgrade Notes:** -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.2/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | 172f569eb85f1182500571a160b134e8b1005ebf | | SHA-256 | 5869df68cd29d48366d653a697bc198e0f3396c2897cd4a668743fc7157fb8df | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.2/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | 49e73a820426a39ab83e6ec2a12f1c24e198a144 | @@ -28,4 +28,4 @@ type: patch ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.2/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.2/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.4.2/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2022-05-18-v4.5.0.md b/docs/_posts/2022-05-18-v4.5.0.md index 013490f509..2801689f40 100644 --- a/docs/_posts/2022-05-18-v4.5.0.md +++ b/docs/_posts/2022-05-18-v4.5.0.md @@ -42,13 +42,13 @@ type: major * Users and teams with `POLICY_VIOLATION_ANALYSIS` permission are automatically granted the `VIEW_POLICY_VIOLATION` permission during the automatic upgrade. * Location of `config.json` in the frontend container changed from `/app/static/config.json` to `/opt/owasp/dependency-track-frontend/static/config.json` -###### dependency-track-apiserver.war +###### [dependency-track-apiserver.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.5.0/dependency-track-apiserver.war) | Algorithm | Checksum | | SHA-1 | 8db4707e3458b122e73cce92e7dc143c115db962 | | SHA-256 | 0c3d75501a0545f90e862aa0e2920f0c6146abcd436983531de7757ff294f568 | -###### dependency-track-bundled.war +###### [dependency-track-bundled.war](https://github.com/DependencyTrack/dependency-track/releases/download/4.5.0/dependency-track-bundled.war) | Algorithm | Checksum | | SHA-1 | 984aafe85ac2dc361f9b0adf3c26d99decbab641 | @@ -57,4 +57,4 @@ type: major ###### Software Bill of Materials (SBOM) ###### [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.5.0/bom.json) -[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.5.0/bom.xml) +[bom.xml](https://github.com/DependencyTrack/dependency-track/releases/download/4.5.0/bom.xml) \ No newline at end of file diff --git a/docs/_posts/2022-10-11-v4.6.0.md b/docs/_posts/2022-10-11-v4.6.0.md index 9d0189335d..24a5e820c9 100644 --- a/docs/_posts/2022-10-11-v4.6.0.md +++ b/docs/_posts/2022-10-11-v4.6.0.md @@ -119,21 +119,21 @@ Special thanks to everyone who contributed code to implement enhancements and fi *@AbdelHajou, @awegg, @dGuerr, @k3rnelpan1c-dev, @maaheeb, @officerNordberg, @rbt-mm, @rkg-mm, @s-spindler, @sahibamittal, @stephan-strate, @syalioune, @tmehnert, @yangsec888* -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.0/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | e40fb14764fb5eb9fcd654472434c3701c44f208 | | SHA-256 | 29d422816b593ddef89b07e9bc1c72a5cfb141eaea4a1d59615309089bab03ea | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.0/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 9e1b283c442e1bfb2c5c4ea23b1a1590cf7afc5d | | SHA-256 | 1e6ba17e6dc1f6422826a020ece5ec6ae2bef1aa9ae563f57653ed6bc0944f14 | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.0/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2022-10-13-v4.6.1.md b/docs/_posts/2022-10-13-v4.6.1.md index 4748636dd5..d37967137e 100644 --- a/docs/_posts/2022-10-13-v4.6.1.md +++ b/docs/_posts/2022-10-13-v4.6.1.md @@ -11,14 +11,14 @@ For a complete list of changes, refer to the respective GitHub milestones: * [API server milestone 4.6.1](https://github.com/DependencyTrack/dependency-track/milestone/28?closed=1) -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.1/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | f3c8e2007f2795b12f438b6b9318c4d5c448fa0b | | SHA-256 | e293756b5e27d6c3213dfbeead946bf220d278d418c817c74a81fda395764977 | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.1/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| @@ -29,4 +29,4 @@ For a complete list of changes, refer to the respective GitHub milestones: * API Server: [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.1/bom.json) -[#2043]: https://github.com/DependencyTrack/dependency-track/issues/2043 +[#2043]: https://github.com/DependencyTrack/dependency-track/issues/2043 \ No newline at end of file diff --git a/docs/_posts/2022-10-24-v4.6.2.md b/docs/_posts/2022-10-24-v4.6.2.md index e5188fdd2c..d25738ac7c 100644 --- a/docs/_posts/2022-10-24-v4.6.2.md +++ b/docs/_posts/2022-10-24-v4.6.2.md @@ -20,21 +20,21 @@ For a complete list of changes, refer to the respective GitHub milestones: * [API server milestone 4.6.2](https://github.com/DependencyTrack/dependency-track/milestone/28?closed=1) * [Frontend milestone 4.6.1](https://github.com/DependencyTrack/frontend/milestone/12?closed=1) -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.2/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 313b2ee9bd957f8bd2b0baba524044197501b2a9 | | SHA-256 | 7ee92f572cebe6d8d8f9e37ab6067e5849c83c56c98b38a21418557260efbfdc | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.2/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | e009cc9345ae5bdb321c651df769a6d02dfc5a67 | | SHA-256 | 0e67de28a99aec1d2e3c4592b42f04e86084129f58f3d338b572fdc5b7064899 | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.2/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| @@ -47,4 +47,4 @@ For a complete list of changes, refer to the respective GitHub milestones: * Frontend: [bom.json](https://github.com/DependencyTrack/frontend/releases/download/4.6.1/bom.json) [#300]: https://github.com/DependencyTrack/frontend/pull/300 -[GHSA-c33w-pm52-mqvf]: https://github.com/DependencyTrack/frontend/security/advisories/GHSA-c33w-pm52-mqvf +[GHSA-c33w-pm52-mqvf]: https://github.com/DependencyTrack/frontend/security/advisories/GHSA-c33w-pm52-mqvf \ No newline at end of file diff --git a/docs/_posts/2022-11-18-v4.6.3.md b/docs/_posts/2022-11-18-v4.6.3.md index 162ff6afdf..f2dcc81230 100644 --- a/docs/_posts/2022-11-18-v4.6.3.md +++ b/docs/_posts/2022-11-18-v4.6.3.md @@ -19,14 +19,14 @@ For a complete list of changes, refer to the respective GitHub milestones: * [API server milestone 4.6.3](https://github.com/DependencyTrack/dependency-track/milestone/30?closed=1) -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.3/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 68b806410c2e68fe8c586b93044f29a648f96466 | | SHA-256 | d9b5337419addee26658da8e421f0286aaa92160b8f6f85caca83aa1a328611f | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.3/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| @@ -38,4 +38,4 @@ For a complete list of changes, refer to the respective GitHub milestones: * API Server: [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.3/bom.json) [#2115]: https://github.com/DependencyTrack/dependency-track/issues/2115 -[component analysis cache]: {{ site.baseurl }}{% link _docs/analysis-types/known-vulnerabilities.md %}#analysis-result-cache +[component analysis cache]: {{ site.baseurl }}{% link _docs/analysis-types/known-vulnerabilities.md %}#analysis-result-cache \ No newline at end of file diff --git a/docs/_posts/2022-12-16-v4.7.0.md b/docs/_posts/2022-12-16-v4.7.0.md index 5a89488b19..619dd893a2 100644 --- a/docs/_posts/2022-12-16-v4.7.0.md +++ b/docs/_posts/2022-12-16-v4.7.0.md @@ -98,21 +98,21 @@ Special thanks to everyone who contributed code to implement enhancements and fi [@mehab], [@nathan-mittelette], [@omerlh], [@rbt-mm], [@ribbybibby], [@s-spindler], [@sahibamittal], [@syalioune], [@valentijnscholten] -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.7.0/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 99f1a012a983b8256d9346e64d3dd27e92d1c808 | | SHA-256 | 373e8efa1a8995193b7c068ea34974040627553647905d38e1dce053333eeb10 | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.7.0/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | c7faee42162e1712377fbd8a03dfd9e3ef251a23 | | SHA-256 | 631807c24fd76c0f44d4494a44147e0414ab471ac1e12fe4ebff054f363a8f0f | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.7.0/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| @@ -198,4 +198,4 @@ Special thanks to everyone who contributed code to implement enhancements and fi [@s-spindler]: https://github.com/s-spindler [@sahibamittal]: https://github.com/sahibamittal [@syalioune]: https://github.com/syalioune -[@valentijnscholten]: https://github.com/valentijnscholten +[@valentijnscholten]: https://github.com/valentijnscholten \ No newline at end of file diff --git a/docs/_posts/2023-01-31-v4.7.1.md b/docs/_posts/2023-01-31-v4.7.1.md index 4809736271..ca00632e35 100644 --- a/docs/_posts/2023-01-31-v4.7.1.md +++ b/docs/_posts/2023-01-31-v4.7.1.md @@ -23,21 +23,21 @@ Special thanks to everyone who contributed code to fix defects: [@JoergBruenner], [@mehab], [@rbt-mm], [@sergioasantiago], [@syalioune] -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.7.1/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | ef119b6f5fb422687e5152528bdb3e40e89c8733 | | SHA-256 | 7fbccad45c730226ab9df1ff51aaa2dba90b93cf22547bbe395d3f3b849c8371 | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.7.1/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 94ca9179dad020c45adfdf0152b3f20081f7cf8b | | SHA-256 | fe3fad9d43235df30880e547f838f65fe6365919dbc19107e4da349a5dce104f | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.7.1/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2023-04-18-v4.8.0.md b/docs/_posts/2023-04-18-v4.8.0.md index 1e8eef644d..1b7e6b13fe 100644 --- a/docs/_posts/2023-04-18-v4.8.0.md +++ b/docs/_posts/2023-04-18-v4.8.0.md @@ -119,21 +119,21 @@ Special thanks to everyone who contributed code to implement enhancements and fi [@mehab], [@msymons], [@mvandermade], [@rbt-mm], [@roadSurfer], [@s-spindler], [@sahibamittal], [@syalioune] [@valentijnscholten], [@walterdeboer], [@zgael] -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.8.0/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 883754d3ed227a124976c3f9247345be48cc0561 | | SHA-256 | 0ab7e3a1d0cd308a9193a6bec7b561f3911d19052312a82e4a59607d4ff50fd0 | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.8.0/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 979f02a5bf3ea5d8b0bba7d4e73a725de1920219 | | SHA-256 | af9f6d79e7828b4f744f9f82215486c0b5649abf6544d0374c945b2ab5d8b58a | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.8.0/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| @@ -237,4 +237,4 @@ Special thanks to everyone who contributed code to implement enhancements and fi [@syalioune]: https://github.com/syalioune [@valentijnscholten]: https://github.com/valentijnscholten [@walterdeboer]: https://github.com/walterdeboer -[@zgael]: https://github.com/zgael +[@zgael]: https://github.com/zgael \ No newline at end of file diff --git a/docs/_posts/2023-05-16-v4.8.1.md b/docs/_posts/2023-05-16-v4.8.1.md index 5cbf28aed9..10647951be 100644 --- a/docs/_posts/2023-05-16-v4.8.1.md +++ b/docs/_posts/2023-05-16-v4.8.1.md @@ -30,21 +30,21 @@ discussions on GitHub & Slack to testing of fixes. Special thanks to everyone who contributed code to fix defects: [@heubeck], [@jakubrak], [@sahibamittal], [@valentijnscholten] -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.8.1/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 553d17a940220d79b686ce6b64d65c0854915f1b | | SHA-256 | 56db674f5b467eac0a5b3fde99bc6285fd9135ad84e8fa0328ed6ace64fc723c | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.8.1/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | b2f0e053083ac672a9eaef19f7363ac854bdb91a | | SHA-256 | e1bd03ea89b312c2125791a0d46ca99aa62365140a4f175d2f45cbb1d59a87a6 | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.8.1/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2023-05-17-v4.8.2.md b/docs/_posts/2023-05-17-v4.8.2.md index 982f15c69a..a062f2ddd6 100644 --- a/docs/_posts/2023-05-17-v4.8.2.md +++ b/docs/_posts/2023-05-17-v4.8.2.md @@ -15,14 +15,14 @@ For a complete list of changes, refer to the respective GitHub milestone: * [API server milestone 4.8.2](https://github.com/DependencyTrack/dependency-track/milestone/33?closed=1) -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.8.2/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | bfc8758eb30ab90f4280cb37ea959964f74706b9 | | SHA-256 | 2b1d249d98f72b863deb4769665efc119a3ef8db195838decddce9a2a12f36b4 | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.8.2/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| @@ -34,4 +34,4 @@ For a complete list of changes, refer to the respective GitHub milestone: * API Server: [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.8.2/bom.json) [apiserver/#2750]: https://github.com/DependencyTrack/dependency-track/pull/2750 -[apiserver/#2766]: https://github.com/DependencyTrack/dependency-track/issues/2766 +[apiserver/#2766]: https://github.com/DependencyTrack/dependency-track/issues/2766 \ No newline at end of file diff --git a/docs/_posts/2023-10-16-v4.9.0.md b/docs/_posts/2023-10-16-v4.9.0.md index 1572f70718..a95e25032b 100644 --- a/docs/_posts/2023-10-16-v4.9.0.md +++ b/docs/_posts/2023-10-16-v4.9.0.md @@ -87,21 +87,21 @@ Special thanks to everyone who contributed code to implement enhancements and fi [@mattmatician], [@melba-lopez], [@muellerst-hg], [@nathan-mittelette], [@sahibamittal], [@sephiroth-j], [@syalioune], [@takumakume], [@valentijnscholten], [@walterdeboer] -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.9.0/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | cd4ec4f1ed075f37476f46da11451158d7460502 | | SHA-256 | 281f091107ef79d9b1e9361dc78608260b364eaa7dbbaeb29d4f7aef1a4bf67b | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.9.0/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 6f3a077219fb49a502a88fcbb40e05865a23f5c5 | | SHA-256 | 4ca0b061ed83fa0b34ede8158f3ec0e2a7380c2736731995cf330f809076951f | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.9.0/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| @@ -190,4 +190,4 @@ Special thanks to everyone who contributed code to implement enhancements and fi [@syalioune]: https://github.com/syalioune [@takumakume]: https://github.com/takumakume [@valentijnscholten]: https://github.com/valentijnscholten -[@walterdeboer]: https://github.com/walterdeboer +[@walterdeboer]: https://github.com/walterdeboer \ No newline at end of file diff --git a/docs/_posts/2023-10-30-v4.9.1.md b/docs/_posts/2023-10-30-v4.9.1.md index 6a857e8b08..b535df73b2 100644 --- a/docs/_posts/2023-10-30-v4.9.1.md +++ b/docs/_posts/2023-10-30-v4.9.1.md @@ -24,21 +24,21 @@ We thank all organizations and individuals who contributed to this release, from Special thanks to everyone who contributed code to implement enhancements and fix defects: [@muellerst-hg] -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.9.1/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 99da5f705c3b0048ecf621e8c738a87147c693d9 | | SHA-256 | 5d925f08f85fe7f39231357c4a4c8057fd354e048b7c9407efb20af78033ecec | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.9.1/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 487801d69bffb2e8def5aad9aa55c34be8cddcb2 | | SHA-256 | 19ac4ede2932ff54c42e0466cdf7d5b410f7a44784562f237fc5b4b8891a8dc8 | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.9.1/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2023-12-08-v4.10.0.md b/docs/_posts/2023-12-08-v4.10.0.md index 6d90c8728d..af9418a49e 100644 --- a/docs/_posts/2023-12-08-v4.10.0.md +++ b/docs/_posts/2023-12-08-v4.10.0.md @@ -65,21 +65,21 @@ We thank all organizations and individuals who contributed to this release, from Special thanks to everyone who contributed code to implement enhancements and fix defects: [@AbdelHajou], [@Nikemare], [@acdha], [@dimitri-rebrikov], [@jadyndev], [@leec94], [@mehab], [@melba-lopez], [@rbt-mm], [@rkg-mm], [@willienel], [@ybelMekk] -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.10.0/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | c308b1f6a2d73fc2bba9da2cc33bf7e3ec49e851 | | SHA-256 | d06f4550e16451ccb7843c36534172744934a7dc69e1d48e970a6eec24e49dc3 | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.10.0/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | b94fb9cbaa91c4e332bcec266e10a0f325f12e22 | | SHA-256 | cf27db44e637b4bc551c16e659e81890f4c5d4f3b4ea9893ebf1717bff98b999 | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.10.0/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| @@ -135,4 +135,4 @@ Special thanks to everyone who contributed code to implement enhancements and fi [@rbt-mm]: https://github.com/rbt-mm [@rkg-mm]: https://github.com/rkg-mm [@willienel]: https://github.com/willienel -[@ybelMekk]: https://github.com/ybelMekk +[@ybelMekk]: https://github.com/ybelMekk \ No newline at end of file diff --git a/docs/_posts/2023-12-19-v4.10.1.md b/docs/_posts/2023-12-19-v4.10.1.md index de988b2a5e..4a657081a3 100644 --- a/docs/_posts/2023-12-19-v4.10.1.md +++ b/docs/_posts/2023-12-19-v4.10.1.md @@ -33,14 +33,14 @@ We thank all organizations and individuals who contributed to this release, from Special thanks to everyone who contributed code to implement enhancements and fix defects: [@jadyndev] -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.10.1/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 1d728ce1788e5db8b3a9308338a9e7e8ab5af12e | | SHA-256 | e30731cd1915d3a1578cf5d8c8596d247fb11a82a3fe4c1ba2fb9fad01667aef | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.10.1/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| @@ -57,4 +57,4 @@ Special thanks to everyone who contributed code to implement enhancements and fi [apiserver/#3315]: https://github.com/DependencyTrack/dependency-track/pull/3315 [apiserver/#3323]: https://github.com/DependencyTrack/dependency-track/pull/3323 -[@jadyndev]: https://github.com/jadyndev +[@jadyndev]: https://github.com/jadyndev \ No newline at end of file diff --git a/docs/_posts/2024-05-07-v4.11.0.md b/docs/_posts/2024-05-07-v4.11.0.md index a519fb9ea5..5e2a2459ef 100644 --- a/docs/_posts/2024-05-07-v4.11.0.md +++ b/docs/_posts/2024-05-07-v4.11.0.md @@ -181,21 +181,21 @@ Special thanks to everyone who contributed code to implement enhancements and fi [@baburkin], [@fnxpt], [@kepten], [@leec94], [@lukas-braune], [@malice00], [@mehab], [@mge-mm] [@mikkeschiren], [@mykter], [@rbt-mm], [@rkesters], [@rkg-mm], [@sahibamittal], [@sebD], [@setchy], [@validide] -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.0/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | a9dae58a25c8aeeb54134ff054214505eb170db9 | | SHA-256 | 03160957fced99c3d923bbb5c6cb352740da1970bd4775b52bb451b95c4cefaf | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.0/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 59b78c3f6b1979ba29c1bd754b7dc1005101fc49 | | SHA-256 | 1a34808cd6c7a9bf7b181e4f175c077f1ee5d5a9daf327b330db9b1c63aac2d3 | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.0/frontend-dist.zip) | Algorithm | Checksum | |:----------|:------------------------------------------------------------------------------------| diff --git a/docs/_posts/2024-05-19-v4.11.1.md b/docs/_posts/2024-05-19-v4.11.1.md index 6b09142801..dee121e16d 100644 --- a/docs/_posts/2024-05-19-v4.11.1.md +++ b/docs/_posts/2024-05-19-v4.11.1.md @@ -21,21 +21,21 @@ We thank all organizations and individuals who contributed to this release, from Special thanks to everyone who contributed code to implement enhancements and fix defects: [@aravindparappil46], [@fnxpt], [@tiwatsuka] -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.1/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | aa3d8ffc6b8f9d15a801148a93275ebeba922010 | | SHA-256 | ed08e60e0761ced93454c14194da02be5950805911dbc7f7c611bdf0e753b437 | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.1/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | c57f1b8c003d95daa871096cbc37a6c03cd08907 | | SHA-256 | e7613d6654083ab6e2c4ae24459444efe4d83df5d2c4d27e58a94bc809e2627a | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.1/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| @@ -56,4 +56,4 @@ Special thanks to everyone who contributed code to implement enhancements and fi [@aravindparappil46]: https://github.com/aravindparappil46 [@fnxpt]: https://github.com/fnxpt -[@tiwatsuka]: https://github.com/tiwatsuka +[@tiwatsuka]: https://github.com/tiwatsuka \ No newline at end of file diff --git a/docs/_posts/2024-06-01-v4.11.2.md b/docs/_posts/2024-06-01-v4.11.2.md index cfa6e517b0..8b929dd3c6 100644 --- a/docs/_posts/2024-06-01-v4.11.2.md +++ b/docs/_posts/2024-06-01-v4.11.2.md @@ -24,21 +24,21 @@ We thank all organizations and individuals who contributed to this release, from Special thanks to everyone who contributed code to implement enhancements and fix defects: [@aravindparappil46], [@lgrguricmileusnic], [@molusk], [@sahibamittal] -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.2/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 174956bf3cd2dab16cfd36e7ab1b5d7001b99160 | | SHA-256 | 135cf4361bbbc65f488796bf196c8d2d3cbebec931b249e037551c6fbbae2ed7 | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.2/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | af75c903b033418ea6326cbb4e6885afba99ee94 | | SHA-256 | 5020ac51158038439b7482d5c5fec151773162724dce1779249bf73053456d34 | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.2/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2024-06-03-v4.11.3.md b/docs/_posts/2024-06-03-v4.11.3.md index 7e902a7034..58ccd93601 100644 --- a/docs/_posts/2024-06-03-v4.11.3.md +++ b/docs/_posts/2024-06-03-v4.11.3.md @@ -14,21 +14,21 @@ For a complete list of changes, refer to the respective GitHub milestones: We thank all organizations and individuals who contributed to this release, from logging issues to taking part in discussions on GitHub & Slack to testing of fixes. -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.3/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:---------| | SHA-1 | ff4284ce635f4da916e907af20bb0e9339349ecd | | SHA-256 | f1e34cc7a0c5e2fe444e934aa221853ac762ee79997bc10fa712ee6ac8f776d8 | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.3/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:---------| | SHA-1 | beea18173e6a52180ac1a8ee721dd7f775eaaf2d | | SHA-256 | d62557345bb244b5d34e7a56d057e264044524d8df7964df23383a2ace658cbd | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.3/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| @@ -40,4 +40,4 @@ We thank all organizations and individuals who contributed to this release, from * API Server: [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.3/bom.json) * Frontend: [bom.json](https://github.com/DependencyTrack/frontend/releases/download/4.11.3/bom.json) -[apiserver/#3801]: https://github.com/DependencyTrack/dependency-track/pull/3801 +[apiserver/#3801]: https://github.com/DependencyTrack/dependency-track/pull/3801 \ No newline at end of file diff --git a/docs/_posts/2024-06-24-v4.11.4.md b/docs/_posts/2024-06-24-v4.11.4.md index 49c91d4fe2..f92cf2db87 100644 --- a/docs/_posts/2024-06-24-v4.11.4.md +++ b/docs/_posts/2024-06-24-v4.11.4.md @@ -26,21 +26,21 @@ We thank all organizations and individuals who contributed to this release, from Special thanks to everyone who contributed code to implement enhancements and fix defects: [@2000rosser], [@fupgang], [@sahibamittal], [@zeed-w-beez] -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.4/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 19531d4f02cccf26478b3a63feba355da8726b3f | | SHA-256 | 9a09259ba4c19d02b81a39fb5894df758f19ff1bb43538d4b999b4a5789a9d9b | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.4/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 3c4bb658783157ae9c408b8323e25e55c9ab25fd | | SHA-256 | 73fc867d347da8a8af14f8c6812e13b870037a28d7de83e2837db9c27d840100 | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.4/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| @@ -66,4 +66,4 @@ Special thanks to everyone who contributed code to implement enhancements and fi [@2000rosser]: https://github.com/2000rosser [@fupgang]: https://github.com/fupgang [@sahibamittal]: https://github.com/sahibamittal -[@zeed-w-beez]: https://github.com/zeed-w-beez +[@zeed-w-beez]: https://github.com/zeed-w-beez \ No newline at end of file diff --git a/docs/_posts/2024-07-08-v4.11.5.md b/docs/_posts/2024-07-08-v4.11.5.md index 5d2d8b81be..3a15b0dbf6 100644 --- a/docs/_posts/2024-07-08-v4.11.5.md +++ b/docs/_posts/2024-07-08-v4.11.5.md @@ -30,21 +30,21 @@ We thank all organizations and individuals who contributed to this release, from Special thanks to everyone who contributed code to implement enhancements and fix defects: [@2000rosser] -###### dependency-track-apiserver.jar +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.5/dependency-track-apiserver.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | 8fd45ea6ae725e8e7dac59ec9d471fcdaeb42c6d | | SHA-256 | c39c15849cbb7dd19833ea689c20aaf92bc9f6965b758961e1d2a01a2b09f86f | -###### dependency-track-bundled.jar +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.5/dependency-track-bundled.jar) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| | SHA-1 | eba6cbaa6c2da9ffb295da83ed39af68ff4130a8 | | SHA-256 | 7ebb11573b2a59084ed98fe92d363240c910dc7b5aa7ebeda64bee7d47089d9a | -###### frontend-dist.zip +###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.5/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| @@ -60,4 +60,4 @@ Special thanks to everyone who contributed code to implement enhancements and fi [apiserver/#3941]: https://github.com/DependencyTrack/dependency-track/pull/3941 [apiserver/#3942]: https://github.com/DependencyTrack/dependency-track/pull/3942 -[@2000rosser]: https://github.com/2000rosser +[@2000rosser]: https://github.com/2000rosser \ No newline at end of file From 489f39e3837c9dcdab87fafb073127274abd3cae Mon Sep 17 00:00:00 2001 From: JCHacking Date: Sun, 21 Jul 2024 22:18:56 +0200 Subject: [PATCH 022/429] fix: fix frontend link in changelog of documentation Refs: 2094 Signed-off-by: JCHacking --- docs/_posts/2018-03-27-v3.0.0.md | 1 + docs/_posts/2022-10-11-v4.6.0.md | 2 +- docs/_posts/2022-10-24-v4.6.2.md | 2 +- docs/_posts/2022-12-16-v4.7.0.md | 2 +- docs/_posts/2023-01-31-v4.7.1.md | 2 +- docs/_posts/2023-04-18-v4.8.0.md | 2 +- docs/_posts/2023-05-16-v4.8.1.md | 2 +- docs/_posts/2023-10-16-v4.9.0.md | 2 +- docs/_posts/2023-10-30-v4.9.1.md | 2 +- docs/_posts/2023-12-08-v4.10.0.md | 2 +- docs/_posts/2024-05-07-v4.11.0.md | 2 +- docs/_posts/2024-05-19-v4.11.1.md | 2 +- docs/_posts/2024-06-01-v4.11.2.md | 2 +- docs/_posts/2024-06-03-v4.11.3.md | 2 +- docs/_posts/2024-06-24-v4.11.4.md | 2 +- docs/_posts/2024-07-08-v4.11.5.md | 2 +- 16 files changed, 16 insertions(+), 15 deletions(-) diff --git a/docs/_posts/2018-03-27-v3.0.0.md b/docs/_posts/2018-03-27-v3.0.0.md index 2a12cf0b1d..5dcc26f36f 100644 --- a/docs/_posts/2018-03-27-v3.0.0.md +++ b/docs/_posts/2018-03-27-v3.0.0.md @@ -22,3 +22,4 @@ Project Reboot Successful! This is the first release after being developed from * Simple to install and configure. Get up and running in just a few minutes **Fixes:** + diff --git a/docs/_posts/2022-10-11-v4.6.0.md b/docs/_posts/2022-10-11-v4.6.0.md index 24a5e820c9..babef7def4 100644 --- a/docs/_posts/2022-10-11-v4.6.0.md +++ b/docs/_posts/2022-10-11-v4.6.0.md @@ -133,7 +133,7 @@ Special thanks to everyone who contributed code to implement enhancements and fi | SHA-1 | 9e1b283c442e1bfb2c5c4ea23b1a1590cf7afc5d | | SHA-256 | 1e6ba17e6dc1f6422826a020ece5ec6ae2bef1aa9ae563f57653ed6bc0944f14 | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.0/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.6.0/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2022-10-24-v4.6.2.md b/docs/_posts/2022-10-24-v4.6.2.md index d25738ac7c..41baff61e7 100644 --- a/docs/_posts/2022-10-24-v4.6.2.md +++ b/docs/_posts/2022-10-24-v4.6.2.md @@ -34,7 +34,7 @@ For a complete list of changes, refer to the respective GitHub milestones: | SHA-1 | e009cc9345ae5bdb321c651df769a6d02dfc5a67 | | SHA-256 | 0e67de28a99aec1d2e3c4592b42f04e86084129f58f3d338b572fdc5b7064899 | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.6.2/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.6.2/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2022-12-16-v4.7.0.md b/docs/_posts/2022-12-16-v4.7.0.md index 619dd893a2..d83cd48cfe 100644 --- a/docs/_posts/2022-12-16-v4.7.0.md +++ b/docs/_posts/2022-12-16-v4.7.0.md @@ -112,7 +112,7 @@ Special thanks to everyone who contributed code to implement enhancements and fi | SHA-1 | c7faee42162e1712377fbd8a03dfd9e3ef251a23 | | SHA-256 | 631807c24fd76c0f44d4494a44147e0414ab471ac1e12fe4ebff054f363a8f0f | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.7.0/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.7.0/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2023-01-31-v4.7.1.md b/docs/_posts/2023-01-31-v4.7.1.md index ca00632e35..0a05b8a812 100644 --- a/docs/_posts/2023-01-31-v4.7.1.md +++ b/docs/_posts/2023-01-31-v4.7.1.md @@ -37,7 +37,7 @@ Special thanks to everyone who contributed code to fix defects: | SHA-1 | 94ca9179dad020c45adfdf0152b3f20081f7cf8b | | SHA-256 | fe3fad9d43235df30880e547f838f65fe6365919dbc19107e4da349a5dce104f | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.7.1/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.7.1/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2023-04-18-v4.8.0.md b/docs/_posts/2023-04-18-v4.8.0.md index 1b7e6b13fe..6d70472f62 100644 --- a/docs/_posts/2023-04-18-v4.8.0.md +++ b/docs/_posts/2023-04-18-v4.8.0.md @@ -133,7 +133,7 @@ Special thanks to everyone who contributed code to implement enhancements and fi | SHA-1 | 979f02a5bf3ea5d8b0bba7d4e73a725de1920219 | | SHA-256 | af9f6d79e7828b4f744f9f82215486c0b5649abf6544d0374c945b2ab5d8b58a | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.8.0/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.8.0/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2023-05-16-v4.8.1.md b/docs/_posts/2023-05-16-v4.8.1.md index 10647951be..d21f3fc3c5 100644 --- a/docs/_posts/2023-05-16-v4.8.1.md +++ b/docs/_posts/2023-05-16-v4.8.1.md @@ -44,7 +44,7 @@ Special thanks to everyone who contributed code to fix defects: | SHA-1 | b2f0e053083ac672a9eaef19f7363ac854bdb91a | | SHA-256 | e1bd03ea89b312c2125791a0d46ca99aa62365140a4f175d2f45cbb1d59a87a6 | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.8.1/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.8.1/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2023-10-16-v4.9.0.md b/docs/_posts/2023-10-16-v4.9.0.md index a95e25032b..b7555a710e 100644 --- a/docs/_posts/2023-10-16-v4.9.0.md +++ b/docs/_posts/2023-10-16-v4.9.0.md @@ -101,7 +101,7 @@ Special thanks to everyone who contributed code to implement enhancements and fi | SHA-1 | 6f3a077219fb49a502a88fcbb40e05865a23f5c5 | | SHA-256 | 4ca0b061ed83fa0b34ede8158f3ec0e2a7380c2736731995cf330f809076951f | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.9.0/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.9.0/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2023-10-30-v4.9.1.md b/docs/_posts/2023-10-30-v4.9.1.md index b535df73b2..ae04f34bcf 100644 --- a/docs/_posts/2023-10-30-v4.9.1.md +++ b/docs/_posts/2023-10-30-v4.9.1.md @@ -38,7 +38,7 @@ Special thanks to everyone who contributed code to implement enhancements and fi | SHA-1 | 487801d69bffb2e8def5aad9aa55c34be8cddcb2 | | SHA-256 | 19ac4ede2932ff54c42e0466cdf7d5b410f7a44784562f237fc5b4b8891a8dc8 | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.9.1/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.9.1/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2023-12-08-v4.10.0.md b/docs/_posts/2023-12-08-v4.10.0.md index af9418a49e..377fcf387d 100644 --- a/docs/_posts/2023-12-08-v4.10.0.md +++ b/docs/_posts/2023-12-08-v4.10.0.md @@ -79,7 +79,7 @@ Special thanks to everyone who contributed code to implement enhancements and fi | SHA-1 | b94fb9cbaa91c4e332bcec266e10a0f325f12e22 | | SHA-256 | cf27db44e637b4bc551c16e659e81890f4c5d4f3b4ea9893ebf1717bff98b999 | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.10.0/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.10.0/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2024-05-07-v4.11.0.md b/docs/_posts/2024-05-07-v4.11.0.md index 5e2a2459ef..3576beff09 100644 --- a/docs/_posts/2024-05-07-v4.11.0.md +++ b/docs/_posts/2024-05-07-v4.11.0.md @@ -195,7 +195,7 @@ Special thanks to everyone who contributed code to implement enhancements and fi | SHA-1 | 59b78c3f6b1979ba29c1bd754b7dc1005101fc49 | | SHA-256 | 1a34808cd6c7a9bf7b181e4f175c077f1ee5d5a9daf327b330db9b1c63aac2d3 | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.0/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.11.0/frontend-dist.zip) | Algorithm | Checksum | |:----------|:------------------------------------------------------------------------------------| diff --git a/docs/_posts/2024-05-19-v4.11.1.md b/docs/_posts/2024-05-19-v4.11.1.md index dee121e16d..6e392bbd9a 100644 --- a/docs/_posts/2024-05-19-v4.11.1.md +++ b/docs/_posts/2024-05-19-v4.11.1.md @@ -35,7 +35,7 @@ Special thanks to everyone who contributed code to implement enhancements and fi | SHA-1 | c57f1b8c003d95daa871096cbc37a6c03cd08907 | | SHA-256 | e7613d6654083ab6e2c4ae24459444efe4d83df5d2c4d27e58a94bc809e2627a | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.1/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.11.1/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2024-06-01-v4.11.2.md b/docs/_posts/2024-06-01-v4.11.2.md index 8b929dd3c6..e322ec4729 100644 --- a/docs/_posts/2024-06-01-v4.11.2.md +++ b/docs/_posts/2024-06-01-v4.11.2.md @@ -38,7 +38,7 @@ Special thanks to everyone who contributed code to implement enhancements and fi | SHA-1 | af75c903b033418ea6326cbb4e6885afba99ee94 | | SHA-256 | 5020ac51158038439b7482d5c5fec151773162724dce1779249bf73053456d34 | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.2/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.11.2/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2024-06-03-v4.11.3.md b/docs/_posts/2024-06-03-v4.11.3.md index 58ccd93601..a738904eb9 100644 --- a/docs/_posts/2024-06-03-v4.11.3.md +++ b/docs/_posts/2024-06-03-v4.11.3.md @@ -28,7 +28,7 @@ We thank all organizations and individuals who contributed to this release, from | SHA-1 | beea18173e6a52180ac1a8ee721dd7f775eaaf2d | | SHA-256 | d62557345bb244b5d34e7a56d057e264044524d8df7964df23383a2ace658cbd | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.3/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.11.3/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2024-06-24-v4.11.4.md b/docs/_posts/2024-06-24-v4.11.4.md index f92cf2db87..2f35f72d8e 100644 --- a/docs/_posts/2024-06-24-v4.11.4.md +++ b/docs/_posts/2024-06-24-v4.11.4.md @@ -40,7 +40,7 @@ Special thanks to everyone who contributed code to implement enhancements and fi | SHA-1 | 3c4bb658783157ae9c408b8323e25e55c9ab25fd | | SHA-256 | 73fc867d347da8a8af14f8c6812e13b870037a28d7de83e2837db9c27d840100 | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.4/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.11.4/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| diff --git a/docs/_posts/2024-07-08-v4.11.5.md b/docs/_posts/2024-07-08-v4.11.5.md index 3a15b0dbf6..93f7dcc47e 100644 --- a/docs/_posts/2024-07-08-v4.11.5.md +++ b/docs/_posts/2024-07-08-v4.11.5.md @@ -44,7 +44,7 @@ Special thanks to everyone who contributed code to implement enhancements and fi | SHA-1 | eba6cbaa6c2da9ffb295da83ed39af68ff4130a8 | | SHA-256 | 7ebb11573b2a59084ed98fe92d363240c910dc7b5aa7ebeda64bee7d47089d9a | -###### [frontend-dist.zip](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.5/frontend-dist.zip) +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.11.5/frontend-dist.zip) | Algorithm | Checksum | |:----------|:-----------------------------------------------------------------| From 096365288e1954961fe11914f892f23c5ef16090 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 08:15:20 +0000 Subject: [PATCH 023/429] Bump docker/build-push-action from 6.3.0 to 6.4.1 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.3.0 to 6.4.1. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/1a162644f9a7e87d8f4b053101d1d9a712edc18c...1ca370b3a9802c92e886402e0dd88098a2533b12) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 5b051f12e3..ee61077a4a 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -121,7 +121,7 @@ jobs: echo "tags=${TAGS}" >> $GITHUB_OUTPUT - name: Build multi-arch Container Image - uses: docker/build-push-action@1a162644f9a7e87d8f4b053101d1d9a712edc18c # tag=v6.3.0 + uses: docker/build-push-action@1ca370b3a9802c92e886402e0dd88098a2533b12 # tag=v6.4.1 with: tags: ${{ steps.tags.outputs.tags }} build-args: |- From e65899a417c6ee97d5ed91e916b7985bbecc3238 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 08:15:32 +0000 Subject: [PATCH 024/429] Bump github/codeql-action from 3.25.12 to 3.25.13 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.12 to 3.25.13. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/4fa2a7953630fd2f3fb380f21be14ede0169dd4f...2d790406f505036ef40ecba973cc774a50395aac) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 5b051f12e3..ff90a73357 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -145,6 +145,6 @@ jobs: - name: Upload Trivy Scan Results to GitHub Security Tab if: ${{ inputs.publish-container }} - uses: github/codeql-action/upload-sarif@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # tag=v3.25.12 + uses: github/codeql-action/upload-sarif@2d790406f505036ef40ecba973cc774a50395aac # tag=v3.25.13 with: sarif_file: 'trivy-results.sarif' From 9eda8335294310d16426ca3591538ba1ae7d1586 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 08:50:40 +0000 Subject: [PATCH 025/429] Bump debian from `f8bbfa0` to `57bd74e` in /src/main/docker Bumps debian from `f8bbfa0` to `57bd74e`. --- updated-dependencies: - dependency-name: debian dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile index 9be91c79dc..09644f54b5 100644 --- a/src/main/docker/Dockerfile +++ b/src/main/docker/Dockerfile @@ -1,6 +1,6 @@ FROM eclipse-temurin:21.0.3_9-jre-jammy@sha256:a56ee1f79cf57b2b31152cd471a4c85b6deb3057e4a1fbe8e50b57e7d2a1d7c9 AS jre-build -FROM debian:stable-slim@sha256:f8bbfa052db81e5b8ac12e4a1d8310a85d1509d4d0d5579148059c0e8b717d4e +FROM debian:stable-slim@sha256:57bd74e95092e6d4c0cdb6e36ca3db5bb828c2f592788734d1a707a4b92e7755 # Arguments that can be passed at build time # Directory names must end with / to avoid errors when ADDing and COPYing From 385bf6eacc1b79446f0032f842e1c262262e352f Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Wed, 24 Jul 2024 15:53:11 +0100 Subject: [PATCH 026/429] replace author with authors for DT model Signed-off-by: Ross Murphy --- .../org/dependencytrack/model/Component.java | 13 ----- .../org/dependencytrack/model/Project.java | 16 ------ .../parser/cyclonedx/util/ModelConverter.java | 50 ++++++++++++++++--- .../persistence/ComponentQueryManager.java | 2 - .../persistence/ProjectQueryManager.java | 2 - .../resources/v1/ComponentResource.java | 3 -- .../resources/v1/ProjectResource.java | 7 ++- .../tasks/BomUploadProcessingTask.java | 2 - .../tasks/BomUploadProcessingTaskTest.java | 2 +- 9 files changed, 46 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/Component.java b/src/main/java/org/dependencytrack/model/Component.java index 7cf40c6261..cb02d7aa5c 100644 --- a/src/main/java/org/dependencytrack/model/Component.java +++ b/src/main/java/org/dependencytrack/model/Component.java @@ -110,11 +110,6 @@ public enum FetchGroup { @JsonIgnore private long id; - @Persistent - @Column(name = "AUTHOR", jdbcType = "CLOB") - @Pattern(regexp = RegexSequence.Definition.PRINTABLE_CHARS, message = "The author may only contain printable characters") - private String author; - @Persistent(defaultFetchGroup = "true") @Convert(OrganizationalContactsJsonConverter.class) @Column(name = "AUTHORS", jdbcType = "CLOB", allowsNull = "true") @@ -392,14 +387,6 @@ public void setId(long id) { this.id = id; } - public String getAuthor() { - return author; - } - - public void setAuthor(String author) { - this.author = author; - } - public List getAuthors() { return authors; } diff --git a/src/main/java/org/dependencytrack/model/Project.java b/src/main/java/org/dependencytrack/model/Project.java index 59c804eaf8..67fea39d1c 100644 --- a/src/main/java/org/dependencytrack/model/Project.java +++ b/src/main/java/org/dependencytrack/model/Project.java @@ -76,7 +76,6 @@ @FetchGroups({ @FetchGroup(name = "ALL", members = { @Persistent(name = "name"), - @Persistent(name = "author"), @Persistent(name = "authors"), @Persistent(name = "publisher"), @Persistent(name = "supplier"), @@ -128,13 +127,6 @@ public enum FetchGroup { @JsonIgnore private long id; - @Persistent - @Column(name = "AUTHOR", jdbcType = "VARCHAR") - @Size(max = 255) - @JsonDeserialize(using = TrimmedStringDeserializer.class) - @Pattern(regexp = RegexSequence.Definition.PRINTABLE_CHARS, message = "The author may only contain printable characters") - private String author; - @Persistent(defaultFetchGroup = "true") @Convert(OrganizationalContactsJsonConverter.class) @Column(name = "AUTHORS", jdbcType = "CLOB", allowsNull = "true") @@ -305,14 +297,6 @@ public void setId(long id) { this.id = id; } - public String getAuthor() { - return author; - } - - public void setAuthor(String author) { - this.author = author; - } - public List getAuthors() { return authors; } diff --git a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java index 294b23ffb7..4bc3832e77 100644 --- a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java +++ b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java @@ -116,8 +116,6 @@ public static Project convertToProject(final org.cyclonedx.model.Metadata cdxMet public static Project convertToProject(final org.cyclonedx.model.Component cdxComponent) { final var project = new Project(); project.setBomRef(useOrGenerateRandomBomRef(cdxComponent.getBomRef())); - project.setAuthor(trimToNull(cdxComponent.getAuthor())); - project.setAuthors(convertCdxContacts(cdxComponent.getAuthors())); project.setPublisher(trimToNull(cdxComponent.getPublisher())); project.setSupplier(convert(cdxComponent.getSupplier())); project.setClassifier(convertClassifier(cdxComponent.getType()).orElse(Classifier.APPLICATION)); @@ -127,6 +125,17 @@ public static Project convertToProject(final org.cyclonedx.model.Component cdxCo project.setDescription(trimToNull(cdxComponent.getDescription())); project.setExternalReferences(convertExternalReferences(cdxComponent.getExternalReferences())); + List contacts = new ArrayList<>(); + if(cdxComponent.getAuthor()!=null){ + contacts.add(new OrganizationalContact() {{ + setName(cdxComponent.getAuthor()); + }}); + } + if(cdxComponent.getAuthors()!=null){ + contacts.addAll(convertCdxContacts(cdxComponent.getAuthors())); + } + project.setAuthors(contacts); + if (cdxComponent.getPurl() != null) { try { final var purl = new PackageURL(cdxComponent.getPurl()); @@ -154,8 +163,6 @@ public static List convertComponents(final List contacts = new ArrayList<>(); + if(cdxComponent.getAuthor()!=null){ + contacts.add(new OrganizationalContact() {{ + setName(cdxComponent.getAuthor()); + }}); + } + if(cdxComponent.getAuthors()!=null){ + contacts.addAll(convertCdxContacts(cdxComponent.getAuthors())); + } + component.setAuthors(contacts); + if (cdxComponent.getPurl() != null) { try { final var purl = new PackageURL(cdxComponent.getPurl()); @@ -527,8 +545,7 @@ public static org.cyclonedx.model.Component convert(final QueryManager qm, final cycloneComponent.setDescription(StringUtils.trimToNull(component.getDescription())); cycloneComponent.setCopyright(StringUtils.trimToNull(component.getCopyright())); cycloneComponent.setCpe(StringUtils.trimToNull(component.getCpe())); - cycloneComponent.setAuthor(StringUtils.trimToNull(component.getAuthor())); - cycloneComponent.setAuthors(convertContacts(component.getAuthors())); + cycloneComponent.setAuthor(StringUtils.trimToNull(convertContactsToString(component.getAuthors()))); cycloneComponent.setSupplier(convert(component.getSupplier())); cycloneComponent.setProperties(convert(component.getProperties())); @@ -657,6 +674,24 @@ private static List co return cdxProperties; } + public static String convertContactsToString(List authors) { + if (authors == null || authors.isEmpty()) { + return ""; + } + StringBuilder stringBuilder = new StringBuilder(); + for (OrganizationalContact author : authors) { + if (author != null && author.getName() != null) { + stringBuilder.append(author.getName()).append(", "); + } + } + //remove trailing comma and space + if (stringBuilder.length() > 0) { + stringBuilder.setLength(stringBuilder.length() - 2); + } + return stringBuilder.toString(); + } + + public static org.cyclonedx.model.Metadata createMetadata(final Project project) { final org.cyclonedx.model.Metadata metadata = new org.cyclonedx.model.Metadata(); final org.cyclonedx.model.Tool tool = new org.cyclonedx.model.Tool(); @@ -669,8 +704,7 @@ public static org.cyclonedx.model.Metadata createMetadata(final Project project) final org.cyclonedx.model.Component cycloneComponent = new org.cyclonedx.model.Component(); cycloneComponent.setBomRef(project.getUuid().toString()); - cycloneComponent.setAuthor(StringUtils.trimToNull(project.getAuthor())); - cycloneComponent.setAuthors(convertContacts(project.getAuthors())); + cycloneComponent.setAuthor(StringUtils.trimToNull(convertContactsToString(project.getAuthors()))); cycloneComponent.setPublisher(StringUtils.trimToNull(project.getPublisher())); cycloneComponent.setGroup(StringUtils.trimToNull(project.getGroup())); cycloneComponent.setName(StringUtils.trimToNull(project.getName())); diff --git a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java index 78401e17e5..2f7114e606 100644 --- a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java @@ -377,7 +377,6 @@ public Component cloneComponent(Component sourceComponent, Project destinationPr component.setLicenseExpression(sourceComponent.getLicenseExpression()); component.setLicenseUrl(sourceComponent.getLicenseUrl()); component.setResolvedLicense(sourceComponent.getResolvedLicense()); - component.setAuthor(sourceComponent.getAuthor()); component.setAuthors(sourceComponent.getAuthors()); component.setSupplier(sourceComponent.getSupplier()); // TODO Add support for parent component and children components @@ -413,7 +412,6 @@ public Component updateComponent(Component transientComponent, boolean commitInd component.setCpe(transientComponent.getCpe()); component.setPurl(transientComponent.getPurl()); component.setInternal(transientComponent.isInternal()); - component.setAuthor(transientComponent.getAuthor()); component.setAuthors(transientComponent.getAuthors()); component.setSupplier(transientComponent.getSupplier()); component.setExternalReferences(transientComponent.getExternalReferences()); diff --git a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java index 29a841e3fb..a9f3bc2443 100644 --- a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java @@ -532,7 +532,6 @@ public Project createProject(final Project project, List tags, boolean comm @Override public Project updateProject(Project transientProject, boolean commitIndex) { final Project project = getObjectByUuid(Project.class, transientProject.getUuid()); - project.setAuthor(transientProject.getAuthor()); project.setAuthors(transientProject.getAuthors()); project.setPublisher(transientProject.getPublisher()); project.setManufacturer(transientProject.getManufacturer()); @@ -604,7 +603,6 @@ public Project clone( return null; } Project project = new Project(); - project.setAuthor(source.getAuthor()); project.setAuthors(source.getAuthors()); project.setManufacturer(source.getManufacturer()); project.setSupplier(source.getSupplier()); diff --git a/src/main/java/org/dependencytrack/resources/v1/ComponentResource.java b/src/main/java/org/dependencytrack/resources/v1/ComponentResource.java index ca4a340942..61b2397e23 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ComponentResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ComponentResource.java @@ -284,7 +284,6 @@ public Response createComponent(@Parameter(description = "The UUID of the projec @PathParam("uuid") @ValidUuid String uuid, Component jsonComponent) { final Validator validator = super.getValidator(); failOnValidationError( - validator.validateProperty(jsonComponent, "author"), validator.validateProperty(jsonComponent, "authors"), validator.validateProperty(jsonComponent, "publisher"), validator.validateProperty(jsonComponent, "name"), @@ -324,7 +323,6 @@ public Response createComponent(@Parameter(description = "The UUID of the projec final License resolvedLicense = qm.getLicense(jsonComponent.getLicense()); Component component = new Component(); component.setProject(project); - component.setAuthor(StringUtils.trimToNull(jsonComponent.getAuthor())); component.setAuthors(jsonComponent.getAuthors()); component.setPublisher(StringUtils.trimToNull(jsonComponent.getPublisher())); component.setName(StringUtils.trimToNull(jsonComponent.getName())); @@ -428,7 +426,6 @@ public Response updateComponent(Component jsonComponent) { if (name != null) { component.setName(name); } - component.setAuthor(StringUtils.trimToNull(jsonComponent.getAuthor())); component.setAuthors(jsonComponent.getAuthors()); component.setPublisher(StringUtils.trimToNull(jsonComponent.getPublisher())); component.setVersion(StringUtils.trimToNull(jsonComponent.getVersion())); diff --git a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java index 847fb74aba..96bd9baad4 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java @@ -289,7 +289,7 @@ public Response getProjectsByClassifier( public Response createProject(Project jsonProject) { final Validator validator = super.getValidator(); failOnValidationError( - validator.validateProperty(jsonProject, "author"), + validator.validateProperty(jsonProject, "authors"), validator.validateProperty(jsonProject, "publisher"), validator.validateProperty(jsonProject, "group"), validator.validateProperty(jsonProject, "name"), @@ -353,7 +353,7 @@ public Response createProject(Project jsonProject) { public Response updateProject(Project jsonProject) { final Validator validator = super.getValidator(); failOnValidationError( - validator.validateProperty(jsonProject, "author"), + validator.validateProperty(jsonProject, "authors"), validator.validateProperty(jsonProject, "publisher"), validator.validateProperty(jsonProject, "group"), validator.validateProperty(jsonProject, "name"), @@ -429,7 +429,7 @@ public Response patchProject( Project jsonProject) { final Validator validator = getValidator(); failOnValidationError( - validator.validateProperty(jsonProject, "author"), + validator.validateProperty(jsonProject, "authors"), validator.validateProperty(jsonProject, "publisher"), validator.validateProperty(jsonProject, "group"), jsonProject.getName() != null ? validator.validateProperty(jsonProject, "name") : Set.of(), @@ -455,7 +455,6 @@ public Response patchProject( if (modified && qm.doesProjectExist(project.getName(), project.getVersion())) { return Response.status(Response.Status.CONFLICT).entity("A project with the specified name and version already exists.").build(); } - modified |= setIfDifferent(jsonProject, project, Project::getAuthor, Project::setAuthor); modified |= setIfDifferent(jsonProject, project, Project::getAuthors, Project::setAuthors); modified |= setIfDifferent(jsonProject, project, Project::getPublisher, Project::setPublisher); modified |= setIfDifferent(jsonProject, project, Project::getGroup, Project::setGroup); diff --git a/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java b/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java index b9afbb091e..210dc6d391 100644 --- a/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java +++ b/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java @@ -323,7 +323,6 @@ private Project processProject( boolean hasChanged = false; if (project != null) { persistentProject.setBomRef(project.getBomRef()); // Transient - hasChanged |= applyIfChanged(persistentProject, project, Project::getAuthor, persistentProject::setAuthor); hasChanged |= applyIfChanged(persistentProject, project, Project::getAuthors, persistentProject::setAuthors); hasChanged |= applyIfChanged(persistentProject, project, Project::getPublisher, persistentProject::setPublisher); hasChanged |= applyIfChanged(persistentProject, project, Project::getManufacturer, persistentProject::setManufacturer); @@ -407,7 +406,6 @@ private Map processComponents( component.setNew(true); // Transient } else { persistentComponent.setBomRef(component.getBomRef()); // Transient - applyIfChanged(persistentComponent, component, Component::getAuthor, persistentComponent::setAuthor); applyIfChanged(persistentComponent, component, Component::getAuthors, persistentComponent::setAuthors); applyIfChanged(persistentComponent, component, Component::getPublisher, persistentComponent::setPublisher); applyIfChanged(persistentComponent, component, Component::getSupplier, persistentComponent::setSupplier); diff --git a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java index 4703e41ae7..a7dc674695 100644 --- a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java @@ -211,7 +211,7 @@ public void informTest() throws Exception { assertThat(component.getSupplier().getContacts().get(0).getEmail()).isEqualTo("foojr@bar.com"); assertThat(component.getSupplier().getContacts().get(0).getPhone()).isEqualTo("123-456-7890"); - assertThat(component.getAuthor()).isEqualTo("Sometimes this field is long because it is composed of a list of authors......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................"); + assertThat(component.getAuthors().get(0).getName()).isEqualTo("Sometimes this field is long because it is composed of a list of authors......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................"); assertThat(component.getPublisher()).isEqualTo("Example Incorporated"); assertThat(component.getGroup()).isEqualTo("com.example"); assertThat(component.getName()).isEqualTo("xmlutil"); From 8dce89f78b370c045727049bf2a6707512eb89e5 Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Wed, 24 Jul 2024 17:36:18 +0100 Subject: [PATCH 027/429] whitespace Signed-off-by: Ross Murphy --- .../dependencytrack/parser/cyclonedx/util/ModelConverter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java index 4bc3832e77..374f41391b 100644 --- a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java +++ b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java @@ -690,7 +690,6 @@ public static String convertContactsToString(List authors } return stringBuilder.toString(); } - public static org.cyclonedx.model.Metadata createMetadata(final Project project) { final org.cyclonedx.model.Metadata metadata = new org.cyclonedx.model.Metadata(); From b423e4186a66e8d95c98c334ca1ffd744b0610df Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 08:37:06 +0000 Subject: [PATCH 028/429] Bump com.fasterxml.woodstox:woodstox-core from 6.6.2 to 7.0.0 Bumps [com.fasterxml.woodstox:woodstox-core](https://github.com/FasterXML/woodstox) from 6.6.2 to 7.0.0. - [Commits](https://github.com/FasterXML/woodstox/compare/woodstox-core-6.6.2...woodstox-core-7.0.0) --- updated-dependencies: - dependency-name: com.fasterxml.woodstox:woodstox-core dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f2dab7ceda..e87ed26668 100644 --- a/pom.xml +++ b/pom.xml @@ -115,7 +115,7 @@ 1.19.0 1.20.0 2.35.2 - 6.6.2 + 7.0.0 1.1.1 2.1.1 4.5.14 From c4bb56d7795aa38d4594c38a5affea105a25a41e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 08:37:14 +0000 Subject: [PATCH 029/429] Bump com.google.cloud.sql:postgres-socket-factory from 1.18.0 to 1.19.1 Bumps com.google.cloud.sql:postgres-socket-factory from 1.18.0 to 1.19.1. --- updated-dependencies: - dependency-name: com.google.cloud.sql:postgres-socket-factory dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f2dab7ceda..2d46d73ada 100644 --- a/pom.xml +++ b/pom.xml @@ -92,7 +92,7 @@ 10.16.0 1.19.1 1.18.0 - 1.18.0 + 1.19.1 2.1.0 1.26.2 1.12.0 From a15e8da238f10c1fbaca11f036695ab51170d894 Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Fri, 26 Jul 2024 00:25:09 +0100 Subject: [PATCH 030/429] add author getter and setters to component and project and test Signed-off-by: Ross Murphy --- .../org/dependencytrack/model/Component.java | 17 +++++++++++++++++ .../java/org/dependencytrack/model/Project.java | 17 +++++++++++++++++ .../resources/v1/BomResourceTest.java | 7 +++++++ .../resources/v1/ComponentResourceTest.java | 2 ++ 4 files changed, 43 insertions(+) diff --git a/src/main/java/org/dependencytrack/model/Component.java b/src/main/java/org/dependencytrack/model/Component.java index cb02d7aa5c..9a5eb88bd7 100644 --- a/src/main/java/org/dependencytrack/model/Component.java +++ b/src/main/java/org/dependencytrack/model/Component.java @@ -33,6 +33,7 @@ import org.dependencytrack.persistence.converter.OrganizationalContactsJsonConverter; import org.dependencytrack.persistence.converter.OrganizationalEntityJsonConverter; import org.dependencytrack.resources.v1.serializers.CustomPackageURLSerializer; +import org.dependencytrack.parser.cyclonedx.util.ModelConverter; import jakarta.json.JsonObject; import jakarta.validation.constraints.NotBlank; @@ -399,6 +400,22 @@ public String getPublisher() { return publisher; } + @Deprecated + @JsonIgnore + public String getAuthor(){ + return ModelConverter.convertContactsToString(this.authors); + } + + @Deprecated + public void setAuthor(String author){ + if(this.authors==null){ + this.authors = new ArrayList<>(); + } + this.authors.add(new OrganizationalContact() {{ + setName(author); + }}); + } + public void setPublisher(String publisher) { this.publisher = publisher; } diff --git a/src/main/java/org/dependencytrack/model/Project.java b/src/main/java/org/dependencytrack/model/Project.java index 67fea39d1c..c672fd9484 100644 --- a/src/main/java/org/dependencytrack/model/Project.java +++ b/src/main/java/org/dependencytrack/model/Project.java @@ -33,6 +33,7 @@ import com.github.packageurl.PackageURL; import io.swagger.v3.oas.annotations.media.Schema; +import org.dependencytrack.parser.cyclonedx.util.ModelConverter; import org.dependencytrack.persistence.converter.OrganizationalContactsJsonConverter; import org.dependencytrack.persistence.converter.OrganizationalEntityJsonConverter; import org.dependencytrack.resources.v1.serializers.CustomPackageURLSerializer; @@ -305,6 +306,22 @@ public void setAuthors(List authors) { this.authors = authors; } + @Deprecated + @JsonIgnore + public String getAuthor(){ + return ModelConverter.convertContactsToString(this.authors); + } + + @Deprecated + public void setAuthor(String author){ + if(this.authors==null){ + this.authors = new ArrayList<>(); + } + this.authors.add(new OrganizationalContact() {{ + setName(author); + }}); + } + public String getPublisher() { return publisher; } diff --git a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java index 9ab156d916..da0b7e04ac 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java @@ -59,6 +59,7 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Base64; import java.util.List; import java.util.UUID; @@ -130,6 +131,11 @@ public void exportProjectAsCycloneDxInventoryTest() { project.setClassifier(Classifier.APPLICATION); project.setManufacturer(projectManufacturer); project.setSupplier(projectSupplier); + List authors = new ArrayList<>(); + authors.add(new OrganizationalContact() {{ + setName("SampleAuthor"); + }}); + project.setAuthors(authors); project = qm.createProject(project, null, false); final var projectProperty = new ProjectProperty(); @@ -237,6 +243,7 @@ public void exportProjectAsCycloneDxInventoryTest() { "component": { "type": "application", "bom-ref": "${json-unit.matches:projectUuid}", + "author": "SampleAuthor", "supplier": { "name": "projectSupplier" }, diff --git a/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java index 18fe19668f..9b49d94f58 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java @@ -506,6 +506,7 @@ public void createComponentTest() { component.setProject(project); component.setName("My Component"); component.setVersion("1.0"); + component.setAuthor("SampleAuthor"); Response response = jersey.target(V1_COMPONENT + "/project/" + project.getUuid().toString()).request() .header(X_API_KEY, apiKey) .put(Entity.entity(component, MediaType.APPLICATION_JSON)); @@ -513,6 +514,7 @@ public void createComponentTest() { JsonObject json = parseJsonObject(response); Assert.assertNotNull(json); Assert.assertEquals("My Component", json.getString("name")); + Assert.assertEquals("SampleAuthor" ,json.getJsonArray("authors").getJsonObject(0).getString("name")); Assert.assertEquals("1.0", json.getString("version")); Assert.assertTrue(UuidUtil.isValidUUID(json.getString("uuid"))); } From b33c1351dba7de7b2baa8bcbaee95e0babc4ad45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 08:15:24 +0000 Subject: [PATCH 031/429] Bump docker/setup-qemu-action from 3.1.0 to 3.2.0 Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/5927c834f5b4fdf503fca6f4c7eccda82949e1ee...49b3bc8e6bdd4a60e6116a5414239cba5943d3cf) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 5e8c4f9fca..275b12bd81 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -87,7 +87,7 @@ jobs: path: target - name: Set up QEMU - uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee # tag=v3.1.0 + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # tag=v3.2.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # tag=v3.4.0 From 73350803cfa94f487ffae011ab810848a5393ad4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 08:15:32 +0000 Subject: [PATCH 032/429] Bump docker/login-action from 3.2.0 to 3.3.0 Bumps [docker/login-action](https://github.com/docker/login-action) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/0d4c9c5ea7693da7b068278f7b52bda2a190a446...9780b0c442fbb1117ed29e0efdff1e18412f7567) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 5e8c4f9fca..f0cc0df421 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -96,7 +96,7 @@ jobs: install: true - name: Login to Docker.io - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # tag=v3.2.0 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # tag=v3.3.0 if: ${{ inputs.publish-container }} with: registry: docker.io From 15257fe23b6c28ea84a51b4f62b6f93d5c77d9af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 08:15:38 +0000 Subject: [PATCH 033/429] Bump github/codeql-action from 3.25.13 to 3.25.15 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.13 to 3.25.15. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/2d790406f505036ef40ecba973cc774a50395aac...afb54ba388a7dca6ecae48f608c4ff05ff4cc77a) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 5e8c4f9fca..bd50a5fc74 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -145,6 +145,6 @@ jobs: - name: Upload Trivy Scan Results to GitHub Security Tab if: ${{ inputs.publish-container }} - uses: github/codeql-action/upload-sarif@2d790406f505036ef40ecba973cc774a50395aac # tag=v3.25.13 + uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # tag=v3.25.15 with: sarif_file: 'trivy-results.sarif' From 718c9d0556344eb0b36e14d0c83a74768f72cb08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 08:54:05 +0000 Subject: [PATCH 034/429] Bump docker/setup-buildx-action from 3.4.0 to 3.5.0 Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.4.0 to 3.5.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/4fd812986e6c8c2a69e18311145f9371337f27d4...aa33708b10e362ff993539393ff100fa93ed6a27) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 43972f2c12..590101a56b 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -90,7 +90,7 @@ jobs: uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # tag=v3.2.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # tag=v3.4.0 + uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # tag=v3.5.0 id: buildx with: install: true From 3ec5edf948936a5de904ca394f7df4f51a30b8c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 08:27:49 +0000 Subject: [PATCH 035/429] Bump org.eclipse.jetty.ee10:jetty-ee10-maven-plugin Bumps org.eclipse.jetty.ee10:jetty-ee10-maven-plugin from 12.0.11 to 12.0.12. --- updated-dependencies: - dependency-name: org.eclipse.jetty.ee10:jetty-ee10-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bab9a429c6..10d76d1e56 100644 --- a/pom.xml +++ b/pom.xml @@ -130,7 +130,7 @@ application json false - 12.0.11 + 12.0.12 src/main/webapp/** From 589f1794a8b19d830b1c5c09f8aece0f18637cbe Mon Sep 17 00:00:00 2001 From: Philipp Nanz Date: Wed, 31 Jul 2024 10:56:51 +0200 Subject: [PATCH 036/429] Fix validation error when XML BOM declares multiple namespaces Signed-off-by: Philipp Nanz --- .../parser/cyclonedx/CycloneDxValidator.java | 4 + .../resources/v1/BomResourceTest.java | 6 ++ src/test/resources/unit/bom-issue4008.xml | 99 +++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 src/test/resources/unit/bom-issue4008.xml diff --git a/src/main/java/org/dependencytrack/parser/cyclonedx/CycloneDxValidator.java b/src/main/java/org/dependencytrack/parser/cyclonedx/CycloneDxValidator.java index fae7216c9d..88351ff1e2 100644 --- a/src/main/java/org/dependencytrack/parser/cyclonedx/CycloneDxValidator.java +++ b/src/main/java/org/dependencytrack/parser/cyclonedx/CycloneDxValidator.java @@ -195,6 +195,10 @@ private Version detectSchemaVersionFromXml(final byte[] bomBytes) throws XMLStre case NS_BOM_16 -> VERSION_16; default -> null; }; + + if (schemaVersion != null) { + break; + } } if (schemaVersion == null) { diff --git a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java index 425763ca78..c867eb32dc 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java @@ -1146,4 +1146,10 @@ public void uploadBomTooLargeViaPutTest() { """); } + @Test + public void validateCycloneDxBomWithMultipleNamespacesTest() throws Exception { + byte[] bom = resourceToByteArray("/unit/bom-issue4008.xml"); + assertThatNoException().isThrownBy(() -> CycloneDxValidator.getInstance().validate(bom)); + } + } diff --git a/src/test/resources/unit/bom-issue4008.xml b/src/test/resources/unit/bom-issue4008.xml new file mode 100644 index 0000000000..f4d2a5630b --- /dev/null +++ b/src/test/resources/unit/bom-issue4008.xml @@ -0,0 +1,99 @@ + + + + + + Author + author@example.com + 123-456-7890 + + + + + Foo Incorporated + https://foo.bar.com + + Foo Jr. + foojr@bar.com + 123-456-7890 + + + DependencyTrack + Acme example + + + https://acme.example + + + https://acme.example + + + https://acme.example + + + https://acme.example + + + + + Foo Incorporated + https://foo.bar.com + + Foo Sr. + foo@bar.com + 800-123-4567 + + + + Foo Incorporated + https://foo.bar.com + + Foo Jr. + foojr@bar.com + 123-456-7890 + + + + + + + Foo Incorporated + https://foo.bar.com + + Foo Jr. + foojr@bar.com + 123-456-7890 + + + Sometimes this field is long because it is composed of a list of authors...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... + Example Incorporated + com.example + xmlutil + 1.0.0 + A makebelieve XML utility library + + 2b67669c925048d1a5c7f124d9ba1d2a + 72ca79908c814022905e86f8bbecd9b829352139 + 1389877662864d2bb0488b4b1e417ce5647a1687084341178a203b243dfe90e7 + + + + Apache-2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + + + Copyright Example Inc. All rights reserved. + cpe:/a:example:xmlutil:1.0.0 + pkg:maven/com.example/xmlutil@1.0.0?packaging=jar + false + + foo + bar + baz + qux + qux + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + + + From 7402bf1005918c8ef9ce195269ddb1ebc89a181e Mon Sep 17 00:00:00 2001 From: mehab Date: Wed, 31 Jul 2024 11:54:19 +0100 Subject: [PATCH 037/429] added changes to index.html Signed-off-by: mehab --- src/main/webapp/index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index dc859aeb51..facbae7a0d 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -8,7 +8,8 @@

Dependency-Track API Server

From 60ced7b9b4d835b6a6ab478b8a39bbcf9f4431c3 Mon Sep 17 00:00:00 2001 From: nscuro Date: Wed, 31 Jul 2024 17:32:45 +0200 Subject: [PATCH 038/429] Handle breaking change in Trivy v0.54.0 server API See https://github.com/DependencyTrack/dependency-track/issues/4021#issuecomment-2260758711 for details. Fixes #4021 Signed-off-by: nscuro --- .../parser/trivy/model/Options.java | 27 ++++++++++++++++--- .../tasks/scanners/TrivyAnalysisTask.java | 2 +- .../tasks/scanners/TrivyAnalysisTaskTest.java | 6 ++++- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/dependencytrack/parser/trivy/model/Options.java b/src/main/java/org/dependencytrack/parser/trivy/model/Options.java index bce21e204b..888f162187 100644 --- a/src/main/java/org/dependencytrack/parser/trivy/model/Options.java +++ b/src/main/java/org/dependencytrack/parser/trivy/model/Options.java @@ -21,13 +21,32 @@ import com.google.gson.annotations.SerializedName; public class Options { + + /** + * NB: GSON doesn't support serialization of getters, it can only deal with fields. + * Need to have libraries as redundant field to packages, with Jackson we could just + * use a computed getter with {@link com.fasterxml.jackson.annotation.JsonGetter}. + * Migrate this to Jackson eventually. + * + * @see GitHub issue + * @deprecated Kept for compatibility with Trivy < 0.54.0 + */ + @Deprecated(forRemoval = true) @SerializedName("vuln_type") private String[] vulnType; + + @SerializedName("pkg_types") + private String[] pkgTypes; + private String[] scanners; - public String[] getVulnType() { return vulnType; } - public void setVulnType(String[] value) { this.vulnType = value; } + public void setPkgTypes(String[] value) { + this.pkgTypes = value; + this.vulnType = value; + } + + public void setScanners(String[] value) { + this.scanners = value; + } - public String[] getScanners() { return scanners; } - public void setScanners(String[] value) { this.scanners = value; } } \ No newline at end of file diff --git a/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java b/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java index 0dbb155cc9..0a07573df0 100644 --- a/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java +++ b/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java @@ -414,7 +414,7 @@ private TrivyResponse scanBlob(PutRequest input) { scan.setBlobIDS(new String[]{input.getDiffID()}); final var opts = new Options(); - opts.setVulnType(new String[]{"os", "library"}); + opts.setPkgTypes(new String[]{"os", "library"}); opts.setScanners(new String[]{"vuln"}); scan.setOptions(opts); diff --git a/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java b/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java index d04102b764..9ef8abf723 100644 --- a/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java @@ -27,6 +27,7 @@ import com.github.packageurl.PackageURL; import com.github.tomakehurst.wiremock.http.Fault; import com.github.tomakehurst.wiremock.junit.WireMockRule; +import jakarta.json.Json; import org.assertj.core.api.SoftAssertions; import org.dependencytrack.PersistenceCapableTest; import org.dependencytrack.common.ManagedHttpClientFactory; @@ -44,7 +45,6 @@ import org.junit.Rule; import org.junit.Test; -import jakarta.json.Json; import java.util.Date; import java.util.List; import java.util.Map; @@ -410,6 +410,10 @@ Those using Woodstox to parse XML data may be vulnerable to Denial of Service at "${json-unit.regex}(^sha256:[a-f0-9]{64}$)" ], "options": { + "pkg_types": [ + "os", + "library" + ], "vuln_type": [ "os", "library" From da4f57c846cd6e8ed8881b7024d8aa4d42a62e88 Mon Sep 17 00:00:00 2001 From: Niklas Date: Thu, 1 Aug 2024 12:57:30 +0200 Subject: [PATCH 039/429] Deal with JUnit 4 version no longer being inherited from `alpine-parent` Alpine was migrated to JUnit Jupiter via https://github.com/stevespringett/Alpine/pull/609 and thus no longer provides JUnit 4 versions. Signed-off-by: Niklas --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 10d76d1e56..cc49d98f09 100644 --- a/pom.xml +++ b/pom.xml @@ -104,6 +104,7 @@ 2.17.1 20240303 3.4.1 + 4.13.2 8.11.3 3.9.8 5.15.0 From 810fd17014111e86e30bacac036be7a4eb76b7d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:36:38 +0000 Subject: [PATCH 040/429] Bump org.testcontainers:testcontainers from 1.20.0 to 1.20.1 Bumps [org.testcontainers:testcontainers](https://github.com/testcontainers/testcontainers-java) from 1.20.0 to 1.20.1. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.20.0...1.20.1) --- updated-dependencies: - dependency-name: org.testcontainers:testcontainers dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cc49d98f09..5ae6029136 100644 --- a/pom.xml +++ b/pom.xml @@ -114,7 +114,7 @@ 2.2.0 2.1.22 1.19.0 - 1.20.0 + 1.20.1 2.35.2 7.0.0 1.1.1 From 5b48dbf5708ff5d7483acf39cf9df7c96af4f3a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:36:48 +0000 Subject: [PATCH 041/429] Bump com.microsoft.sqlserver:mssql-jdbc Bumps [com.microsoft.sqlserver:mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 12.6.1.jre11 to 12.8.0.jre11. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) --- updated-dependencies: - dependency-name: com.microsoft.sqlserver:mssql-jdbc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cc49d98f09..b4a00ab622 100644 --- a/pom.xml +++ b/pom.xml @@ -124,7 +124,7 @@ 2.0.13 1.321 - 12.6.1.jre11 + 12.8.0.jre11 8.0.33 42.7.3 From 96bafd84e2a6a8b9dc44736b4e00ee46a02eec76 Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Thu, 1 Aug 2024 12:47:33 +0100 Subject: [PATCH 042/429] fix project link for new vulnerable dependency for email Signed-off-by: Ross Murphy --- src/main/resources/templates/notification/publisher/email.peb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/templates/notification/publisher/email.peb b/src/main/resources/templates/notification/publisher/email.peb index 3cdbb7320c..2e09ce5fca 100644 --- a/src/main/resources/templates/notification/publisher/email.peb +++ b/src/main/resources/templates/notification/publisher/email.peb @@ -21,7 +21,7 @@ Project: [{{ affectedProject.name }} : {{ affectedProject.version }}] Project URL: {{ baseUrl }}/projects/{{ affectedProject.uuid }} {% endif %}{% endfor %}{% endif %}{% elseif notification.group == "NEW_VULNERABLE_DEPENDENCY" %} Project: {{ subject.component.project.toString }} -Project URL: {{ baseUrl }}/projects/?uuid={{ subject.component.project.uuid }} +Project URL: {{ baseUrl }}/projects/{{ subject.component.project.uuid }} Component: {{ subject.component.toString }} Component URL: {{ baseUrl }}/component/?uuid={{ subject.component.uuid }} From e298d22472839bec47a2f486d5926f100635e01b Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Thu, 1 Aug 2024 12:53:47 +0100 Subject: [PATCH 043/429] fix url in mail publisher unit test Signed-off-by: Ross Murphy --- .../notification/publisher/SendMailPublisherTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java b/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java index bee3a3be2a..4b038b2ec8 100644 --- a/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java +++ b/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java @@ -328,7 +328,7 @@ public void testInformWithNewVulnerableDependencyNotification() { -------------------------------------------------------------------------------- Project: pkg:maven/org.acme/projectName@projectVersion - Project URL: /projects/?uuid=c9c9539a-e381-4b36-ac52-6a7ab83b2c95 + Project URL: /projects/c9c9539a-e381-4b36-ac52-6a7ab83b2c95 Component: componentName : componentVersion Component URL: /component/?uuid=94f87321-a5d1-4c2f-b2fe-95165debebc6 From 7026f5e2b064fda36ef57d47888096530bbf4072 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 08:53:08 +0000 Subject: [PATCH 044/429] Bump org.kohsuke:github-api from 1.321 to 1.323 Bumps [org.kohsuke:github-api](https://github.com/hub4j/github-api) from 1.321 to 1.323. - [Release notes](https://github.com/hub4j/github-api/releases) - [Changelog](https://github.com/hub4j/github-api/blob/main/CHANGELOG.md) - [Commits](https://github.com/hub4j/github-api/compare/github-api-1.321...github-api-1.323) --- updated-dependencies: - dependency-name: org.kohsuke:github-api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5942136636..1c41c7b654 100644 --- a/pom.xml +++ b/pom.xml @@ -122,7 +122,7 @@ 4.5.14 5.3.1 2.0.13 - 1.321 + 1.323 12.8.0.jre11 8.0.33 From 88c49a9caab512935281c1edfeb66d9e8d8f8d2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 08:53:24 +0000 Subject: [PATCH 045/429] Bump com.puppycrawl.tools:checkstyle from 10.16.0 to 10.17.0 Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 10.16.0 to 10.17.0. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-10.16.0...checkstyle-10.17.0) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5942136636..f4cc162300 100644 --- a/pom.xml +++ b/pom.xml @@ -89,7 +89,7 @@ ${project.parent.version} 4.2.1 0.1.2 - 10.16.0 + 10.17.0 1.19.1 1.18.0 1.19.1 From 3520236f9212faab059ad8fca42af3ff42720185 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sat, 3 Aug 2024 18:34:44 +0200 Subject: [PATCH 046/429] Update changelog for v4.12.0 with recent changes Signed-off-by: nscuro --- docs/_posts/2024-xx-xx-v4.12.0.md | 61 ++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/docs/_posts/2024-xx-xx-v4.12.0.md b/docs/_posts/2024-xx-xx-v4.12.0.md index c9a2b7a04b..f3a32d45ff 100644 --- a/docs/_posts/2024-xx-xx-v4.12.0.md +++ b/docs/_posts/2024-xx-xx-v4.12.0.md @@ -6,15 +6,48 @@ type: major **Features:** * Raise baseline Java version to 21 - [apiserver/#3682] +* Include whether a project's version is active in the `/api/v1/project/{uuid}` response - [apiserver/#3691] +* Remove legacy `BomUploadProcessingTask` - [apiserver/#3722] +* Gracefully handle `NotSortableException`s in the REST API - [apiserver/#3724] +* Migrate REST API docs from Swagger v2 to OpenAPI v3 - [apiserver/#3726] +* Migrate to Jakarta EE 10 and Jetty 12 - [apiserver/#3730] +* Add support for EPSS policy conditions - [apiserver/#3746] +* Consider the group/namespace when searching components - [apiserver/#3761] +* Add notification for BOM validation failures - [apiserver/#3796] +* Bump CWE dictionary to v4.14 - [apiserver/#3819] +* Add ability to tag project upon BOM upload - [apiserver/#3843] * Bump SPDX license list to v3.24.0, bringing in 25 new licenses - [apiserver/#3846] +* Improve performance of finding retrieval via REST API - [apiserver/#3869] +* Add REST endpoints for tag retrieval - [apiserver/#3881] +* Deprecate `/api/v1/tag/{policyUuid}` in favor of `/api/v1/tag/policy/{uuid}` - [apiserver/#3887] +* Enable string de-duplication JVM option per default - [apiserver/#3893] +* Add REST endpoints for bulk tagging & un-tagging of projects - [apiserver/#3894] +* Add REST endpoint for tag deletion - [apiserver/#3896] +* Add OIDC Documentation for OneLogin - [apiserver/#3921] +* Add REST endpoints to tag and untag policies in bulk - [apiserver/#3924] +* Make project cloning an atomic operation - [apiserver/#3982] +* Log warning when dependency graph is missing the root node - [apiserver/#3990] **Fixes:** +* Fix wrong types in OpenAPI spec for UNIX timestamp fields - [apiserver/#3731] +* Fix `JDOUserException` when multiple licenses match a component's license name - [apiserver/#3958] +* Fix broken anchors in documentation - [apiserver/#3965] +* Fix BOM validation failing for XML with multiple namespaces - [apiserver/#4020] +* Handle breaking change in Trivy 0.54.0 server API - [apiserver/#4023] +* Fix project link for new vulnerable dependency for email - [apiserver/#4026] + **Upgrade Notes:** * The API server now requires Java 21 or newer. Users deploying Dependency-Track via containers don't have to do anything, since those have been shipped with Java 21 since version 4.10.0. Users deploying Dependency-Track as JAR will need to upgrade their Java installation accordingly. +* The `/api/swagger.json` endpoint no longer exists. The REST API documentation is now available +at `/api/openapi.json` and `/api/openapi.yaml` respectively. The documentation format follows +the OpenAPI v3 specification, the Swagger v2 format is no longer provided. +* The `/api/v1/tag/{policyUuid}` REST API endpoint has been deprecated in favor of +`/api/v1/tag/policy/{uuid}`. Users relying on the outdated endpoint for their custom integrations +are encouraged to migrate to the new endpoint. For a complete list of changes, refer to the respective GitHub milestones: @@ -52,4 +85,30 @@ Special thanks to everyone who contributed code to implement enhancements and fi * Frontend: [bom.json](https://github.com/DependencyTrack/frontend/releases/download/4.12.0/bom.json) [apiserver/#3682]: https://github.com/DependencyTrack/dependency-track/pull/3682 -[apiserver/#3846]: https://github.com/DependencyTrack/dependency-track/pull/3846 \ No newline at end of file +[apiserver/#3691]: https://github.com/DependencyTrack/dependency-track/pull/3691 +[apiserver/#3722]: https://github.com/DependencyTrack/dependency-track/pull/3722 +[apiserver/#3724]: https://github.com/DependencyTrack/dependency-track/pull/3724 +[apiserver/#3726]: https://github.com/DependencyTrack/dependency-track/pull/3726 +[apiserver/#3730]: https://github.com/DependencyTrack/dependency-track/pull/3730 +[apiserver/#3731]: https://github.com/DependencyTrack/dependency-track/pull/3731 +[apiserver/#3746]: https://github.com/DependencyTrack/dependency-track/pull/3746 +[apiserver/#3761]: https://github.com/DependencyTrack/dependency-track/pull/3761 +[apiserver/#3796]: https://github.com/DependencyTrack/dependency-track/pull/3796 +[apiserver/#3819]: https://github.com/DependencyTrack/dependency-track/pull/3819 +[apiserver/#3843]: https://github.com/DependencyTrack/dependency-track/pull/3843 +[apiserver/#3846]: https://github.com/DependencyTrack/dependency-track/pull/3846 +[apiserver/#3869]: https://github.com/DependencyTrack/dependency-track/pull/3869 +[apiserver/#3881]: https://github.com/DependencyTrack/dependency-track/pull/3881 +[apiserver/#3887]: https://github.com/DependencyTrack/dependency-track/pull/3887 +[apiserver/#3893]: https://github.com/DependencyTrack/dependency-track/pull/3893 +[apiserver/#3894]: https://github.com/DependencyTrack/dependency-track/pull/3894 +[apiserver/#3896]: https://github.com/DependencyTrack/dependency-track/pull/3896 +[apiserver/#3921]: https://github.com/DependencyTrack/dependency-track/pull/3921 +[apiserver/#3924]: https://github.com/DependencyTrack/dependency-track/pull/3924 +[apiserver/#3958]: https://github.com/DependencyTrack/dependency-track/pull/3958 +[apiserver/#3965]: https://github.com/DependencyTrack/dependency-track/pull/3965 +[apiserver/#3982]: https://github.com/DependencyTrack/dependency-track/pull/3982 +[apiserver/#3990]: https://github.com/DependencyTrack/dependency-track/pull/3990 +[apiserver/#4020]: https://github.com/DependencyTrack/dependency-track/pull/4020 +[apiserver/#4023]: https://github.com/DependencyTrack/dependency-track/pull/4023 +[apiserver/#4026]: https://github.com/DependencyTrack/dependency-track/pull/4026 \ No newline at end of file From b9c7bb6ddb3b5d57ab05eedd1b923d5ae6fe68e1 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sat, 3 Aug 2024 18:43:58 +0200 Subject: [PATCH 047/429] Update REST API docs to reflect change from Swagger v2 to OpenAPI v3 Signed-off-by: nscuro --- docs/_docs/integrations/rest-api.md | 11 ++++++----- .../images/screenshots/swagger-ui-console.png | Bin 397067 -> 288704 bytes docs/images/screenshots/teams.png | Bin 809417 -> 329344 bytes 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/_docs/integrations/rest-api.md b/docs/_docs/integrations/rest-api.md index 53efd08b76..9783eea045 100644 --- a/docs/_docs/integrations/rest-api.md +++ b/docs/_docs/integrations/rest-api.md @@ -5,16 +5,17 @@ chapter: 6 order: 8 --- -Dependency-Track is built using a *thin server architecture* and an *API-first design*. API's are simply at the heart -of the platform. Every API is fully documented via Swagger 2.0. +Dependency-Track is built using a *thin server architecture* and an *API-first design*. APIs are simply at the heart +of the platform. Every API is fully documented via OpenAPI v3. -> http://{hostname}:{port}/api/swagger.json +> http://{hostname}:{port}/api/openapi.json +> http://{hostname}:{port}/api/openapi.yaml The Swagger UI Console (not included) can be used to visualize and explore the wide range of possibilities. Chrome and -FireFox extensions can be use to quickly use the Swagger UI Console. +FireFox extensions can be used to quickly use the Swagger UI Console. ![Swagger UI Console](/images/screenshots/swagger-ui-console.png) -Prior to using the REST APIs, an API Key must be generated. By default, creating a team will NOT create a an API key. A team may have multiple keys. +Prior to using the REST APIs, an API Key must be generated. By default, creating a team will NOT create an API key. A team may have multiple keys. ![Teams - API Key](/images/screenshots/teams.png) diff --git a/docs/images/screenshots/swagger-ui-console.png b/docs/images/screenshots/swagger-ui-console.png index e56288779eba39412d3391c1bfecf87a2a6dc3db..2e0e5fb6b9a008776bede346d316fa8d9cec68a8 100644 GIT binary patch literal 288704 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%0A(>m@g$Vl~>*FSr!Sx*&z-)40e)1&$gN?4V=HS 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
    m^(4q_*CHDg~(X&o82y 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

    |a_iS$Z7bCr;$8%5id1sS(Clqs9vBL%; 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 AmjD0& literal 397067 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 GpZ^7_oBdk= diff --git a/docs/images/screenshots/teams.png b/docs/images/screenshots/teams.png index d5dcc400f167647aa947e347fa6d4fcf7c07a104..b7f7566e9d07f0f58b67d7bf2e3910fc62c45bab 100644 GIT binary patch literal 329344 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

    ^5r9*GRU;2tY1rT;H2fUoa9zoq;oO^q`^ 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%0Dy6Z_U}#}7CquY%`p+lp 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 N`Q^mn0t4sh{{zziht>c9 literal 809417 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

    jb^!h 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�Ds%uec&!8He}- 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 WGLgPk+p7m@@XL13ZADxB&-@$xZQerw From dcafb392910edbd8bc29c7d4259ae0bbb7ac8c49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 08:40:34 +0000 Subject: [PATCH 048/429] Bump actions/upload-artifact from 4.3.4 to 4.3.5 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.4 to 4.3.5. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/0b2256b8c012f0828dc542b3febcab082c67f72b...89ef406dd8d7e03cfd12d9e0a4a378f454709029) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- .github/workflows/ci-test.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 590101a56b..2abc126ff0 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -55,7 +55,7 @@ jobs: mvn -B --no-transfer-progress cyclonedx:makeBom -Dservices.bom.merge.skip=false org.codehaus.mojo:exec-maven-plugin:exec@merge-services-bom - name: Upload Artifacts - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # tag=v4.3.4 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # tag=v4.3.5 with: name: assembled-wars path: |- diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml index 73373c4a73..d1eba75fdf 100644 --- a/.github/workflows/ci-test.yaml +++ b/.github/workflows/ci-test.yaml @@ -66,7 +66,7 @@ jobs: - name: Upload PR test coverage report if: ${{ github.event_name == 'pull_request' }} - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # tag=v4.3.4 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # tag=v4.3.5 with: name: pr-test-coverage-report path: |- From ac217340679b1abc709652fbcb006e2b4a8c789b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 08:40:37 +0000 Subject: [PATCH 049/429] Bump docker/build-push-action from 6.4.1 to 6.5.0 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.4.1 to 6.5.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/1ca370b3a9802c92e886402e0dd88098a2533b12...5176d81f87c23d6fc96624dfdbcd9f3830bbe445) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 590101a56b..74a1b1b040 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -121,7 +121,7 @@ jobs: echo "tags=${TAGS}" >> $GITHUB_OUTPUT - name: Build multi-arch Container Image - uses: docker/build-push-action@1ca370b3a9802c92e886402e0dd88098a2533b12 # tag=v6.4.1 + uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 # tag=v6.5.0 with: tags: ${{ steps.tags.outputs.tags }} build-args: |- From 51e02a52a3b300825aa7f2b3af4c47d029d603e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 08:40:40 +0000 Subject: [PATCH 050/429] Bump docker/setup-buildx-action from 3.5.0 to 3.6.1 Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.5.0 to 3.6.1. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/aa33708b10e362ff993539393ff100fa93ed6a27...988b5a0280414f521da01fcc63a27aeeb4b104db) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 590101a56b..2db692f1fb 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -90,7 +90,7 @@ jobs: uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # tag=v3.2.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # tag=v3.5.0 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # tag=v3.6.1 id: buildx with: install: true From 52503889076f7a0aa76f61b67f5fbf5d3d67e8d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 08:45:56 +0000 Subject: [PATCH 051/429] Bump org.codehaus.mojo:exec-maven-plugin from 3.3.0 to 3.4.0 Bumps [org.codehaus.mojo:exec-maven-plugin](https://github.com/mojohaus/exec-maven-plugin) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/mojohaus/exec-maven-plugin/releases) - [Commits](https://github.com/mojohaus/exec-maven-plugin/compare/3.3.0...3.4.0) --- updated-dependencies: - dependency-name: org.codehaus.mojo:exec-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4e6456a7ee..66d61d447a 100644 --- a/pom.xml +++ b/pom.xml @@ -543,7 +543,7 @@ org.codehaus.mojo exec-maven-plugin - 3.3.0 + 3.4.0 merge-services-bom From 319eea4256476b427c60a071eaf916a51aae0633 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 08:46:12 +0000 Subject: [PATCH 052/429] Bump org.slf4j:log4j-over-slf4j from 2.0.13 to 2.0.14 Bumps org.slf4j:log4j-over-slf4j from 2.0.13 to 2.0.14. --- updated-dependencies: - dependency-name: org.slf4j:log4j-over-slf4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4e6456a7ee..c44aeb1750 100644 --- a/pom.xml +++ b/pom.xml @@ -121,7 +121,7 @@ 2.1.1 4.5.14 5.3.1 - 2.0.13 + 2.0.14 1.323 12.8.0.jre11 From 1c784cb5751c04b2e39abe1525088c2cc034f76f Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Wed, 7 Aug 2024 22:03:15 +0100 Subject: [PATCH 053/429] add test to make sure authorz is populated correctly Signed-off-by: Ross Murphy --- .../resources/v1/BomResourceTest.java | 55 ------------------- .../tasks/BomUploadProcessingTaskTest.java | 23 ++++++++ .../resources/unit/bom-issue3936-author.json | 28 ++++++++++ .../resources/unit/bom-issue3936-authors.json | 32 +++++++++++ .../resources/unit/bom-issue3936-both.json | 33 +++++++++++ 5 files changed, 116 insertions(+), 55 deletions(-) create mode 100644 src/test/resources/unit/bom-issue3936-author.json create mode 100644 src/test/resources/unit/bom-issue3936-authors.json create mode 100644 src/test/resources/unit/bom-issue3936-both.json diff --git a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java index d297eb60d2..f1d4f0cae5 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java @@ -1013,61 +1013,6 @@ public void uploadBomInvalidParentTest() throws Exception { Assert.assertEquals("The parent component could not be found.", body); } - @Test - public void uploadBomAuthorsTest() { - initializeWithPermissions(Permissions.BOM_UPLOAD); - - qm.createConfigProperty( - BOM_VALIDATION_ENABLED.getGroupName(), - BOM_VALIDATION_ENABLED.getPropertyName(), - "true", - BOM_VALIDATION_ENABLED.getPropertyType(), - null - ); - - final var project = new Project(); - project.setName("acme-app"); - project.setVersion("1.0.0"); - qm.persist(project); - - final String bomJson = """ - { - "bomFormat": "CycloneDX", - "specVersion": "1.6", - "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", - "version": 1, - "components": [ - { - "type": "application", - "name": "acme-library", - "version": "1.0.0", - "authors" : [ - { - "name" : "bomAuthor" - } - ] - } - ] - } - """; - - final String encodedBom = Base64.getEncoder().encodeToString(bomJson.getBytes(StandardCharsets.UTF_8)); - - assertThatNoException().isThrownBy(() -> CycloneDxValidator.getInstance().validate(bomJson.getBytes(StandardCharsets.UTF_8))); - - final Response response = jersey.target(V1_BOM).request() - .header(X_API_KEY, apiKey) - .put(Entity.entity(""" - { - "projectName": "acme-app", - "projectVersion": "1.0.0", - "bom": "%s" - } - """.formatted(encodedBom), MediaType.APPLICATION_JSON)); - - assertThat(response.getStatus()).isEqualTo(200); - } - @Test public void uploadBomInvalidJsonTest() { initializeWithPermissions(Permissions.BOM_UPLOAD); diff --git a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java index a7dc674695..8cdc2ef431 100644 --- a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java @@ -25,6 +25,8 @@ import alpine.notification.NotificationLevel; import alpine.notification.NotificationService; import alpine.notification.Subscription; +import java.util.Arrays; + import org.awaitility.core.ConditionTimeoutException; import org.dependencytrack.PersistenceCapableTest; import org.dependencytrack.event.BomUploadEvent; @@ -55,6 +57,7 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -68,6 +71,7 @@ import static org.awaitility.Awaitility.await; import static org.dependencytrack.assertion.Assertions.assertConditionWithTimeout; import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_ENABLED; +import static org.junit.Assert.assertEquals; public class BomUploadProcessingTaskTest extends PersistenceCapableTest { @@ -1293,6 +1297,25 @@ public void informIssue3981Test() { }); } + @Test + public void informIssue3936Test() throws Exception{ + + final Project project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); + List boms = new ArrayList<>(Arrays.asList("/unit/bom-issue3936-authors.json", "/unit/bom-issue3936-author.json", "/unit/bom-issue3936-both.json")); + for(String bom : boms){ + final var bomUploadEvent = new BomUploadEvent(qm.detach(Project.class, project.getId()), + resourceToByteArray(bom)); + new BomUploadProcessingTask().inform(bomUploadEvent); + awaitBomProcessedNotification(bomUploadEvent); + + assertThat(qm.getAllComponents(project)).isNotEmpty(); + Component component = qm.getAllComponents().getFirst(); + assertThat(component.getAuthor()).isEqualTo("Joane Doe et al."); + assertThat(component.getAuthors().get(0).getName()).isEqualTo("Joane Doe et al."); + assertThat(component.getAuthors().size()).isEqualTo(1); + } + } + private void awaitBomProcessedNotification(final BomUploadEvent bomUploadEvent) { try { await("BOM Processed Notification") diff --git a/src/test/resources/unit/bom-issue3936-author.json b/src/test/resources/unit/bom-issue3936-author.json new file mode 100644 index 0000000000..a3209fb989 --- /dev/null +++ b/src/test/resources/unit/bom-issue3936-author.json @@ -0,0 +1,28 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "serialNumber": "urn:uuid:23d6e70a-4502-4da8-b196-efb41664c7fe", + "version": 1, + "metadata": { + "timestamp": "2023-12-13T13:21:58+01:00", + "component": { + "type": "firmware" + } + }, + "components": [ + { + "name": "alsa-utils", + "version": "1.2.1.2", + "author" : "Joane Doe et al.", + "licenses": [ + { + "license": { + "id": "GPL-2.0" + } + } + ], + "cpe": "cpe:2.3:*:alsa-project:alsa:1.2.1.2:*:*:*:*:*:*:*", + "type": "application" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/unit/bom-issue3936-authors.json b/src/test/resources/unit/bom-issue3936-authors.json new file mode 100644 index 0000000000..a50a318043 --- /dev/null +++ b/src/test/resources/unit/bom-issue3936-authors.json @@ -0,0 +1,32 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:23d6e70a-4502-4da8-b196-efb41664c7fe", + "version": 1, + "metadata": { + "timestamp": "2023-12-13T13:21:58+01:00", + "component": { + "type": "firmware" + } + }, + "components": [ + { + "name": "alsa-utils", + "version": "1.2.1.2", + "authors" : [ + { + "name" : "Joane Doe et al." + } + ], + "licenses": [ + { + "license": { + "id": "GPL-2.0" + } + } + ], + "cpe": "cpe:2.3:*:alsa-project:alsa:1.2.1.2:*:*:*:*:*:*:*", + "type": "application" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/unit/bom-issue3936-both.json b/src/test/resources/unit/bom-issue3936-both.json new file mode 100644 index 0000000000..3a0a234b3d --- /dev/null +++ b/src/test/resources/unit/bom-issue3936-both.json @@ -0,0 +1,33 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:23d6e70a-4502-4da8-b196-efb41664c7fe", + "version": 1, + "metadata": { + "timestamp": "2023-12-13T13:21:58+01:00", + "component": { + "type": "firmware" + } + }, + "components": [ + { + "name": "alsa-utils", + "version": "1.2.1.2", + "authors" : [ + { + "name" : "Joane Doe et al." + } + ], + "author" : "Joane Doe et al.", + "licenses": [ + { + "license": { + "id": "GPL-2.0" + } + } + ], + "cpe": "cpe:2.3:*:alsa-project:alsa:1.2.1.2:*:*:*:*:*:*:*", + "type": "application" + } + ] +} \ No newline at end of file From c8aa0030d86ecbb373bc2550829da90e385fc578 Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Thu, 4 Jul 2024 19:03:30 +0200 Subject: [PATCH 054/429] Make exported VEXs and BOMs valid CycloneDX Deduplicate found vulnerabilities and accumulate affected components per vulnerabiltiy. Signed-off-by: Kirill.Sybin --- .../parser/cyclonedx/CycloneDXExporter.java | 3 +- .../parser/cyclonedx/util/ModelConverter.java | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/dependencytrack/parser/cyclonedx/CycloneDXExporter.java b/src/main/java/org/dependencytrack/parser/cyclonedx/CycloneDXExporter.java index 05b88a68ca..09098c7cf8 100644 --- a/src/main/java/org/dependencytrack/parser/cyclonedx/CycloneDXExporter.java +++ b/src/main/java/org/dependencytrack/parser/cyclonedx/CycloneDXExporter.java @@ -80,14 +80,13 @@ private Bom create(List components, final List serv } final List cycloneComponents = (Variant.VEX != variant && components != null) ? components.stream().map(component -> ModelConverter.convert(qm, component)).collect(Collectors.toList()) : null; final List cycloneServices = (Variant.VEX != variant && services != null) ? services.stream().map(service -> ModelConverter.convert(qm, service)).collect(Collectors.toList()) : null; - final List cycloneVulnerabilities = (findings != null) ? findings.stream().map(finding -> ModelConverter.convert(qm, variant, finding)).collect(Collectors.toList()) : null; final Bom bom = new Bom(); bom.setSerialNumber("urn:uuid:" + UUID.randomUUID()); bom.setVersion(1); bom.setMetadata(ModelConverter.createMetadata(project)); bom.setComponents(cycloneComponents); bom.setServices(cycloneServices); - bom.setVulnerabilities(cycloneVulnerabilities); + bom.setVulnerabilities(ModelConverter.generateVulnerabilities(qm, variant, findings)); if (cycloneComponents != null) { bom.setDependencies(ModelConverter.generateDependencies(project, components)); } diff --git a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java index 9f275ec165..5454341755 100644 --- a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java +++ b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java @@ -67,6 +67,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -904,6 +905,34 @@ public static org.cyclonedx.model.vulnerability.Vulnerability convert(final Quer return cdxVulnerability; } + public static List generateVulnerabilities(final QueryManager qm, final CycloneDXExporter.Variant variant, + final List findings) { + if (findings == null) { + return Collections.emptyList(); + } + final var vulnerabilitiesToAffectRefs = new HashMap>(); + final var affectRefsToAffects = new HashMap(); + final List cycloneVulnerabilities = findings.stream() + .map(finding -> convert(qm, variant, finding)) + .filter(vulnerability -> { + var affect = vulnerability.getAffects().getFirst(); + var vulnerabilitySeen = vulnerabilitiesToAffectRefs.containsKey(vulnerability.getId()); + if (vulnerabilitySeen){ + vulnerabilitiesToAffectRefs.get(vulnerability.getId()).add(affect.getRef()); + } else { + vulnerabilitiesToAffectRefs.put(vulnerability.getId(), new HashSet<>(Arrays.asList(affect.getRef()))); + } + + affectRefsToAffects.put(affect.getRef(), affect); + return vulnerabilitySeen; + }) + .toList(); + cycloneVulnerabilities + .forEach(vulnerability -> vulnerability.setAffects(vulnerabilitiesToAffectRefs.get(vulnerability.getId()).stream().map(affectRefsToAffects::get).toList())); + + return cycloneVulnerabilities; + } + /** * Converts {@link Project#getDirectDependencies()} and {@link Component#getDirectDependencies()} * references to a CycloneDX dependency graph. From 94e9fa853cdb64c991244b28c9f769dd30e077d7 Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Mon, 8 Jul 2024 22:00:10 +0200 Subject: [PATCH 055/429] Add test Signed-off-by: Kirill.Sybin --- .../cyclonedx/CycloneDXExporterTest.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/test/java/org/dependencytrack/parser/cyclonedx/CycloneDXExporterTest.java diff --git a/src/test/java/org/dependencytrack/parser/cyclonedx/CycloneDXExporterTest.java b/src/test/java/org/dependencytrack/parser/cyclonedx/CycloneDXExporterTest.java new file mode 100644 index 0000000000..759e386818 --- /dev/null +++ b/src/test/java/org/dependencytrack/parser/cyclonedx/CycloneDXExporterTest.java @@ -0,0 +1,50 @@ +package org.dependencytrack.parser.cyclonedx; + +import org.cyclonedx.exception.GeneratorException; +import org.dependencytrack.PersistenceCapableTest; +import org.dependencytrack.model.Component; +import org.dependencytrack.model.Severity; +import org.dependencytrack.model.Vulnerability; +import org.dependencytrack.tasks.scanners.AnalyzerIdentity; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThatNoException; + +public class CycloneDXExporterTest extends PersistenceCapableTest { + + @Test // https://github.com/DependencyTrack/dependency-track/issues/3834 + public void testSchemaValidityOfExportedVexWithMultipleComponentsWithSameVulnerability() throws GeneratorException { + + var exporter = new CycloneDXExporter(CycloneDXExporter.Variant.VEX, qm); + + var project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); + + var componentAWithTheVulnerability = new Component(); + componentAWithTheVulnerability.setProject(project); + componentAWithTheVulnerability.setName("Acme Component"); + componentAWithTheVulnerability.setVersion("1.0"); + componentAWithTheVulnerability = qm.createComponent(componentAWithTheVulnerability, false); + + var componentBWithTheVulnerability = new Component(); + componentBWithTheVulnerability.setProject(project); + componentBWithTheVulnerability.setName("Acme Component"); + componentBWithTheVulnerability.setVersion("1.1"); + componentBWithTheVulnerability = qm.createComponent(componentBWithTheVulnerability, false); + + var vulnerability = new Vulnerability(); + vulnerability.setVulnId("CVE-2024-29041"); + vulnerability.setSource(Vulnerability.Source.NVD); + vulnerability.setSeverity(Severity.HIGH); + vulnerability = qm.createVulnerability(vulnerability, false); + qm.addVulnerability(vulnerability, componentAWithTheVulnerability, AnalyzerIdentity.NONE); + qm.addVulnerability(vulnerability, componentBWithTheVulnerability, AnalyzerIdentity.NONE); + + var vexBytes = exporter.export(exporter.create(project), CycloneDXExporter.Format.JSON).getBytes(); + + var validator = new CycloneDxValidator(); + + assertThatNoException() + .isThrownBy(() -> validator.validate(vexBytes)); + } +} \ No newline at end of file From 84fa36690eaed1cf082a7c9a5201b7b1d37710a6 Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Mon, 22 Jul 2024 22:38:32 +0200 Subject: [PATCH 056/429] Use full Vulnerability object to check for dupes Use all fields of Vulnerability objects, except for `affects`, for deduplicated. Requires https://github.com/CycloneDX/cyclonedx-core-java/issues/463. Signed-off-by: Kirill.Sybin --- .../parser/cyclonedx/util/ModelConverter.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java index 5454341755..63c2e978e1 100644 --- a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java +++ b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java @@ -910,26 +910,26 @@ public static List generateVuln if (findings == null) { return Collections.emptyList(); } - final var vulnerabilitiesToAffectRefs = new HashMap>(); - final var affectRefsToAffects = new HashMap(); + final var vulnerabilitiesToAffects = new HashMap>(); final List cycloneVulnerabilities = findings.stream() .map(finding -> convert(qm, variant, finding)) .filter(vulnerability -> { var affect = vulnerability.getAffects().getFirst(); - var vulnerabilitySeen = vulnerabilitiesToAffectRefs.containsKey(vulnerability.getId()); + vulnerability.setAffects(new ArrayList<>()); + var vulnerabilitySeen = vulnerabilitiesToAffects.containsKey(vulnerability); if (vulnerabilitySeen){ - vulnerabilitiesToAffectRefs.get(vulnerability.getId()).add(affect.getRef()); + vulnerabilitiesToAffects.get(vulnerability).add(affect); } else { - vulnerabilitiesToAffectRefs.put(vulnerability.getId(), new HashSet<>(Arrays.asList(affect.getRef()))); + vulnerabilitiesToAffects.put(vulnerability, new HashSet<>(Arrays.asList(affect))); } - affectRefsToAffects.put(affect.getRef(), affect); - return vulnerabilitySeen; + return !vulnerabilitySeen; }) .toList(); cycloneVulnerabilities - .forEach(vulnerability -> vulnerability.setAffects(vulnerabilitiesToAffectRefs.get(vulnerability.getId()).stream().map(affectRefsToAffects::get).toList())); - + .forEach(vulnerability -> { + vulnerability.setAffects(new ArrayList<>(vulnerabilitiesToAffects.get(vulnerability))); + }); return cycloneVulnerabilities; } From c1244fcb89f6953a2cc6f8cc91cb81231720fecf Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Tue, 23 Jul 2024 22:40:41 +0200 Subject: [PATCH 057/429] Simplify Vulnerability deduplication logic Implement CycloneDX schema 1.5's definition of uniqueness of the vulnerability field literally and have affects contribute to a vulnerability's uniquenss too, and do not accumulate affects if other all other fields of multiple vulnerability objects are the same. Signed-off-by: Kirill.Sybin --- .../parser/cyclonedx/util/ModelConverter.java | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java index 63c2e978e1..e7f2ba02ed 100644 --- a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java +++ b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java @@ -910,27 +910,11 @@ public static List generateVuln if (findings == null) { return Collections.emptyList(); } - final var vulnerabilitiesToAffects = new HashMap>(); - final List cycloneVulnerabilities = findings.stream() + final var vulnerabilitiesSeen = new HashSet(); + return findings.stream() .map(finding -> convert(qm, variant, finding)) - .filter(vulnerability -> { - var affect = vulnerability.getAffects().getFirst(); - vulnerability.setAffects(new ArrayList<>()); - var vulnerabilitySeen = vulnerabilitiesToAffects.containsKey(vulnerability); - if (vulnerabilitySeen){ - vulnerabilitiesToAffects.get(vulnerability).add(affect); - } else { - vulnerabilitiesToAffects.put(vulnerability, new HashSet<>(Arrays.asList(affect))); - } - - return !vulnerabilitySeen; - }) + .filter(vulnerabilitiesSeen::add) .toList(); - cycloneVulnerabilities - .forEach(vulnerability -> { - vulnerability.setAffects(new ArrayList<>(vulnerabilitiesToAffects.get(vulnerability))); - }); - return cycloneVulnerabilities; } /** From 6b7c18f9f34ea71a3b803f2683b5b5dbfdca6909 Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Sat, 27 Jul 2024 19:13:12 +0200 Subject: [PATCH 058/429] Move VEX export validity tests to VexResourceTest As per change request, moving, reworking and enhancing the validity test. It is moved to VexResourceTest. The rework consists of asserting the VEX export Response instead of the validity of the exported BOM object, as the new way checks for the original issue right where it is produced from the user perspective, while the old checked where the original issue was noticed by the user as well as being susceptible to errors in the validate method as well. The enhancement consists of testing with vulnerabilities with differing objects such as differing analysis of the same vuln id on different components, thereby testing the deduplication of the entire Vulnerability object. Signed-off-by: Kirill.Sybin --- .../parser/cyclonedx/util/ModelConverter.java | 1 - .../cyclonedx/CycloneDXExporterTest.java | 50 ---- .../resources/v1/VexResourceTest.java | 221 ++++++++++++++++++ 3 files changed, 221 insertions(+), 51 deletions(-) delete mode 100644 src/test/java/org/dependencytrack/parser/cyclonedx/CycloneDXExporterTest.java diff --git a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java index e7f2ba02ed..6cff72fbd5 100644 --- a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java +++ b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java @@ -67,7 +67,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.Optional; diff --git a/src/test/java/org/dependencytrack/parser/cyclonedx/CycloneDXExporterTest.java b/src/test/java/org/dependencytrack/parser/cyclonedx/CycloneDXExporterTest.java deleted file mode 100644 index 759e386818..0000000000 --- a/src/test/java/org/dependencytrack/parser/cyclonedx/CycloneDXExporterTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.dependencytrack.parser.cyclonedx; - -import org.cyclonedx.exception.GeneratorException; -import org.dependencytrack.PersistenceCapableTest; -import org.dependencytrack.model.Component; -import org.dependencytrack.model.Severity; -import org.dependencytrack.model.Vulnerability; -import org.dependencytrack.tasks.scanners.AnalyzerIdentity; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThatNoException; - -public class CycloneDXExporterTest extends PersistenceCapableTest { - - @Test // https://github.com/DependencyTrack/dependency-track/issues/3834 - public void testSchemaValidityOfExportedVexWithMultipleComponentsWithSameVulnerability() throws GeneratorException { - - var exporter = new CycloneDXExporter(CycloneDXExporter.Variant.VEX, qm); - - var project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); - - var componentAWithTheVulnerability = new Component(); - componentAWithTheVulnerability.setProject(project); - componentAWithTheVulnerability.setName("Acme Component"); - componentAWithTheVulnerability.setVersion("1.0"); - componentAWithTheVulnerability = qm.createComponent(componentAWithTheVulnerability, false); - - var componentBWithTheVulnerability = new Component(); - componentBWithTheVulnerability.setProject(project); - componentBWithTheVulnerability.setName("Acme Component"); - componentBWithTheVulnerability.setVersion("1.1"); - componentBWithTheVulnerability = qm.createComponent(componentBWithTheVulnerability, false); - - var vulnerability = new Vulnerability(); - vulnerability.setVulnId("CVE-2024-29041"); - vulnerability.setSource(Vulnerability.Source.NVD); - vulnerability.setSeverity(Severity.HIGH); - vulnerability = qm.createVulnerability(vulnerability, false); - qm.addVulnerability(vulnerability, componentAWithTheVulnerability, AnalyzerIdentity.NONE); - qm.addVulnerability(vulnerability, componentBWithTheVulnerability, AnalyzerIdentity.NONE); - - var vexBytes = exporter.export(exporter.create(project), CycloneDXExporter.Format.JSON).getBytes(); - - var validator = new CycloneDxValidator(); - - assertThatNoException() - .isThrownBy(() -> validator.validate(vexBytes)); - } -} \ No newline at end of file diff --git a/src/test/java/org/dependencytrack/resources/v1/VexResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/VexResourceTest.java index bd158c0140..ee47bde480 100644 --- a/src/test/java/org/dependencytrack/resources/v1/VexResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/VexResourceTest.java @@ -212,6 +212,227 @@ public void exportProjectAsCycloneDxTest() { """); } + @Test + public void exportVexWithSameVulnAnalysisValidJsonTest() { + var project = new Project(); + project.setName("acme-app"); + project.setVersion("1.0.0"); + project.setClassifier(Classifier.APPLICATION); + qm.persist(project); + + var componentAWithVuln = new Component(); + componentAWithVuln.setProject(project); + componentAWithVuln.setName("acme-lib-a"); + componentAWithVuln.setVersion("1.0.0"); + componentAWithVuln = qm.createComponent(componentAWithVuln, false); + + var componentBWithVuln = new Component(); + componentBWithVuln.setProject(project); + componentBWithVuln.setName("acme-lib-b"); + componentBWithVuln.setVersion("1.0.0"); + componentBWithVuln = qm.createComponent(componentBWithVuln, false); + + var vuln = new Vulnerability(); + vuln.setVulnId("INT-001"); + vuln.setSource(Vulnerability.Source.INTERNAL); + vuln.setSeverity(Severity.HIGH); + vuln = qm.createVulnerability(vuln, false); + qm.addVulnerability(vuln, componentAWithVuln, AnalyzerIdentity.NONE); + qm.makeAnalysis(componentAWithVuln, vuln, AnalysisState.RESOLVED, null, AnalysisResponse.UPDATE, null, true); + qm.addVulnerability(vuln, componentBWithVuln, AnalyzerIdentity.NONE); + qm.makeAnalysis(componentBWithVuln, vuln, AnalysisState.RESOLVED, null, AnalysisResponse.UPDATE, null, true); + + qm.persist(project); + + final Response response = jersey.target("%s/cyclonedx/project/%s".formatted(V1_VEX, project.getUuid())) + .request() + .header(X_API_KEY, apiKey) + .get(Response.class); + assertThat(response.getStatus()).isEqualTo(200); + final String jsonResponse = getPlainTextBody(response); + assertThatNoException().isThrownBy(() -> CycloneDxValidator.getInstance().validate(jsonResponse.getBytes())); + assertThatJson(jsonResponse) + .withMatcher("vulnUuid", equalTo(vuln.getUuid().toString())) + .withMatcher("projectUuid", equalTo(project.getUuid().toString())) + .isEqualTo(""" + { + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "serialNumber": "${json-unit.any-string}", + "version": 1, + "metadata": { + "timestamp": "${json-unit.any-string}", + "component": { + "type": "application", + "bom-ref": "${json-unit.matches:projectUuid}", + "name": "acme-app", + "version": "1.0.0" + }, + "tools": [ + { + "vendor": "OWASP", + "name": "Dependency-Track", + "version": "${json-unit.any-string}" + } + ] + }, + "vulnerabilities": [ + { + "bom-ref": "${json-unit.matches:vulnUuid}", + "id": "INT-001", + "source": { + "name": "INTERNAL" + }, + "ratings": [ + { + "source": { + "name": "INTERNAL" + }, + "severity": "high", + "method": "other" + } + ], + "analysis":{ + "state": "resolved", + "response": [ + "update" + ] + }, + "affects": [ + { + "ref": "${json-unit.matches:projectUuid}" + } + ] + } + ] + } + """); + } + + @Test + public void exportVexWithDifferentVulnAnalysisValidJsonTest() { + var project = new Project(); + project.setName("acme-app"); + project.setVersion("1.0.0"); + project.setClassifier(Classifier.APPLICATION); + qm.persist(project); + + var componentAWithVuln = new Component(); + componentAWithVuln.setProject(project); + componentAWithVuln.setName("acme-lib-a"); + componentAWithVuln.setVersion("1.0.0"); + componentAWithVuln = qm.createComponent(componentAWithVuln, false); + + var componentBWithVuln = new Component(); + componentBWithVuln.setProject(project); + componentBWithVuln.setName("acme-lib-b"); + componentBWithVuln.setVersion("1.0.0"); + componentBWithVuln = qm.createComponent(componentBWithVuln, false); + + var vuln = new Vulnerability(); + vuln.setVulnId("INT-001"); + vuln.setSource(Vulnerability.Source.INTERNAL); + vuln.setSeverity(Severity.HIGH); + vuln = qm.createVulnerability(vuln, false); + qm.addVulnerability(vuln, componentAWithVuln, AnalyzerIdentity.NONE); + qm.makeAnalysis(componentAWithVuln, vuln, AnalysisState.IN_TRIAGE, null, AnalysisResponse.UPDATE, null, true); + qm.addVulnerability(vuln, componentBWithVuln, AnalyzerIdentity.NONE); + qm.makeAnalysis(componentBWithVuln, vuln, AnalysisState.EXPLOITABLE, null, AnalysisResponse.UPDATE, null, true); + + qm.persist(project); + + final Response response = jersey.target("%s/cyclonedx/project/%s".formatted(V1_VEX, project.getUuid())) + .request() + .header(X_API_KEY, apiKey) + .get(Response.class); + assertThat(response.getStatus()).isEqualTo(200); + final String jsonResponse = getPlainTextBody(response); + assertThatNoException().isThrownBy(() -> CycloneDxValidator.getInstance().validate(jsonResponse.getBytes())); + assertThatJson(jsonResponse) + .withMatcher("vulnUuid", equalTo(vuln.getUuid().toString())) + .withMatcher("projectUuid", equalTo(project.getUuid().toString())) + .isEqualTo(""" + { + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "serialNumber": "${json-unit.any-string}", + "version": 1, + "metadata": { + "timestamp": "${json-unit.any-string}", + "component": { + "type": "application", + "bom-ref": "${json-unit.matches:projectUuid}", + "name": "acme-app", + "version": "1.0.0" + }, + "tools": [ + { + "vendor": "OWASP", + "name": "Dependency-Track", + "version": "${json-unit.any-string}" + } + ] + }, + "vulnerabilities": [ + { + "bom-ref": "${json-unit.matches:vulnUuid}", + "id": "INT-001", + "source": { + "name": "INTERNAL" + }, + "ratings": [ + { + "source": { + "name": "INTERNAL" + }, + "severity": "high", + "method": "other" + } + ], + "analysis":{ + "state": "in_triage", + "response": [ + "update" + ] + }, + "affects": [ + { + "ref": "${json-unit.matches:projectUuid}" + } + ] + }, + { + "bom-ref": "${json-unit.matches:vulnUuid}", + "id": "INT-001", + "source": { + "name": "INTERNAL" + }, + "ratings": [ + { + "source": { + "name": "INTERNAL" + }, + "severity": "high", + "method": "other" + } + ], + "analysis":{ + "state": "exploitable", + "response": [ + "update" + ] + }, + "affects": [ + { + "ref": "${json-unit.matches:projectUuid}" + } + ] + } + ] + } + """); + } + @Test public void uploadVexInvalidJsonTest() { initializeWithPermissions(Permissions.BOM_UPLOAD); From 3881bd71a46e676b09c4e51fbfa5ce342da1aed6 Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Wed, 7 Aug 2024 23:25:36 +0200 Subject: [PATCH 059/429] Bump org.cyclonedx.cyclonedx-core-java Bump library to use the version containing the required enchangement https://github.com/CycloneDX/cyclonedx-core-java/issues/463 Signed-off-by: Kirill.Sybin --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 405fed913f..10a14435b5 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ 1.12.0 1.4.2 1.0.1 - 9.0.4 + 9.0.5 2.0.1 2.17.1 2.17.1 From 9957cc1bea594b52b078f7c646876420397ebe23 Mon Sep 17 00:00:00 2001 From: nscuro Date: Thu, 8 Aug 2024 14:57:37 +0200 Subject: [PATCH 060/429] Port regression test for `parent` field occasionally missing in `/api/v1/project/{uuid}` responses Note, the underlying issue is already fixed in v4.12 / Alpine v3. Refer to https://github.com/DependencyTrack/dependency-track/issues/4048#issuecomment-2275716424 for details. Relates to #4048 Signed-off-by: nscuro --- .../resources/v1/ProjectResourceTest.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java index 98aa353ca7..acce124c10 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java @@ -26,6 +26,7 @@ import jakarta.json.Json; import jakarta.json.JsonArray; import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; import jakarta.ws.rs.HttpMethod; import jakarta.ws.rs.client.Entity; import jakarta.ws.rs.core.MediaType; @@ -61,7 +62,9 @@ import java.time.Duration; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -1245,4 +1248,76 @@ public void issue3883RegressionTest() { """); } + @Test // https://github.com/DependencyTrack/dependency-track/issues/4048 + public void issue4048RegressionTest() { + final int projectsPerLevel = 10; + final int maxDepth = 5; + + final Map> projectUuidsByLevel = new HashMap<>(); + + // Create multiple parent-child hierarchies of projects. + for (int i = 0; i < maxDepth; i++) { + final List parentUuids = projectUuidsByLevel.get(i - 1); + + for (int j = 0; j < projectsPerLevel; j++) { + final UUID parentUuid = i > 0 ? parentUuids.get(j) : null; + + final JsonObjectBuilder requestBodyBuilder = Json.createObjectBuilder() + .add("name", "project-%d-%d".formatted(i, j)) + .add("version", "%d.%d".formatted(i, j)); + if (parentUuid != null) { + requestBodyBuilder.add("parent", Json.createObjectBuilder() + .add("uuid", parentUuid.toString())); + } + + final Response response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .put(Entity.json(requestBodyBuilder.build().toString())); + assertThat(response.getStatus()).isEqualTo(201); + final JsonObject jsonResponse = parseJsonObject(response); + + projectUuidsByLevel.compute(i, (ignored, uuids) -> { + final UUID uuid = UUID.fromString(jsonResponse.getString("uuid")); + if (uuids == null) { + return new ArrayList<>(List.of(uuid)); + } + + uuids.add(uuid); + return uuids; + }); + } + } + + // Pick out the UUIDs of projects that should have a parent (i.e. level 1 or above). + final List childUuids = projectUuidsByLevel.entrySet().stream() + .filter(entry -> entry.getKey() > 0) + .map(Map.Entry::getValue) + .flatMap(List::stream) + .toList(); + + // Create a [uuid -> level] mapping for better assertion failure reporting. + final Map levelByChildUuid = projectUuidsByLevel.entrySet().stream() + .filter(entry -> entry.getKey() > 0) + .flatMap(entry -> { + final Integer level = entry.getKey(); + return entry.getValue().stream().map(uuid -> Map.entry(uuid, level)); + }) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + // Request all child projects individually. + // Ensure that the parent field is populated for all of them. + for (final UUID uuid : childUuids) { + final Response response = jersey.target(V1_PROJECT + "/" + uuid) + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(200); + final JsonObject json = parseJsonObject(response); + assertThat(json.getJsonObject("parent")) + .withFailMessage("Parent missing on level: " + levelByChildUuid.get(uuid)) + .isNotEmpty(); + } + } + } From 180c755a567c9c9b86995c7dccaebdcd28d35544 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 08:09:22 +0000 Subject: [PATCH 061/429] Bump org.apache.commons:commons-compress from 1.26.2 to 1.27.0 Bumps org.apache.commons:commons-compress from 1.26.2 to 1.27.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-compress dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 405fed913f..d4ea0dc12e 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ 1.18.0 1.19.1 2.1.0 - 1.26.2 + 1.27.0 1.12.0 1.4.2 1.0.1 From 9da64e0d6c170712680dbd9c4afc3e9d7a3d5744 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 08:10:04 +0000 Subject: [PATCH 062/429] Bump org.slf4j:log4j-over-slf4j from 2.0.14 to 2.0.15 Bumps org.slf4j:log4j-over-slf4j from 2.0.14 to 2.0.15. --- updated-dependencies: - dependency-name: org.slf4j:log4j-over-slf4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 405fed913f..526f9629ad 100644 --- a/pom.xml +++ b/pom.xml @@ -121,7 +121,7 @@ 2.1.1 4.5.14 5.3.1 - 2.0.14 + 2.0.15 1.323 12.8.0.jre11 From df55a0e5a9cc9c53d38dda2eba2bf352236459bc Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Fri, 9 Aug 2024 20:38:43 +0200 Subject: [PATCH 063/429] Update expected schema validator messages in tests Adjust expected json schema validator error messages after the library producing them got bumped as part of the bump of org.cyclonedx.cyclonedx-core-java. Signed-off-by: Kirill.Sybin --- .../java/org/dependencytrack/resources/v1/BomResourceTest.java | 2 +- .../java/org/dependencytrack/resources/v1/VexResourceTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java index c867eb32dc..d9e04f9819 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java @@ -1056,7 +1056,7 @@ public void uploadBomInvalidJsonTest() { "title": "The uploaded BOM is invalid", "detail": "Schema validation failed", "errors": [ - "$.components[0].type: does not have a value in the enumeration [application, framework, library, container, operating-system, device, firmware, file]" + "$.components[0].type: does not have a value in the enumeration [\\"application\\", \\"framework\\", \\"library\\", \\"container\\", \\"operating-system\\", \\"device\\", \\"firmware\\", \\"file\\"]" ] } """); diff --git a/src/test/java/org/dependencytrack/resources/v1/VexResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/VexResourceTest.java index ee47bde480..fbe8eead8a 100644 --- a/src/test/java/org/dependencytrack/resources/v1/VexResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/VexResourceTest.java @@ -483,7 +483,7 @@ public void uploadVexInvalidJsonTest() { "title": "The uploaded BOM is invalid", "detail": "Schema validation failed", "errors": [ - "$.components[0].type: does not have a value in the enumeration [application, framework, library, container, operating-system, device, firmware, file]" + "$.components[0].type: does not have a value in the enumeration [\\"application\\", \\"framework\\", \\"library\\", \\"container\\", \\"operating-system\\", \\"device\\", \\"firmware\\", \\"file\\"]" ] } """); From c7b8b4e69a4fcd0a056a0587089fdeb3bc547ed6 Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Fri, 9 Aug 2024 23:14:53 +0200 Subject: [PATCH 064/429] Update expected schema validator message in test Adjust another expected message missed in the previous commit. Signed-off-by: Kirill.Sybin --- .../parser/cyclonedx/CycloneDxValidatorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/dependencytrack/parser/cyclonedx/CycloneDxValidatorTest.java b/src/test/java/org/dependencytrack/parser/cyclonedx/CycloneDxValidatorTest.java index 1e2e439163..9ad3ab5cf5 100644 --- a/src/test/java/org/dependencytrack/parser/cyclonedx/CycloneDxValidatorTest.java +++ b/src/test/java/org/dependencytrack/parser/cyclonedx/CycloneDxValidatorTest.java @@ -153,7 +153,7 @@ public void testValidateJsonWithInvalidComponentType() { .extracting(InvalidBomException::getValidationErrors).asList() .containsExactly(""" $.components[0].type: does not have a value in the enumeration \ - [application, framework, library, container, operating-system, device, firmware, file]\ + ["application", "framework", "library", "container", "operating-system", "device", "firmware", "file"]\ """); } From 4bc536b705a360634f305466eb4889b5500498e1 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sat, 10 Aug 2024 18:52:16 +0200 Subject: [PATCH 065/429] Bump Temurin base image to `21.0.4_7` Signed-off-by: nscuro --- src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile index 09644f54b5..f6817bddd3 100644 --- a/src/main/docker/Dockerfile +++ b/src/main/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM eclipse-temurin:21.0.3_9-jre-jammy@sha256:a56ee1f79cf57b2b31152cd471a4c85b6deb3057e4a1fbe8e50b57e7d2a1d7c9 AS jre-build +FROM eclipse-temurin:21.0.4_7-jre-jammy@sha256:870aae69d4521fdaf26e952f8026f75b37cb721e6302d4d4d7100f6b09823057 AS jre-build FROM debian:stable-slim@sha256:57bd74e95092e6d4c0cdb6e36ca3db5bb828c2f592788734d1a707a4b92e7755 From b3fce5a19c07d72d56957dd412b4100f8cf074b3 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sat, 10 Aug 2024 19:27:46 +0200 Subject: [PATCH 066/429] Bump bundled frontend to v4.11.6 Signed-off-by: nscuro --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 55f2b26380..359472fa71 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ 21 - 4.11.5 + 4.11.6 ${project.parent.version} 4.2.1 0.1.2 From dca7a5691098f02597bc880a8c99e628ec74642a Mon Sep 17 00:00:00 2001 From: Niklas Date: Sat, 10 Aug 2024 22:13:49 +0200 Subject: [PATCH 067/429] Add changelog for v4.11.6 Signed-off-by: Niklas --- docs/_posts/2024-08-10-v4.11.6.md | 70 +++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 docs/_posts/2024-08-10-v4.11.6.md diff --git a/docs/_posts/2024-08-10-v4.11.6.md b/docs/_posts/2024-08-10-v4.11.6.md new file mode 100644 index 0000000000..f8d1fa90ba --- /dev/null +++ b/docs/_posts/2024-08-10-v4.11.6.md @@ -0,0 +1,70 @@ +--- +title: v4.11.6 +type: patch +--- + +**Enhancements:** + +* Improve French translation - [frontend/#964] + +**Fixes:** + +* Handle breaking change in Trivy v0.54.0 server API - [apiserver/#4040] +* Fix validation error when XML BOM declares multiple namespaces - [apiserver/#4041] +* Fix `JDOUserException` when multiple licenses match a component's license name - [apiserver/#4042] +* Fix anchors in changelog documentation - [apiserver/#4043] +* Fix project link for new vulnerable dependency in email notifications - [apiserver/#4044] +* Fix `parent` field occasionally missing in `/api/v1/project/{uuid}` responses - [apiserver/#4049] +* Fix VEX export returning invalid CycloneDX - [apiserver/#4054] + +For a complete list of changes, refer to the respective GitHub milestones: + +* [API server milestone 4.11.6](https://github.com/DependencyTrack/dependency-track/milestone/43?closed=1) +* [Frontend milestone 4.11.6](https://github.com/DependencyTrack/frontend/milestone/28?closed=1) + +We thank all organizations and individuals who contributed to this release, from logging issues to taking part in discussions on GitHub & Slack to testing of fixes. + +Special thanks to everyone who contributed code to implement enhancements and fix defects: +[@2000rosser], [@JCHacking], [@SaberStrat], [@molusk], [@philippn] + +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.6/dependency-track-apiserver.jar) + +| Algorithm | Checksum | +|:----------|:-----------------------------------------------------------------| +| SHA-1 | daab7ed5b760ff909e4b9cc041b89c3374c1d955 | +| SHA-256 | a76cc3417728bdc880f41af613e543d3e5f033d7b0b1db84ffb397bcbcb3936b | + +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.6/dependency-track-bundled.jar) + +| Algorithm | Checksum | +|:----------|:-----------------------------------------------------------------| +| SHA-1 | 8ff2bd4db69e7083d501a4c489f703677044a5f0 | +| SHA-256 | fd1c25e2b2d727f377eeec8240370558a9796225fe4dc0f258021b1061fbc36f | + +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.11.6/frontend-dist.zip) + +| Algorithm | Checksum | +|:----------|:-----------------------------------------------------------------| +| SHA-1 | c91bede201957c994f338a043a44ebd32824319e | +| SHA-256 | 55ea0735b80c8cc17d31590ba16c3650943a3cdb595accf3540fefd1670ee1b9 | + +###### Software Bill of Materials (SBOM) + +* API Server: [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.6/bom.json) +* Frontend: [bom.json](https://github.com/DependencyTrack/frontend/releases/download/4.11.6/bom.json) + +[apiserver/#4040]: https://github.com/DependencyTrack/dependency-track/pull/4040 +[apiserver/#4041]: https://github.com/DependencyTrack/dependency-track/pull/4041 +[apiserver/#4042]: https://github.com/DependencyTrack/dependency-track/pull/4042 +[apiserver/#4043]: https://github.com/DependencyTrack/dependency-track/pull/4043 +[apiserver/#4044]: https://github.com/DependencyTrack/dependency-track/pull/4044 +[apiserver/#4049]: https://github.com/DependencyTrack/dependency-track/pull/4049 +[apiserver/#4054]: https://github.com/DependencyTrack/dependency-track/pull/4054 + +[frontend/#964]: https://github.com/DependencyTrack/frontend/pull/964 + +[@2000rosser]: https://github.com/2000rosser +[@JCHacking]: https://github.com/JCHacking +[@SaberStrat]: https://github.com/SaberStrat +[@molusk]: https://github.com/molusk +[@philippn]: https://github.com/philippn From 908470a8ad387e1613a54297d65496d382037142 Mon Sep 17 00:00:00 2001 From: Niklas Date: Sat, 10 Aug 2024 22:14:24 +0200 Subject: [PATCH 068/429] Update versions in issue template for defects Signed-off-by: Niklas --- .github/ISSUE_TEMPLATE/defect-report.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/defect-report.yml b/.github/ISSUE_TEMPLATE/defect-report.yml index 1a6d650baa..74f805a909 100644 --- a/.github/ISSUE_TEMPLATE/defect-report.yml +++ b/.github/ISSUE_TEMPLATE/defect-report.yml @@ -70,6 +70,7 @@ body: - 4.11.3 - 4.11.4 - 4.11.5 + - 4.11.6 - 4.12.0-SNAPSHOT validations: required: true From 4db5ed3845346fa654106fbc742793113b134eef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 08:02:17 +0000 Subject: [PATCH 069/429] Bump actions/setup-java from 4.2.1 to 4.2.2 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.2.1 to 4.2.2. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/99b8673ff64fbf99d8d325f52d9a5bdedb8483e9...6a0805fcefea3d4657a47ac4c165951e33482018) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- .github/workflows/ci-release.yaml | 2 +- .github/workflows/ci-test.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 2980d03d25..9df1e60c5f 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -31,7 +31,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 - name: Set up JDK - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # tag=v4.2.1 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # tag=v4.2.2 with: distribution: 'temurin' java-version: '21' diff --git a/.github/workflows/ci-release.yaml b/.github/workflows/ci-release.yaml index 7e51ae0764..0c25acf37f 100644 --- a/.github/workflows/ci-release.yaml +++ b/.github/workflows/ci-release.yaml @@ -54,7 +54,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 - name: Set up JDK - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # tag=v4.2.1 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # tag=v4.2.2 with: distribution: 'temurin' java-version: '21' diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml index d1eba75fdf..66f354902a 100644 --- a/.github/workflows/ci-test.yaml +++ b/.github/workflows/ci-test.yaml @@ -36,7 +36,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 - name: Set up JDK - uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # tag=v4.2.1 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # tag=v4.2.2 with: distribution: 'temurin' java-version: '21' From 5609f14c1c1b0929890912712e5c479930086391 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 08:02:22 +0000 Subject: [PATCH 070/429] Bump actions/upload-artifact from 4.3.5 to 4.3.6 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.5 to 4.3.6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/89ef406dd8d7e03cfd12d9e0a4a378f454709029...834a144ee995460fba8ed112a2fc961b36a5ec5a) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- .github/workflows/ci-test.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 2980d03d25..89daa8bdac 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -55,7 +55,7 @@ jobs: mvn -B --no-transfer-progress cyclonedx:makeBom -Dservices.bom.merge.skip=false org.codehaus.mojo:exec-maven-plugin:exec@merge-services-bom - name: Upload Artifacts - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # tag=v4.3.5 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # tag=v4.3.6 with: name: assembled-wars path: |- diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml index d1eba75fdf..ee9d47378f 100644 --- a/.github/workflows/ci-test.yaml +++ b/.github/workflows/ci-test.yaml @@ -66,7 +66,7 @@ jobs: - name: Upload PR test coverage report if: ${{ github.event_name == 'pull_request' }} - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # tag=v4.3.5 + uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # tag=v4.3.6 with: name: pr-test-coverage-report path: |- From d51d991df62e35c087d460a96900b3f31b99998e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 08:02:27 +0000 Subject: [PATCH 071/429] Bump github/codeql-action from 3.25.15 to 3.26.0 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.15 to 3.26.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/afb54ba388a7dca6ecae48f608c4ff05ff4cc77a...eb055d739abdc2e8de2e5f4ba1a8b246daa779aa) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 2980d03d25..eabd4e746d 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -145,6 +145,6 @@ jobs: - name: Upload Trivy Scan Results to GitHub Security Tab if: ${{ inputs.publish-container }} - uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # tag=v3.25.15 + uses: github/codeql-action/upload-sarif@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # tag=v3.26.0 with: sarif_file: 'trivy-results.sarif' From a90a773c48c8e7258e454d21f229c536042bd161 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 08:02:30 +0000 Subject: [PATCH 072/429] Bump docker/build-push-action from 6.5.0 to 6.6.1 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.5.0 to 6.6.1. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/5176d81f87c23d6fc96624dfdbcd9f3830bbe445...16ebe778df0e7752d2cfcbd924afdbbd89c1a755) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 2980d03d25..4679b55203 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -121,7 +121,7 @@ jobs: echo "tags=${TAGS}" >> $GITHUB_OUTPUT - name: Build multi-arch Container Image - uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 # tag=v6.5.0 + uses: docker/build-push-action@16ebe778df0e7752d2cfcbd924afdbbd89c1a755 # tag=v6.6.1 with: tags: ${{ steps.tags.outputs.tags }} build-args: |- From 233ded86af716c6a8c6628a6917ab144e2f09ec9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 08:33:22 +0000 Subject: [PATCH 073/429] Bump org.slf4j:log4j-over-slf4j from 2.0.15 to 2.0.16 Bumps org.slf4j:log4j-over-slf4j from 2.0.15 to 2.0.16. --- updated-dependencies: - dependency-name: org.slf4j:log4j-over-slf4j dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 359472fa71..73c983eb24 100644 --- a/pom.xml +++ b/pom.xml @@ -121,7 +121,7 @@ 2.1.1 4.5.14 5.3.1 - 2.0.15 + 2.0.16 1.323 12.8.0.jre11 From 79ba79fc3cb2d0c8e39ce06e6fda214a95376e1a Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Mon, 12 Aug 2024 20:44:00 +0100 Subject: [PATCH 074/429] remove unused import Signed-off-by: Ross Murphy --- .../org/dependencytrack/tasks/BomUploadProcessingTaskTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java index 8cdc2ef431..bfdcbb7fa8 100644 --- a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java @@ -71,7 +71,6 @@ import static org.awaitility.Awaitility.await; import static org.dependencytrack.assertion.Assertions.assertConditionWithTimeout; import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_ENABLED; -import static org.junit.Assert.assertEquals; public class BomUploadProcessingTaskTest extends PersistenceCapableTest { From b2d3a546bc6c6c644e0791b98ad53110d0a5c0c0 Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Tue, 13 Aug 2024 00:12:53 +0100 Subject: [PATCH 075/429] add new enpoint to test the notification rule using the uuid Signed-off-by: Ross Murphy --- .../v1/NotificationPublisherResource.java | 107 ++++++++++-------- 1 file changed, 61 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java index 34b3ecbfaf..4c72dd7688 100644 --- a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java @@ -35,29 +35,23 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirements; import io.swagger.v3.oas.annotations.tags.Tag; -import org.apache.commons.text.WordUtils; import org.dependencytrack.auth.Permissions; import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.NotificationPublisher; +import org.dependencytrack.model.NotificationRule; import org.dependencytrack.model.validation.ValidUuid; import org.dependencytrack.notification.NotificationConstants; import org.dependencytrack.notification.NotificationGroup; import org.dependencytrack.notification.NotificationScope; -import org.dependencytrack.notification.publisher.ConsolePublisher; -import org.dependencytrack.notification.publisher.CsWebexPublisher; -import org.dependencytrack.notification.publisher.JiraPublisher; -import org.dependencytrack.notification.publisher.MattermostPublisher; -import org.dependencytrack.notification.publisher.MsTeamsPublisher; import org.dependencytrack.notification.publisher.PublishContext; import org.dependencytrack.notification.publisher.Publisher; import org.dependencytrack.notification.publisher.SendMailPublisher; -import org.dependencytrack.notification.publisher.SlackPublisher; -import org.dependencytrack.notification.publisher.WebhookPublisher; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.util.NotificationUtil; import jakarta.json.Json; import jakarta.json.JsonObject; +import jakarta.json.JsonReader; import jakarta.validation.Validator; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; @@ -71,6 +65,7 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.io.IOException; +import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.util.List; @@ -295,7 +290,7 @@ public Response restoreDefaultTemplates() { } @POST - @Path("/test/{publisher}") + @Path("/test/smtp") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces(MediaType.APPLICATION_JSON) @Operation( @@ -307,55 +302,75 @@ public Response restoreDefaultTemplates() { @ApiResponse(responseCode = "401", description = "Unauthorized") }) @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) - public Response testSlackPublisherConfig(@FormParam("destination") String destination, @PathParam("publisher") String publisherType) { - try(QueryManager qm = new QueryManager()){ - Class defaultPublisherClass; - switch (publisherType) { - case "email": - defaultPublisherClass = SendMailPublisher.class; - break; - case "cisco_webex": - defaultPublisherClass = CsWebexPublisher.class; - break; - case "slack": - defaultPublisherClass = SlackPublisher.class; - break; - case "microsoft_teams": - defaultPublisherClass = MsTeamsPublisher.class; - break; - case "jira": - defaultPublisherClass = JiraPublisher.class; - break; - case "mattermost": - defaultPublisherClass = MattermostPublisher.class; - break; - case "outbound_webhook": - defaultPublisherClass = WebhookPublisher.class; - break; - case "console": - defaultPublisherClass = ConsolePublisher.class; - break; - default: - return Response.status(Response.Status.BAD_REQUEST).entity("Invalid publisher was provided.").build(); - } - NotificationPublisher notificationPublisher = qm.getDefaultNotificationPublisher(defaultPublisherClass); - final Publisher publisher = defaultPublisherClass.getDeclaredConstructor().newInstance(); + public Response testSmtpPublisherConfig(@FormParam("destination") String destination) { + try(QueryManager qm = new QueryManager()) { + Class defaultEmailPublisherClass = SendMailPublisher.class; + NotificationPublisher emailNotificationPublisher = qm.getDefaultNotificationPublisher(defaultEmailPublisherClass); + final Publisher emailPublisher = defaultEmailPublisherClass.getDeclaredConstructor().newInstance(); final JsonObject config = Json.createObjectBuilder() .add(Publisher.CONFIG_DESTINATION, destination) - .add(Publisher.CONFIG_TEMPLATE_KEY, notificationPublisher.getTemplate()) - .add(Publisher.CONFIG_TEMPLATE_MIME_TYPE_KEY, notificationPublisher.getTemplateMimeType()) + .add(Publisher.CONFIG_TEMPLATE_KEY, emailNotificationPublisher.getTemplate()) + .add(Publisher.CONFIG_TEMPLATE_MIME_TYPE_KEY, emailNotificationPublisher.getTemplateMimeType()) .build(); final Notification notification = new Notification() .scope(NotificationScope.SYSTEM) .group(NotificationGroup.CONFIGURATION) .title(NotificationConstants.Title.NOTIFICATION_TEST) - .content(WordUtils.capitalize(publisherType.replaceAll("_", " ")) + " Configuration test") + .content("SMTP configuration test") .level(NotificationLevel.INFORMATIONAL); + // Bypass Notification.dispatch() and go directly to the publisher itself + emailPublisher.inform(PublishContext.from(notification), notification, config); + return Response.ok().build(); + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + LOGGER.error(e.getMessage(), e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Exception occured while sending test mail notification.").build(); + } + } + + @POST + @Path("/test/{uuid}") + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.APPLICATION_JSON) + @Operation( + summary = "Dispatches a rule notification test", + description = "

    Requires permission SYSTEM_CONFIGURATION

    " + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Test notification dispatched successfully"), + @ApiResponse(responseCode = "401", description = "Unauthorized") + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response testSlackPublisherConfig( + @Parameter(description = "The UUID of the rule to test", schema = @Schema(type = "string", format = "uuid"), required = true) + @PathParam("uuid") @ValidUuid String ruleUuid) throws Exception { + try(QueryManager qm = new QueryManager()){ + NotificationRule rule = qm.getObjectByUuid(NotificationRule.class, ruleUuid); + NotificationPublisher notificationPublisher = rule.getPublisher(); + final Class publisherClass = Class.forName(notificationPublisher.getPublisherClass()); + Publisher publisher = (Publisher) publisherClass.getDeclaredConstructor().newInstance(); + String publisherConfig = rule.getPublisherConfig(); + JsonReader jsonReader = Json.createReader(new StringReader(publisherConfig)); + JsonObject configObject = jsonReader.readObject(); + jsonReader.close(); + final JsonObject config = Json.createObjectBuilder() + .add(Publisher.CONFIG_DESTINATION, configObject.getString("destination")) + .add(Publisher.CONFIG_TEMPLATE_KEY, rule.getPublisher().getTemplate()) + .add(Publisher.CONFIG_TEMPLATE_MIME_TYPE_KEY, rule.getPublisher().getTemplateMimeType()) + .build(); + + for(NotificationGroup group : rule.getNotifyOn()){ + final Notification notification = new Notification() + .scope(rule.getScope()) + .group(group.toString()+"_TEST") + .title(group) + .content("Rule configuration test") + .level(rule.getNotificationLevel()); publisher.inform(PublishContext.from(notification), notification, config); + } return Response.ok().build(); } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { LOGGER.error(e.getMessage(), e); - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Exception occured while sending " + publisherType + " test notification.").build(); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("Exception occured while sending the notification.").build(); } } } From dc7485c283216c2ccef00a9ba3c03af3fb90e0f5 Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Tue, 13 Aug 2024 01:07:34 +0100 Subject: [PATCH 076/429] add unit test for new notification testing enpoint Signed-off-by: Ross Murphy --- .../v1/NotificationPublisherResourceTest.java | 59 +++++++++++++------ 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java index d968dd5757..e6a7838c8d 100644 --- a/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java @@ -18,19 +18,29 @@ */ package org.dependencytrack.resources.v1; -import alpine.common.util.UuidUtil; -import alpine.notification.NotificationLevel; -import alpine.server.filters.ApiFilter; -import alpine.server.filters.AuthenticationFilter; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + import org.dependencytrack.JerseyTestRule; import org.dependencytrack.ResourceTest; import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.NotificationPublisher; import org.dependencytrack.model.NotificationRule; import org.dependencytrack.notification.NotificationScope; +import org.dependencytrack.notification.publisher.ConsolePublisher; +import org.dependencytrack.notification.publisher.CsWebexPublisher; import org.dependencytrack.notification.publisher.DefaultNotificationPublishers; +import org.dependencytrack.notification.publisher.JiraPublisher; +import org.dependencytrack.notification.publisher.MattermostPublisher; +import org.dependencytrack.notification.publisher.MsTeamsPublisher; import org.dependencytrack.notification.publisher.Publisher; import org.dependencytrack.notification.publisher.SendMailPublisher; +import org.dependencytrack.notification.publisher.SlackPublisher; +import org.dependencytrack.notification.publisher.WebhookPublisher; import org.dependencytrack.persistence.DefaultObjectGenerator; import org.glassfish.jersey.server.ResourceConfig; import org.junit.Assert; @@ -38,6 +48,10 @@ import org.junit.ClassRule; import org.junit.Test; +import alpine.common.util.UuidUtil; +import alpine.notification.NotificationLevel; +import alpine.server.filters.ApiFilter; +import alpine.server.filters.AuthenticationFilter; import jakarta.json.JsonArray; import jakarta.json.JsonObject; import jakarta.ws.rs.client.Entity; @@ -45,10 +59,6 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; - public class NotificationPublisherResourceTest extends ResourceTest { @ClassRule @@ -333,16 +343,29 @@ public void deleteDefaultNotificationPublisherTest() { } @Test - public void testPublishersConfigTest() { - List publishers = Arrays.asList("slack", "email", "cisco_webex", "microsoft_teams", "jira", "mattermost", "outbound_webhook", "console"); - for(String publisher : publishers){ - Form form = new Form(); - form.param("destination", "http://example.com"); - Response response = jersey.target(V1_NOTIFICATION_PUBLISHER + "/test/" + publisher).request() - .header(X_API_KEY, apiKey) - .post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); - Assert.assertEquals(200, response.getStatus(), 0); - } + public void testSmtpPublisherConfigTest() { + Form form = new Form(); + form.param("destination", "test@example.com"); + Response response = jersey.target(V1_NOTIFICATION_PUBLISHER + "/test/smtp").request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + Assert.assertEquals(200, response.getStatus(), 0); + } + + @Test + public void testNotificationRuleTest() { + NotificationPublisher publisher = qm.createNotificationPublisher( + "Example Publisher", "Publisher description", + SlackPublisher.class, "template", "text/html", + false); + NotificationRule rule = qm.createNotificationRule("Example Rule 1", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + rule.setPublisherConfig("{\"destination\":\"https://example.com/webhook\"}"); + + Response sendMailResponse = jersey.target(V1_NOTIFICATION_PUBLISHER + "/test/" + rule.getUuid()).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity("", MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + + Assert.assertEquals(200, sendMailResponse.getStatus()); } @Test From 0af6307984a8e24f26767d5654c08ca93b7a66d9 Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Tue, 13 Aug 2024 01:08:30 +0100 Subject: [PATCH 077/429] unused imports Signed-off-by: Ross Murphy --- .../v1/NotificationPublisherResourceTest.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java index e6a7838c8d..dd4b9a6030 100644 --- a/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java @@ -18,11 +18,6 @@ */ package org.dependencytrack.resources.v1; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.UUID; import org.dependencytrack.JerseyTestRule; @@ -31,16 +26,10 @@ import org.dependencytrack.model.NotificationPublisher; import org.dependencytrack.model.NotificationRule; import org.dependencytrack.notification.NotificationScope; -import org.dependencytrack.notification.publisher.ConsolePublisher; -import org.dependencytrack.notification.publisher.CsWebexPublisher; import org.dependencytrack.notification.publisher.DefaultNotificationPublishers; -import org.dependencytrack.notification.publisher.JiraPublisher; -import org.dependencytrack.notification.publisher.MattermostPublisher; -import org.dependencytrack.notification.publisher.MsTeamsPublisher; import org.dependencytrack.notification.publisher.Publisher; import org.dependencytrack.notification.publisher.SendMailPublisher; import org.dependencytrack.notification.publisher.SlackPublisher; -import org.dependencytrack.notification.publisher.WebhookPublisher; import org.dependencytrack.persistence.DefaultObjectGenerator; import org.glassfish.jersey.server.ResourceConfig; import org.junit.Assert; From 225bfe31f9485a4094077f2fa6b5030cd5c6c06d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 08:47:27 +0000 Subject: [PATCH 078/429] Bump org.codehaus.mojo:exec-maven-plugin from 3.4.0 to 3.4.1 Bumps [org.codehaus.mojo:exec-maven-plugin](https://github.com/mojohaus/exec-maven-plugin) from 3.4.0 to 3.4.1. - [Release notes](https://github.com/mojohaus/exec-maven-plugin/releases) - [Commits](https://github.com/mojohaus/exec-maven-plugin/compare/3.4.0...3.4.1) --- updated-dependencies: - dependency-name: org.codehaus.mojo:exec-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 73c983eb24..0f7fdf1112 100644 --- a/pom.xml +++ b/pom.xml @@ -543,7 +543,7 @@ org.codehaus.mojo exec-maven-plugin - 3.4.0 + 3.4.1 merge-services-bom From eebfb332bc3cf3ff8baf5324f299da5e72f178a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 09:01:30 +0000 Subject: [PATCH 079/429] Bump debian from `57bd74e` to `382967f` in /src/main/docker Bumps debian from `57bd74e` to `382967f`. --- updated-dependencies: - dependency-name: debian dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile index f6817bddd3..caa1e101f7 100644 --- a/src/main/docker/Dockerfile +++ b/src/main/docker/Dockerfile @@ -1,6 +1,6 @@ FROM eclipse-temurin:21.0.4_7-jre-jammy@sha256:870aae69d4521fdaf26e952f8026f75b37cb721e6302d4d4d7100f6b09823057 AS jre-build -FROM debian:stable-slim@sha256:57bd74e95092e6d4c0cdb6e36ca3db5bb828c2f592788734d1a707a4b92e7755 +FROM debian:stable-slim@sha256:382967fd7c35a0899ca3146b0b73d0791478fba2f71020c7aa8c27e3a4f26672 # Arguments that can be passed at build time # Directory names must end with / to avoid errors when ADDing and COPYing From 108dc87305fa1205f39045b644626eab832f23e6 Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Tue, 13 Aug 2024 20:06:22 +0100 Subject: [PATCH 080/429] generate the subject based off the group Signed-off-by: Ross Murphy --- .../v1/NotificationPublisherResource.java | 5 +- .../util/NotificationUtil.java | 108 ++++++++++++++++++ 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java index 4c72dd7688..105c660c96 100644 --- a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java @@ -361,10 +361,11 @@ public Response testSlackPublisherConfig( for(NotificationGroup group : rule.getNotifyOn()){ final Notification notification = new Notification() .scope(rule.getScope()) - .group(group.toString()+"_TEST") + .group(group.toString()) .title(group) .content("Rule configuration test") - .level(rule.getNotificationLevel()); + .level(rule.getNotificationLevel()) + .subject(NotificationUtil.generateSubject(group.toString())); publisher.inform(PublishContext.from(notification), notification, config); } return Response.ok().build(); diff --git a/src/main/java/org/dependencytrack/util/NotificationUtil.java b/src/main/java/org/dependencytrack/util/NotificationUtil.java index 4eb50a6320..32a74388bf 100644 --- a/src/main/java/org/dependencytrack/util/NotificationUtil.java +++ b/src/main/java/org/dependencytrack/util/NotificationUtil.java @@ -24,6 +24,7 @@ import alpine.notification.NotificationLevel; import org.apache.commons.io.FileUtils; import org.dependencytrack.model.Analysis; +import org.dependencytrack.model.AnalysisState; import org.dependencytrack.model.Bom; import org.dependencytrack.model.Component; import org.dependencytrack.model.ComponentIdentity; @@ -32,9 +33,12 @@ import org.dependencytrack.model.NotificationPublisher; import org.dependencytrack.model.Policy; import org.dependencytrack.model.PolicyCondition; +import org.dependencytrack.model.PolicyCondition.Operator; import org.dependencytrack.model.PolicyViolation; import org.dependencytrack.model.Project; +import org.dependencytrack.model.Severity; import org.dependencytrack.model.Tag; +import org.dependencytrack.model.Vex; import org.dependencytrack.model.ViolationAnalysis; import org.dependencytrack.model.ViolationAnalysisState; import org.dependencytrack.model.Vulnerability; @@ -65,11 +69,14 @@ import java.io.IOException; import java.net.URLDecoder; import java.nio.file.Path; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.UUID; import static java.nio.charset.StandardCharsets.UTF_8; @@ -634,4 +641,105 @@ public static String generateNotificationTitle(String messageType, Project proje } return messageType; } + + public static Object generateSubject(String group) { + final Project project = createProject(); + final Vulnerability vuln = createVulnerability(); + final Component component = createComponent(project); + final Analysis analysis = createAnalysis(component, vuln); + final PolicyViolation policyViolation = createPolicyViolation(component, project); + + switch (group) { + case "BOM_CONSUMED": + return new BomConsumedOrProcessed(project, "bomContent", Bom.Format.CYCLONEDX, "1.5"); + case "BOM_PROCESSED": + return new BomConsumedOrProcessed(project, "bomContent", Bom.Format.CYCLONEDX, "1.5"); + case "BOM_PROCESSING_FAILED": + return new BomProcessingFailed(project, "bomContent", "cause", Bom.Format.CYCLONEDX, "1.5"); + case "BOM_VALIDATION_FAILED": + return new BomValidationFailed(project, "bomContent", List.of("TEST"), Bom.Format.CYCLONEDX); + case "VEX_CONSUMED": + return new VexConsumedOrProcessed(project, "", Vex.Format.CYCLONEDX, ""); + case "VEX_PROCESSED": + return new VexConsumedOrProcessed(project, "", Vex.Format.CYCLONEDX, ""); + case "NEW_VULNERABILITY": + return new NewVulnerabilityIdentified(vuln, component, Set.of(project), VulnerabilityAnalysisLevel.BOM_UPLOAD_ANALYSIS); + case "NEW_VULNERABLE_DEPENDENCY": + return new NewVulnerableDependency(component, List.of(vuln)); + case "POLICY_VIOLATION": + return new PolicyViolationIdentified(policyViolation, component, project); + case "PROJECT_CREATED": + return NotificationUtil.toJson(project); + case "PROJECT_AUDIT_CHANGE": + return new AnalysisDecisionChange(vuln, component, project, analysis); + default: + return null; + } + } + + private static Project createProject() { + final Project project = new Project(); + project.setUuid(UUID.fromString("c9c9539a-e381-4b36-ac52-6a7ab83b2c95")); + project.setName("projectName"); + project.setVersion("projectVersion"); + project.setPurl("pkg:maven/org.acme/projectName@projectVersion"); + return project; + } + + private static Vulnerability createVulnerability() { + final Vulnerability vuln = new Vulnerability(); + vuln.setUuid(UUID.fromString("bccec5d5-ec21-4958-b3e8-22a7a866a05a")); + vuln.setVulnId("INT-001"); + vuln.setSource(Vulnerability.Source.INTERNAL); + vuln.setSeverity(Severity.MEDIUM); + return vuln; + } + + private static Component createComponent(Project project) { + final Component component = new Component(); + component.setProject(project); + component.setUuid(UUID.fromString("94f87321-a5d1-4c2f-b2fe-95165debebc6")); + component.setName("componentName"); + component.setVersion("componentVersion"); + return component; + } + + private static Analysis createAnalysis(Component component, Vulnerability vuln) { + final Analysis analysis = new Analysis(); + analysis.setComponent(component); + analysis.setVulnerability(vuln); + analysis.setAnalysisState(AnalysisState.FALSE_POSITIVE); + analysis.setSuppressed(true); + return analysis; + } + + private static PolicyViolation createPolicyViolation(Component component, Project project) { + final Policy policy = new Policy(); + policy.setId(1); + policy.setName("test"); + policy.setOperator(Policy.Operator.ALL); + policy.setProjects(List.of(project)); + policy.setUuid(UUID.randomUUID()); + policy.setViolationState(Policy.ViolationState.INFO); + + final PolicyCondition condition = new PolicyCondition(); + condition.setId(1); + condition.setUuid(UUID.randomUUID()); + condition.setOperator(Operator.NUMERIC_EQUAL); + condition.setSubject(PolicyCondition.Subject.AGE); + condition.setValue("1"); + condition.setPolicy(policy); + + final PolicyViolation policyViolation = new PolicyViolation(); + policyViolation.setId(1); + policyViolation.setPolicyCondition(condition); + policyViolation.setComponent(component); + policyViolation.setText("test"); + policyViolation.setType(PolicyViolation.Type.SECURITY); + policyViolation.setAnalysis(new ViolationAnalysis()); + policyViolation.setUuid(UUID.randomUUID()); + policyViolation.setTimestamp(new Date(System.currentTimeMillis())); + return policyViolation; + } + } From 11a96f5874cc44c728f45f936fc14d54b87e2ba0 Mon Sep 17 00:00:00 2001 From: Niklas Date: Wed, 14 Aug 2024 14:44:20 +0200 Subject: [PATCH 081/429] Add changelog for v4.11.7 Signed-off-by: Niklas --- docs/_posts/2024-08-14-v4.11.7.md | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 docs/_posts/2024-08-14-v4.11.7.md diff --git a/docs/_posts/2024-08-14-v4.11.7.md b/docs/_posts/2024-08-14-v4.11.7.md new file mode 100644 index 0000000000..3cb91d1fa2 --- /dev/null +++ b/docs/_posts/2024-08-14-v4.11.7.md @@ -0,0 +1,43 @@ +--- +title: v4.11.7 +type: patch +--- + +**Fixes:** + +* Fix `directDependencies`, `externalReferences`, and `metadata` fields missing from `/api/v1/project/{uuid}` response when not already cached - [apiserver/#4071] + +For a complete list of changes, refer to the respective GitHub milestones: + +* [API server milestone 4.11.7](https://github.com/DependencyTrack/dependency-track/milestone/44?closed=1) +* [Frontend milestone 4.11.7](https://github.com/DependencyTrack/frontend/milestone/29?closed=1) + +We thank all organizations and individuals who contributed to this release, from logging issues to taking part in discussions on GitHub & Slack to testing of fixes. + +###### [dependency-track-apiserver.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.7/dependency-track-apiserver.jar) + +| Algorithm | Checksum | +|:----------|:---------| +| SHA-1 | 9a916abcbb478a4dbad101f5335acdf2b8462062 | +| SHA-256 | 2df1b2ea67a16cdc6108c3ac2f538018e529205ce5f36a6da78f2feefeddd2c8 | + +###### [dependency-track-bundled.jar](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.7/dependency-track-bundled.jar) + +| Algorithm | Checksum | +|:----------|:---------| +| SHA-1 | c5a30ee550af8a943bb77167e515fb6422e51b36 | +| SHA-256 | 4665cdd14351d7b1c41004ffc57791297c4ec5fc7f958635cff246d1b1a95eed | + +###### [frontend-dist.zip](https://github.com/DependencyTrack/frontend/releases/download/4.11.7/frontend-dist.zip) + +| Algorithm | Checksum | +|:----------|:-----------------------------------------------------------------| +| SHA-1 | f481a9fca8e9f1eca7693cd638eef0eb5a1ed5a2 | +| SHA-256 | 332cc69c102c3df90f41c10687b78553dfb8bf6a66ffb6236f97d24fc932b2b7 | + +###### Software Bill of Materials (SBOM) + +* API Server: [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.11.7/bom.json) +* Frontend: [bom.json](https://github.com/DependencyTrack/frontend/releases/download/4.11.7/bom.json) + +[apiserver/#4071]: https://github.com/DependencyTrack/dependency-track/pull/4071 From 7b75819dec8df77087baba69bb2d4cdf734d5b08 Mon Sep 17 00:00:00 2001 From: Niklas Date: Wed, 14 Aug 2024 14:44:47 +0200 Subject: [PATCH 082/429] Update versions in issue template for defects Signed-off-by: Niklas --- .github/ISSUE_TEMPLATE/defect-report.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/defect-report.yml b/.github/ISSUE_TEMPLATE/defect-report.yml index 74f805a909..c2c23fb7ad 100644 --- a/.github/ISSUE_TEMPLATE/defect-report.yml +++ b/.github/ISSUE_TEMPLATE/defect-report.yml @@ -71,6 +71,7 @@ body: - 4.11.4 - 4.11.5 - 4.11.6 + - 4.11.7 - 4.12.0-SNAPSHOT validations: required: true From cda16bbac3cdba4cf187e0a9a4130054a8ca3c5f Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Wed, 14 Aug 2024 18:20:34 +0100 Subject: [PATCH 083/429] add groups to test rule Signed-off-by: Ross Murphy --- .../v1/NotificationPublisherResourceTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java index dd4b9a6030..677303f372 100644 --- a/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java @@ -18,6 +18,8 @@ */ package org.dependencytrack.resources.v1; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; import org.dependencytrack.JerseyTestRule; @@ -25,6 +27,7 @@ import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.NotificationPublisher; import org.dependencytrack.model.NotificationRule; +import org.dependencytrack.notification.NotificationGroup; import org.dependencytrack.notification.NotificationScope; import org.dependencytrack.notification.publisher.DefaultNotificationPublishers; import org.dependencytrack.notification.publisher.Publisher; @@ -347,7 +350,15 @@ public void testNotificationRuleTest() { "Example Publisher", "Publisher description", SlackPublisher.class, "template", "text/html", false); + NotificationRule rule = qm.createNotificationRule("Example Rule 1", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + + Set groups = new HashSet<>(Set.of(NotificationGroup.BOM_CONSUMED, NotificationGroup.BOM_PROCESSED, NotificationGroup.BOM_PROCESSING_FAILED, + NotificationGroup.BOM_VALIDATION_FAILED, NotificationGroup.NEW_VULNERABILITY, NotificationGroup.NEW_VULNERABLE_DEPENDENCY, + NotificationGroup.POLICY_VIOLATION, NotificationGroup.PROJECT_CREATED, NotificationGroup.PROJECT_AUDIT_CHANGE, + NotificationGroup.VEX_CONSUMED, NotificationGroup.VEX_PROCESSED)); + rule.setNotifyOn(groups); + rule.setPublisherConfig("{\"destination\":\"https://example.com/webhook\"}"); Response sendMailResponse = jersey.target(V1_NOTIFICATION_PUBLISHER + "/test/" + rule.getUuid()).request() From 69a9ccd17ffd014875f2eac9a6608aa14e63609e Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Wed, 14 Aug 2024 23:07:40 +0100 Subject: [PATCH 084/429] add author to json response Signed-off-by: Ross Murphy --- src/main/java/org/dependencytrack/model/Component.java | 6 ++++-- src/main/java/org/dependencytrack/model/Project.java | 4 +++- .../org/dependencytrack/resources/v1/ComponentResource.java | 1 + .../org/dependencytrack/resources/v1/ProjectResource.java | 2 ++ .../dependencytrack/resources/v1/ComponentResourceTest.java | 2 ++ 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/Component.java b/src/main/java/org/dependencytrack/model/Component.java index 9a5eb88bd7..c4a36e101f 100644 --- a/src/main/java/org/dependencytrack/model/Component.java +++ b/src/main/java/org/dependencytrack/model/Component.java @@ -401,15 +401,17 @@ public String getPublisher() { } @Deprecated - @JsonIgnore + @JsonInclude(JsonInclude.Include.NON_EMPTY) public String getAuthor(){ return ModelConverter.convertContactsToString(this.authors); } @Deprecated public void setAuthor(String author){ - if(this.authors==null){ + if (this.authors == null) { this.authors = new ArrayList<>(); + } else { + this.authors.clear(); } this.authors.add(new OrganizationalContact() {{ setName(author); diff --git a/src/main/java/org/dependencytrack/model/Project.java b/src/main/java/org/dependencytrack/model/Project.java index c672fd9484..74fdef9446 100644 --- a/src/main/java/org/dependencytrack/model/Project.java +++ b/src/main/java/org/dependencytrack/model/Project.java @@ -307,7 +307,7 @@ public void setAuthors(List authors) { } @Deprecated - @JsonIgnore + @JsonInclude(JsonInclude.Include.NON_EMPTY) public String getAuthor(){ return ModelConverter.convertContactsToString(this.authors); } @@ -316,6 +316,8 @@ public String getAuthor(){ public void setAuthor(String author){ if(this.authors==null){ this.authors = new ArrayList<>(); + } else{ + this.authors.clear(); } this.authors.add(new OrganizationalContact() {{ setName(author); diff --git a/src/main/java/org/dependencytrack/resources/v1/ComponentResource.java b/src/main/java/org/dependencytrack/resources/v1/ComponentResource.java index 61b2397e23..eaff272e25 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ComponentResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ComponentResource.java @@ -285,6 +285,7 @@ public Response createComponent(@Parameter(description = "The UUID of the projec final Validator validator = super.getValidator(); failOnValidationError( validator.validateProperty(jsonComponent, "authors"), + validator.validateProperty(jsonComponent, "author"), validator.validateProperty(jsonComponent, "publisher"), validator.validateProperty(jsonComponent, "name"), validator.validateProperty(jsonComponent, "version"), diff --git a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java index 96bd9baad4..c9cd063908 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java @@ -289,6 +289,7 @@ public Response getProjectsByClassifier( public Response createProject(Project jsonProject) { final Validator validator = super.getValidator(); failOnValidationError( + validator.validateProperty(jsonProject, "author"), validator.validateProperty(jsonProject, "authors"), validator.validateProperty(jsonProject, "publisher"), validator.validateProperty(jsonProject, "group"), @@ -429,6 +430,7 @@ public Response patchProject( Project jsonProject) { final Validator validator = getValidator(); failOnValidationError( + validator.validateProperty(jsonProject, "author"), validator.validateProperty(jsonProject, "authors"), validator.validateProperty(jsonProject, "publisher"), validator.validateProperty(jsonProject, "group"), diff --git a/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java index 9b49d94f58..a5d14a8783 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ComponentResourceTest.java @@ -48,6 +48,7 @@ import java.util.UUID; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json; import static org.assertj.core.api.Assertions.assertThat; public class ComponentResourceTest extends ResourceTest { @@ -515,6 +516,7 @@ public void createComponentTest() { Assert.assertNotNull(json); Assert.assertEquals("My Component", json.getString("name")); Assert.assertEquals("SampleAuthor" ,json.getJsonArray("authors").getJsonObject(0).getString("name")); + Assert.assertEquals("SampleAuthor", json.getString("author")); Assert.assertEquals("1.0", json.getString("version")); Assert.assertTrue(UuidUtil.isValidUUID(json.getString("uuid"))); } From 55e5a71c8180ed49447b095626d6310054370b0d Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Wed, 14 Aug 2024 23:29:50 +0100 Subject: [PATCH 085/429] trigger test Signed-off-by: Ross Murphy --- src/main/java/org/dependencytrack/model/Component.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/model/Component.java b/src/main/java/org/dependencytrack/model/Component.java index c4a36e101f..3cb1c1fa86 100644 --- a/src/main/java/org/dependencytrack/model/Component.java +++ b/src/main/java/org/dependencytrack/model/Component.java @@ -408,7 +408,7 @@ public String getAuthor(){ @Deprecated public void setAuthor(String author){ - if (this.authors == null) { + if(this.authors==null){ this.authors = new ArrayList<>(); } else { this.authors.clear(); From 521ea712a24ab949054428511178d41e31ccf96f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 08:10:44 +0000 Subject: [PATCH 086/429] Bump com.google.cloud.sql:cloud-sql-connector-jdbc-sqlserver Bumps com.google.cloud.sql:cloud-sql-connector-jdbc-sqlserver from 1.19.1 to 1.20.0. --- updated-dependencies: - dependency-name: com.google.cloud.sql:cloud-sql-connector-jdbc-sqlserver dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0f7fdf1112..b132ba066d 100644 --- a/pom.xml +++ b/pom.xml @@ -90,7 +90,7 @@ 4.2.1 0.1.2 10.17.0 - 1.19.1 + 1.20.0 1.18.0 1.19.1 2.1.0 From 3d52950481c1da952812de0ae6ce7990f899f1b9 Mon Sep 17 00:00:00 2001 From: Niklas Date: Wed, 14 Aug 2024 21:35:53 +0200 Subject: [PATCH 087/429] Bump Alpine to 3.0.1 Signed-off-by: nscuro --- pom.xml | 77 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index 0f7fdf1112..ef1f3f8397 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ us.springett alpine-parent - 3.0.0-SNAPSHOT + 3.0.1 4.0.0 @@ -495,6 +495,39 @@ false + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + none + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + none + + + + + org.apache.maven.plugins @@ -604,27 +637,27 @@ - - org.apache.maven.plugins - maven-clean-plugin - 3.4.0 - - true - - - target - false - false - - */ - - - dependency-track*.jar - - - - - + + org.apache.maven.plugins + maven-clean-plugin + 3.4.0 + + true + + + target + false + false + + */ + + + dependency-track*.jar + + + + + From f2b8387e1b9ed0508c4cefe00d6c17f5829fb583 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 08:22:33 +0000 Subject: [PATCH 088/429] Bump github/codeql-action from 3.26.0 to 3.26.2 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.0 to 3.26.2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/eb055d739abdc2e8de2e5f4ba1a8b246daa779aa...429e1977040da7a23b6822b13c129cd1ba93dbb2) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 82a5dc222e..9dd46e91b3 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -145,6 +145,6 @@ jobs: - name: Upload Trivy Scan Results to GitHub Security Tab if: ${{ inputs.publish-container }} - uses: github/codeql-action/upload-sarif@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # tag=v3.26.0 + uses: github/codeql-action/upload-sarif@429e1977040da7a23b6822b13c129cd1ba93dbb2 # tag=v3.26.2 with: sarif_file: 'trivy-results.sarif' From a8d49f08a7209bef3705c92dd54b6cefc15c3741 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 08:22:37 +0000 Subject: [PATCH 089/429] Bump docker/build-push-action from 6.6.1 to 6.7.0 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.6.1 to 6.7.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/16ebe778df0e7752d2cfcbd924afdbbd89c1a755...5cd11c3a4ced054e52742c5fd54dca954e0edd85) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 82a5dc222e..e6d317a894 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -121,7 +121,7 @@ jobs: echo "tags=${TAGS}" >> $GITHUB_OUTPUT - name: Build multi-arch Container Image - uses: docker/build-push-action@16ebe778df0e7752d2cfcbd924afdbbd89c1a755 # tag=v6.6.1 + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # tag=v6.7.0 with: tags: ${{ steps.tags.outputs.tags }} build-args: |- From c9d3f76633bdb64f7e1c5deff4f26d0e1c2ba751 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 08:31:22 +0000 Subject: [PATCH 090/429] Bump org.apache.maven:maven-artifact from 3.9.8 to 3.9.9 Bumps [org.apache.maven:maven-artifact](https://github.com/apache/maven) from 3.9.8 to 3.9.9. - [Release notes](https://github.com/apache/maven/releases) - [Commits](https://github.com/apache/maven/compare/maven-3.9.8...maven-3.9.9) --- updated-dependencies: - dependency-name: org.apache.maven:maven-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c8c4ff0388..19ac1b252d 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ 3.4.1 4.13.2 8.11.3 - 3.9.8 + 3.9.9 5.15.0 6.1.7 1.5.0 From d74bd1d8eadae9ab2b8b8fb0e5da5528e8efaece Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 08:03:39 +0000 Subject: [PATCH 091/429] Bump org.apache.commons:commons-compress from 1.27.0 to 1.27.1 Bumps org.apache.commons:commons-compress from 1.27.0 to 1.27.1. --- updated-dependencies: - dependency-name: org.apache.commons:commons-compress dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 19ac1b252d..9f7ebe39ec 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ 1.18.0 1.19.1 2.1.0 - 1.27.0 + 1.27.1 1.12.0 1.4.2 1.0.1 From 36d336ed2dbfdb938eb8a5d8f1dd15e968503a8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 08:41:11 +0000 Subject: [PATCH 092/429] Bump org.apache.maven.plugins:maven-checkstyle-plugin Bumps [org.apache.maven.plugins:maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.4.0 to 3.5.0. - [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.4.0...maven-checkstyle-plugin-3.5.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9f7ebe39ec..f37e480328 100644 --- a/pom.xml +++ b/pom.xml @@ -532,7 +532,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.4.0 + 3.5.0 ${project.basedir}/.checkstyle.xml false From 73b85fb535a5d70c8cd29a17e4ac7438af6c7d7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 08:41:33 +0000 Subject: [PATCH 093/429] Bump com.microsoft.sqlserver:mssql-jdbc Bumps [com.microsoft.sqlserver:mssql-jdbc](https://github.com/Microsoft/mssql-jdbc) from 12.8.0.jre11 to 12.8.1.jre11. - [Release notes](https://github.com/Microsoft/mssql-jdbc/releases) - [Changelog](https://github.com/microsoft/mssql-jdbc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Microsoft/mssql-jdbc/commits) --- updated-dependencies: - dependency-name: com.microsoft.sqlserver:mssql-jdbc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9f7ebe39ec..0869fbd90c 100644 --- a/pom.xml +++ b/pom.xml @@ -124,7 +124,7 @@ 2.0.16 1.323 - 12.8.0.jre11 + 12.8.1.jre11 8.0.33 42.7.3 From 3a41ad892a073e90fb3026d585120f27381825e2 Mon Sep 17 00:00:00 2001 From: Guilhem Bonnefille Date: Thu, 22 Aug 2024 07:48:48 +0200 Subject: [PATCH 094/429] Add test for license finding by Id or Name Signed-off-by: Guilhem Bonnefille --- .../tasks/BomUploadProcessingTaskTest.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java index bfdcbb7fa8..9bb3896501 100644 --- a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java @@ -1032,6 +1032,49 @@ public void informWithLicenseResolutionByNameTest() { }); } + @Test + public void informWithLicenseResolutionByIdOrNameTest() { + final var license = new License(); + license.setLicenseId("MIT"); + license.setName("MIT License"); + qm.persist(license); + + final var project = new Project(); + project.setName("acme-license-app"); + qm.persist(project); + + final byte[] bomBytes = """ + { + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b80", + "version": 1, + "components": [ + { + "type": "library", + "name": "acme-lib-x", + "licenses": [ + { + "license": { + "name": "MIT" + } + } + ] + } + ] + } + """.getBytes(StandardCharsets.UTF_8); + + final var bomUploadEvent = new BomUploadEvent(qm.detach(Project.class, project.getId()), bomBytes); + new BomUploadProcessingTask().inform(bomUploadEvent); + awaitBomProcessedNotification(bomUploadEvent); + + assertThat(qm.getAllComponents(project)).satisfiesExactly(component -> { + assertThat(component.getResolvedLicense()).isNotNull(); + assertThat(component.getResolvedLicense().getLicenseId()).isEqualTo("MIT"); + }); + } + @Test // https://github.com/DependencyTrack/dependency-track/issues/1905 public void informIssue1905Test() throws Exception { final var project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); From dfeb2694f0a77ca565ac88de9b85edb78b7e7041 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 08:38:28 +0000 Subject: [PATCH 095/429] Bump github/codeql-action from 3.26.2 to 3.26.5 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.2 to 3.26.5. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/429e1977040da7a23b6822b13c129cd1ba93dbb2...2c779ab0d087cd7fe7b826087247c2c81f27bfa6) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index d0171ed1f8..27789a742c 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -145,6 +145,6 @@ jobs: - name: Upload Trivy Scan Results to GitHub Security Tab if: ${{ inputs.publish-container }} - uses: github/codeql-action/upload-sarif@429e1977040da7a23b6822b13c129cd1ba93dbb2 # tag=v3.26.2 + uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # tag=v3.26.5 with: sarif_file: 'trivy-results.sarif' From ae6863369f92230360edd6d5c4451efdda09578f Mon Sep 17 00:00:00 2001 From: nscuro Date: Fri, 30 Aug 2024 15:23:01 +0200 Subject: [PATCH 096/429] Bump DataNucleus to 6.0.8 Fixes #2061 Signed-off-by: nscuro --- pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pom.xml b/pom.xml index 4eebccd177..5ba49c6618 100644 --- a/pom.xml +++ b/pom.xml @@ -99,6 +99,13 @@ 1.4.2 1.0.1 9.0.5 + + 6.0.3 + 6.0.8 + 6.0.8 2.0.1 2.17.1 2.17.1 From da631ddcf9eb53cfcea49e95acd65aa43238cae9 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sat, 31 Aug 2024 19:09:17 +0200 Subject: [PATCH 097/429] Disable H2 shutdown hook H2 registers a JVM shutdown hook to close the database upon reception of `SIGTERM`. However, the hook executes before the servlet context has shut down, so event processing can still be going on at that time, causing a flood of "database already closed" exceptions. The H2 database is already closed as part of Alpine's `PersistenceManagerFactory#contextDestroyed` method, thus making the additional shutdown hook of H2 redundant anyway. Signed-off-by: nscuro --- src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a6f5fa53a8..b271bd40d8 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -63,7 +63,7 @@ alpine.database.port=9092 # Required # Specifies the JDBC URL to use when connecting to the database. -alpine.database.url=jdbc:h2:~/.dependency-track/db +alpine.database.url=jdbc:h2:~/.dependency-track/db;DB_CLOSE_ON_EXIT=FALSE # Required # Specifies the JDBC driver class to use. From 68e615a1b98ceed147809f5eb0e952b77cc4c8a3 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 7 Jul 2024 15:56:46 +0200 Subject: [PATCH 098/429] Support tagging of notification rules Supersedes #3506 Co-authored-by: Sebastien Delcoigne Signed-off-by: nscuro --- docs/_docs/integrations/notifications.md | 12 + .../model/NotificationRule.java | 14 + .../java/org/dependencytrack/model/Tag.java | 13 + .../notification/NotificationRouter.java | 221 ++++++--- .../persistence/NotificationQueryManager.java | 85 +++- .../persistence/PolicyQueryManager.java | 8 + .../persistence/ProjectQueryManager.java | 77 --- .../persistence/QueryManager.java | 30 +- .../persistence/TagQueryManager.java | 238 +++++++++- .../resources/v1/TagResource.java | 120 ++++- .../resources/v1/vo/TagListResponseItem.java | 3 +- ...aggedNotificationRuleListResponseItem.java | 32 ++ .../notification/NotificationRouterTest.java | 135 +++++- .../v1/NotificationRuleResourceTest.java | 107 +++++ .../resources/v1/TagResourceTest.java | 444 +++++++++++++++++- 15 files changed, 1347 insertions(+), 192 deletions(-) create mode 100644 src/main/java/org/dependencytrack/resources/v1/vo/TaggedNotificationRuleListResponseItem.java diff --git a/docs/_docs/integrations/notifications.md b/docs/_docs/integrations/notifications.md index 8cdc82a919..28c94ed297 100644 --- a/docs/_docs/integrations/notifications.md +++ b/docs/_docs/integrations/notifications.md @@ -476,6 +476,18 @@ to notify on. Then specify the destination: By default, portfolio notifications are published regardless of which project is affected. This behavior can be altered by optionally limiting the projects. Expand the 'Limit To' button to reveal and configure the list of projects. +Since v4.12, it is also possible to limit notifications to projects with a specific tag. +Multiple tags can be configured. Projects must have *at least one* of the configured tags +in order for the notification to be sent. + +If both *Limit to Projects* and *Limit to Tags* are configured, projects must match *any* +of the two conditions. They are disjunctive. + +When making use of parent-child relationships of projects, it can be desirable to configure notifications +only for a parent project, and have its children inherit the notification configuration. This can be achieved +by enabling the *Include active children of projects* option in the *Limit To* section. +Both *Limit to projects* and *Limit to tags* are inherited. + ## Outbound Webhooks With outbound webhooks, notifications and all of their relevant details can be delivered via HTTP to an endpoint configured through Dependency-Track's notification settings. diff --git a/src/main/java/org/dependencytrack/model/NotificationRule.java b/src/main/java/org/dependencytrack/model/NotificationRule.java index 202eacf1a5..9fdad4c536 100644 --- a/src/main/java/org/dependencytrack/model/NotificationRule.java +++ b/src/main/java/org/dependencytrack/model/NotificationRule.java @@ -116,6 +116,12 @@ public class NotificationRule implements Serializable { @Order(extensions = @Extension(vendorName = "datanucleus", key = "list-ordering", value = "name ASC, version ASC")) private List projects; + @Persistent(table = "NOTIFICATIONRULE_TAGS", defaultFetchGroup = "true", mappedBy = "notificationRules") + @Join(column = "NOTIFICATIONRULE_ID") + @Element(column = "TAG_ID") + @Order(extensions = @Extension(vendorName = "datanucleus", key = "list-ordering", value = "name ASC")) + private List tags; + @Persistent(table = "NOTIFICATIONRULE_TEAMS", defaultFetchGroup = "true") @Join(column = "NOTIFICATIONRULE_ID") @Element(column = "TEAM_ID") @@ -214,6 +220,14 @@ public void setProjects(List projects) { this.projects = projects; } + public List getTags() { + return tags; + } + + public void setTags(final List tags) { + this.tags = tags; + } + public List getTeams() { return teams; } diff --git a/src/main/java/org/dependencytrack/model/Tag.java b/src/main/java/org/dependencytrack/model/Tag.java index 5e7367cfae..a681cd05e1 100644 --- a/src/main/java/org/dependencytrack/model/Tag.java +++ b/src/main/java/org/dependencytrack/model/Tag.java @@ -63,6 +63,11 @@ public class Tag implements Serializable { @Pattern(regexp = RegexSequence.Definition.PRINTABLE_CHARS, message = "The name may only contain printable characters") private String name; + @Persistent + @JsonIgnore + @Order(extensions = @Extension(vendorName = "datanucleus", key = "list-ordering", value = "name ASC")) + private List notificationRules; + @Persistent @JsonIgnore @Order(extensions = @Extension(vendorName = "datanucleus", key = "list-ordering", value = "name ASC")) @@ -96,6 +101,14 @@ public void setName(String name) { this.name = name; } + public List getNotificationRules() { + return notificationRules; + } + + public void setNotificationRules(final List notificationRules) { + this.notificationRules = notificationRules; + } + public List getPolicies() { return policies; } diff --git a/src/main/java/org/dependencytrack/notification/NotificationRouter.java b/src/main/java/org/dependencytrack/notification/NotificationRouter.java index a5b6cf7aa5..cd9537fcb5 100644 --- a/src/main/java/org/dependencytrack/notification/NotificationRouter.java +++ b/src/main/java/org/dependencytrack/notification/NotificationRouter.java @@ -26,6 +26,7 @@ import org.dependencytrack.model.NotificationPublisher; import org.dependencytrack.model.NotificationRule; import org.dependencytrack.model.Project; +import org.dependencytrack.model.Tag; import org.dependencytrack.notification.publisher.PublishContext; import org.dependencytrack.notification.publisher.Publisher; import org.dependencytrack.notification.publisher.SendMailPublisher; @@ -51,8 +52,10 @@ import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.function.Predicate; import java.util.stream.Collectors; +import static java.util.Objects.requireNonNull; import static org.dependencytrack.notification.publisher.Publisher.CONFIG_TEMPLATE_KEY; import static org.dependencytrack.notification.publisher.Publisher.CONFIG_TEMPLATE_MIME_TYPE_KEY; @@ -103,30 +106,75 @@ public void inform(final Notification notification) { } } - public Notification restrictNotificationToRuleProjects(final Notification initialNotification, final NotificationRule rule) { - Notification restrictedNotification = initialNotification; - if (canRestrictNotificationToRuleProjects(initialNotification, rule)) { - Set ruleProjectsUuids = rule.getProjects().stream().map(Project::getUuid).map(UUID::toString).collect(Collectors.toSet()); - restrictedNotification = new Notification(); - restrictedNotification.setGroup(initialNotification.getGroup()); - restrictedNotification.setLevel(initialNotification.getLevel()); - restrictedNotification.scope(initialNotification.getScope()); - restrictedNotification.setContent(initialNotification.getContent()); - restrictedNotification.setTitle(initialNotification.getTitle()); - restrictedNotification.setTimestamp(initialNotification.getTimestamp()); - if (initialNotification.getSubject() instanceof final NewVulnerabilityIdentified subject) { - Set restrictedProjects = subject.getAffectedProjects().stream().filter(project -> ruleProjectsUuids.contains(project.getUuid().toString())).collect(Collectors.toSet()); - NewVulnerabilityIdentified restrictedSubject = new NewVulnerabilityIdentified(subject.getVulnerability(), subject.getComponent(), restrictedProjects, null); - restrictedNotification.setSubject(restrictedSubject); - } + private Notification restrictNotificationToRuleProjects(final Notification notification, final NotificationRule rule) { + if (!(notification.getSubject() instanceof final NewVulnerabilityIdentified subject) + || subject.getAffectedProjects() == null || subject.getAffectedProjects().isEmpty()) { + return notification; + } + + final boolean shouldFilterOnRuleProjects = rule.getProjects() != null && !rule.getProjects().isEmpty(); + final boolean shouldFilterOnRuleTags = rule.getTags() != null && !rule.getTags().isEmpty(); + if (!shouldFilterOnRuleProjects && !shouldFilterOnRuleTags) { + return notification; + } + + final Predicate projectFilterPredicate; + if (shouldFilterOnRuleProjects && shouldFilterOnRuleTags) { + projectFilterPredicate = matchesAnyProjectOfRule(rule).or(hasAnyTagOfRule(rule)); + } else if (shouldFilterOnRuleProjects) { + projectFilterPredicate = matchesAnyProjectOfRule(rule); + } else { + projectFilterPredicate = hasAnyTagOfRule(rule); + } + + final Set filteredAffectedProjects = subject.getAffectedProjects().stream() + .filter(projectFilterPredicate) + .collect(Collectors.toSet()); + if (filteredAffectedProjects.size() == subject.getAffectedProjects().size()) { + return notification; } - return restrictedNotification; + + final var filteredSubject = new NewVulnerabilityIdentified( + subject.getVulnerability(), + subject.getComponent(), + filteredAffectedProjects, + subject.getVulnerabilityAnalysisLevel() + ); + + return new Notification() + .group(notification.getGroup()) + .scope(notification.getScope()) + .level(notification.getLevel()) + .title(notification.getTitle()) + .content(notification.getContent()) + .timestamp(notification.getTimestamp()) + .subject(filteredSubject); + } + + private Predicate matchesAnyProjectOfRule(final NotificationRule rule) { + requireNonNull(rule.getProjects()); + + return project -> rule.getProjects().stream() + .map(Project::getUuid) + .anyMatch(project.getUuid()::equals); } - private boolean canRestrictNotificationToRuleProjects(final Notification initialNotification, final NotificationRule rule) { - return initialNotification.getSubject() instanceof NewVulnerabilityIdentified - && rule.getProjects() != null - && !rule.getProjects().isEmpty(); + private Predicate hasAnyTagOfRule(final NotificationRule rule) { + requireNonNull(rule.getTags()); + + return project -> { + if (project.getTags() == null || project.getTags().isEmpty()) { + return false; + } + + final Set projectTagNames = project.getTags().stream() + .map(Tag::getName) + .collect(Collectors.toSet()); + + return rule.getTags().stream() + .map(Tag::getName) + .anyMatch(projectTagNames::contains); + }; } List resolveRules(final PublishContext ctx, final Notification notification) { @@ -160,24 +208,7 @@ List resolveRules(final PublishContext ctx, final Notification if (NotificationScope.PORTFOLIO.name().equals(notification.getScope()) && notification.getSubject() instanceof final NewVulnerabilityIdentified subject) { - // If the rule specified one or more projects as targets, reduce the execution - // of the notification down to those projects that the rule matches and which - // also match project the component is included in. - // NOTE: This logic is slightly different from what is implemented in limitToProject() - for (final NotificationRule rule : result) { - if (rule.getNotifyOn().contains(NotificationGroup.valueOf(notification.getGroup()))) { - if (rule.getProjects() != null && !rule.getProjects().isEmpty() - && subject.getComponent() != null && subject.getComponent().getProject() != null) { - for (final Project project : rule.getProjects()) { - if (subject.getComponent().getProject().getUuid().equals(project.getUuid()) || (Boolean.TRUE.equals(rule.isNotifyChildren() && checkIfChildrenAreAffected(project, subject.getComponent().getProject().getUuid())))) { - rules.add(rule); - } - } - } else { - rules.add(rule); - } - } - } + limitToProject(ctx, rules, result, notification, subject.getComponent().getProject()); } else if (NotificationScope.PORTFOLIO.name().equals(notification.getScope()) && notification.getSubject() instanceof final NewVulnerableDependency subject) { limitToProject(ctx, rules, result, notification, subject.getComponent().getProject()); @@ -218,44 +249,92 @@ List resolveRules(final PublishContext ctx, final Notification * of the notification down to those projects that the rule matches and which * also match projects affected by the vulnerability. */ - private void limitToProject(final PublishContext ctx, final List applicableRules, - final List rules, final Notification notification, - final Project limitToProject) { + private void limitToProject( + final PublishContext ctx, + final List applicableRules, + final List rules, + final Notification notification, + final Project limitToProject + ) { + requireNonNull(limitToProject, "limitToProject must not be null"); + for (final NotificationRule rule : rules) { final PublishContext ruleCtx = ctx.withRule(rule); - if (rule.getNotifyOn().contains(NotificationGroup.valueOf(notification.getGroup()))) { - if (rule.getProjects() != null && !rule.getProjects().isEmpty()) { - for (final Project project : rule.getProjects()) { - if (project.getUuid().equals(limitToProject.getUuid())) { - LOGGER.debug("Project %s is part of the \"limit to\" list of the rule; Rule is applicable (%s)" - .formatted(limitToProject.getUuid(), ruleCtx)); - applicableRules.add(rule); - } else if (rule.isNotifyChildren()) { - final boolean isChildOfLimitToProject = checkIfChildrenAreAffected(project, limitToProject.getUuid()); - if (isChildOfLimitToProject) { - LOGGER.debug("Project %s is child of \"limit to\" project %s; Rule is applicable (%s)" - .formatted(limitToProject.getUuid(), project.getUuid(), ruleCtx)); - applicableRules.add(rule); - } else { - LOGGER.debug("Project %s is not a child of \"limit to\" project %s; Rule is not applicable (%s)" - .formatted(limitToProject.getUuid(), project.getUuid(), ruleCtx)); - } + + if (!rule.getNotifyOn().contains(NotificationGroup.valueOf(notification.getGroup()))) { + continue; + } + + final boolean isLimitedToProjects = rule.getProjects() != null && !rule.getProjects().isEmpty(); + final boolean isLimitedToTags = rule.getTags() != null && !rule.getTags().isEmpty(); + if (!isLimitedToProjects && !isLimitedToTags) { + LOGGER.debug("Rule is not limited to projects or tags; Rule is applicable (%s)".formatted(ruleCtx)); + applicableRules.add(rule); + continue; + } + + if (isLimitedToTags) { + final Predicate tagMatchPredicate = project -> (project.isActive() == null || project.isActive()) + && project.getTags() != null + && project.getTags().stream().anyMatch(rule.getTags()::contains); + + if (tagMatchPredicate.test(limitToProject)) { + LOGGER.debug(""" + Project %s is tagged with any of the "limit to" tags; \ + Rule is applicable (%s)""".formatted(limitToProject.getUuid(), ruleCtx)); + applicableRules.add(rule); + continue; + } else if (rule.isNotifyChildren() && isChildOfProjectMatching(limitToProject, tagMatchPredicate)) { + LOGGER.debug(""" + Project %s is child of a project tagged with any of the "limit to" tags; \ + Rule is applicable (%s)""".formatted(limitToProject.getUuid(), ruleCtx)); + applicableRules.add(rule); + continue; + } + } else { + LOGGER.debug("Rule is not limited to tags (%s)".formatted(ruleCtx)); + } + + if (isLimitedToProjects) { + var matched = false; + for (final Project project : rule.getProjects()) { + if (project.getUuid().equals(limitToProject.getUuid())) { + LOGGER.debug("Project %s is part of the \"limit to\" list of the rule; Rule is applicable (%s)" + .formatted(limitToProject.getUuid(), ruleCtx)); + matched = true; + break; + } else if (rule.isNotifyChildren()) { + final boolean isChildOfLimitToProject = checkIfChildrenAreAffected(project, limitToProject.getUuid()); + if (isChildOfLimitToProject) { + LOGGER.debug("Project %s is child of \"limit to\" project %s; Rule is applicable (%s)" + .formatted(limitToProject.getUuid(), project.getUuid(), ruleCtx)); + matched = true; + break; } else { - LOGGER.debug("Project %s is not part of the \"limit to\" list of the rule; Rule is not applicable (%s)" - .formatted(limitToProject.getUuid(), ruleCtx)); + LOGGER.debug("Project %s is not a child of \"limit to\" project %s (%s)" + .formatted(limitToProject.getUuid(), project.getUuid(), ruleCtx)); } } - } else { - LOGGER.debug("Rule is not limited to projects; Rule is applicable (%s)".formatted(ruleCtx)); + } + + if (matched) { applicableRules.add(rule); + } else { + LOGGER.debug("Project %s is not part of the \"limit to\" list of the rule; Rule is not applicable (%s)" + .formatted(limitToProject.getUuid(), ruleCtx)); } + } else { + LOGGER.debug("Rule is not limited to projects (%s)".formatted(ruleCtx)); } } + LOGGER.debug("Applicable rules: %s (%s)" .formatted(applicableRules.stream().map(NotificationRule::getName).collect(Collectors.joining(", ")), ctx)); } private boolean checkIfChildrenAreAffected(Project parent, UUID uuid) { + // TODO: Making this a recursive SQL query would be a lot more efficient. + boolean isChild = false; if (parent.getChildren() == null || parent.getChildren().isEmpty()) { return false; @@ -269,4 +348,20 @@ private boolean checkIfChildrenAreAffected(Project parent, UUID uuid) { } return isChild; } + + private boolean isChildOfProjectMatching(final Project childProject, final Predicate matchFunction) { + // TODO: Making this a recursive SQL query would be a lot more efficient. + + Project parent = childProject.getParent(); + while (parent != null) { + if (matchFunction.test(parent)) { + return true; + } + + parent = parent.getParent(); + } + + return false; + } + } diff --git a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java index 81553b86b9..3f4b6e71ad 100644 --- a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java @@ -25,13 +25,19 @@ import org.dependencytrack.model.NotificationPublisher; import org.dependencytrack.model.NotificationRule; import org.dependencytrack.model.Project; +import org.dependencytrack.model.Tag; import org.dependencytrack.notification.NotificationScope; import org.dependencytrack.notification.publisher.Publisher; import javax.jdo.PersistenceManager; import javax.jdo.Query; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import static org.dependencytrack.util.PersistenceUtil.assertPersistent; +import static org.dependencytrack.util.PersistenceUtil.assertPersistentAll; + public class NotificationQueryManager extends QueryManager implements IQueryManager { @@ -61,15 +67,17 @@ public class NotificationQueryManager extends QueryManager implements IQueryMana * @return a new NotificationRule */ public NotificationRule createNotificationRule(String name, NotificationScope scope, NotificationLevel level, NotificationPublisher publisher) { - final NotificationRule rule = new NotificationRule(); - rule.setName(name); - rule.setScope(scope); - rule.setNotificationLevel(level); - rule.setPublisher(publisher); - rule.setEnabled(true); - rule.setNotifyChildren(true); - rule.setLogSuccessfulPublish(false); - return persist(rule); + return callInTransaction(() -> { + final NotificationRule rule = new NotificationRule(); + rule.setName(name); + rule.setScope(scope); + rule.setNotificationLevel(level); + rule.setPublisher(publisher); + rule.setEnabled(true); + rule.setNotifyChildren(true); + rule.setLogSuccessfulPublish(false); + return persist(rule); + }); } /** @@ -78,15 +86,18 @@ public NotificationRule createNotificationRule(String name, NotificationScope sc * @return a NotificationRule */ public NotificationRule updateNotificationRule(NotificationRule transientRule) { - final NotificationRule rule = getObjectByUuid(NotificationRule.class, transientRule.getUuid()); - rule.setName(transientRule.getName()); - rule.setEnabled(transientRule.isEnabled()); - rule.setNotifyChildren(transientRule.isNotifyChildren()); - rule.setLogSuccessfulPublish(transientRule.isLogSuccessfulPublish()); - rule.setNotificationLevel(transientRule.getNotificationLevel()); - rule.setPublisherConfig(transientRule.getPublisherConfig()); - rule.setNotifyOn(transientRule.getNotifyOn()); - return persist(rule); + return callInTransaction(() -> { + final NotificationRule rule = getObjectByUuid(NotificationRule.class, transientRule.getUuid()); + rule.setName(transientRule.getName()); + rule.setEnabled(transientRule.isEnabled()); + rule.setNotifyChildren(transientRule.isNotifyChildren()); + rule.setLogSuccessfulPublish(transientRule.isLogSuccessfulPublish()); + rule.setNotificationLevel(transientRule.getNotificationLevel()); + rule.setPublisherConfig(transientRule.getPublisherConfig()); + rule.setNotifyOn(transientRule.getNotifyOn()); + bind(rule, resolveTags(transientRule.getTags())); + return persist(rule); + }); } /** @@ -226,4 +237,42 @@ public void deleteNotificationPublisher(final NotificationPublisher notification query.deletePersistentAll(notificationPublisher.getUuid()); delete(notificationPublisher); } + + /** + * @since 4.12.0 + */ + @Override + public boolean bind(final NotificationRule notificationRule, final Collection tags) { + assertPersistent(notificationRule, "notificationRule must be persistent"); + assertPersistentAll(tags, "tags must be persistent"); + + return callInTransaction(() -> { + boolean modified = false; + + for (final Tag existingTag : notificationRule.getTags()) { + if (!tags.contains(existingTag)) { + notificationRule.getTags().remove(existingTag); + existingTag.getNotificationRules().remove(notificationRule); + modified = true; + } + } + + for (final Tag tag : tags) { + if (!notificationRule.getTags().contains(tag)) { + notificationRule.getTags().add(tag); + + if (tag.getNotificationRules() == null) { + tag.setNotificationRules(new ArrayList<>(List.of(notificationRule))); + } else if (!tag.getNotificationRules().contains(notificationRule)) { + tag.getNotificationRules().add(notificationRule); + } + + modified = true; + } + } + + return modified; + }); + } + } diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index 2e2e77606c..aa62e36847 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -627,6 +627,14 @@ public boolean bind(final Policy policy, final Collection tags) { return callInTransaction(() -> { boolean modified = false; + for (final Tag existingTag : policy.getTags()) { + if (!tags.contains(existingTag)) { + policy.getTags().remove(existingTag); + existingTag.getPolicies().remove(policy); + modified = true; + } + } + for (final Tag tag : tags) { if (!policy.getTags().contains(tag)) { policy.getTags().add(tag); diff --git a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java index a9f3bc2443..91ac6fba82 100644 --- a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java @@ -378,83 +378,6 @@ public PaginatedResult getProjects(final Tag tag) { return getProjects(tag, false, false, false); } - /** - * Returns a list of Tag objects what have been resolved. It resolved - * tags by querying the database to retrieve the tag. If the tag does - * not exist, the tag will be created and returned with other resolved - * tags. - * @param tags a List of Tags to resolve - * @return List of resolved Tags - */ - private synchronized List resolveTags(final List tags) { - if (tags == null) { - return new ArrayList<>(); - } - final List resolvedTags = new ArrayList<>(); - final List unresolvedTags = new ArrayList<>(); - for (final Tag tag: tags) { - final String trimmedTag = StringUtils.trimToNull(tag.getName()); - if (trimmedTag != null) { - final Tag resolvedTag = getTagByName(trimmedTag); - if (resolvedTag != null) { - resolvedTags.add(resolvedTag); - } else { - unresolvedTags.add(trimmedTag); - } - } - } - resolvedTags.addAll(createTags(unresolvedTags)); - return resolvedTags; - } - - /** - * Returns a list of Tag objects by name. - * @param name the name of the Tag - * @return a Tag object - */ - @Override - public Tag getTagByName(final String name) { - final String loweredTrimmedTag = StringUtils.lowerCase(StringUtils.trimToNull(name)); - final Query query = pm.newQuery(Tag.class, "name == :name"); - query.setRange(0, 1); - return singleResult(query.execute(loweredTrimmedTag)); - } - - /** - * Creates a new Tag object with the specified name. - * @param name the name of the Tag to create - * @return the created Tag object - */ - @Override - public Tag createTag(final String name) { - final String loweredTrimmedTag = StringUtils.lowerCase(StringUtils.trimToNull(name)); - final Tag resolvedTag = getTagByName(loweredTrimmedTag); - if (resolvedTag != null) { - return resolvedTag; - } - final Tag tag = new Tag(); - tag.setName(loweredTrimmedTag); - return persist(tag); - } - - /** - * Creates one or more Tag objects from the specified name(s). - * @param names the name(s) of the Tag(s) to create - * @return the created Tag object(s) - */ - private List createTags(final List names) { - final List newTags = new ArrayList<>(); - for (final String name: names) { - final String loweredTrimmedTag = StringUtils.lowerCase(StringUtils.trimToNull(name)); - if (getTagByName(loweredTrimmedTag) == null) { - final Tag tag = new Tag(); - tag.setName(loweredTrimmedTag); - newTags.add(tag); - } - } - return new ArrayList<>(persist(newTags)); - } - /** * Creates a new Project. * @param name the name of the project to create diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index 45f51bb50e..cc42de5c96 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -33,7 +33,6 @@ import alpine.server.util.DbUtil; import com.github.packageurl.PackageURL; import com.google.common.collect.Lists; -import jakarta.json.JsonObject; import org.apache.commons.lang3.ClassUtils; import org.datanucleus.PropertyNames; import org.datanucleus.api.jdo.JDOQuery; @@ -85,6 +84,7 @@ import org.dependencytrack.resources.v1.vo.DependencyGraphResponse; import org.dependencytrack.tasks.scanners.AnalyzerIdentity; +import jakarta.json.JsonObject; import javax.jdo.FetchPlan; import javax.jdo.PersistenceManager; import javax.jdo.Query; @@ -466,11 +466,19 @@ public boolean doesProjectExist(final String name, final String version) { } public Tag getTagByName(final String name) { - return getProjectQueryManager().getTagByName(name); + return getTagQueryManager().getTagByName(name); } public Tag createTag(final String name) { - return getProjectQueryManager().createTag(name); + return getTagQueryManager().createTag(name); + } + + public List createTags(final List names) { + return getTagQueryManager().createTags(names); + } + + public List resolveTags(final List tags) { + return getTagQueryManager().resolveTags(tags); } public Project createProject(String name, String description, String version, List tags, Project parent, PackageURL purl, boolean active, boolean commitIndex) { @@ -1299,6 +1307,10 @@ public void clearComponentAnalysisCache(Date threshold) { getCacheQueryManager().clearComponentAnalysisCache(threshold); } + public boolean bind(final NotificationRule notificationRule, final Collection tags) { + return getNotificationQueryManager().bind(notificationRule, tags); + } + public void bind(Project project, List tags) { getProjectQueryManager().bind(project, tags); } @@ -1380,6 +1392,18 @@ public PaginatedResult getTagsForPolicy(String policyUuid) { return getTagQueryManager().getTagsForPolicy(policyUuid); } + public List getTaggedNotificationRules(final String tagName) { + return getTagQueryManager().getTaggedNotificationRules(tagName); + } + + public void tagNotificationRules(final String tagName, final Collection notificationRuleUuids) { + getTagQueryManager().tagNotificationRules(tagName, notificationRuleUuids); + } + + public void untagNotificationRules(final String tagName, final Collection notificationRuleUuids) { + getTagQueryManager().untagNotificationRules(tagName, notificationRuleUuids); + } + /** * Fetch an object from the datastore by its {@link UUID}, using the provided fetch groups. *

    diff --git a/src/main/java/org/dependencytrack/persistence/TagQueryManager.java b/src/main/java/org/dependencytrack/persistence/TagQueryManager.java index f852144277..3ced4ec62c 100644 --- a/src/main/java/org/dependencytrack/persistence/TagQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/TagQueryManager.java @@ -25,8 +25,10 @@ import alpine.persistence.OrderDirection; import alpine.persistence.PaginatedResult; import alpine.resources.AlpineRequest; +import org.apache.commons.lang3.StringUtils; import org.dependencytrack.auth.Permissions; import org.dependencytrack.exception.TagOperationFailedException; +import org.dependencytrack.model.NotificationRule; import org.dependencytrack.model.Policy; import org.dependencytrack.model.Project; import org.dependencytrack.model.Tag; @@ -70,14 +72,105 @@ public class TagQueryManager extends QueryManager implements IQueryManager { super(pm, request); } + /** + * Returns a list of Tag objects by name. + * @param name the name of the Tag + * @return a Tag object + */ + @Override + public Tag getTagByName(final String name) { + final String loweredTrimmedTag = StringUtils.lowerCase(StringUtils.trimToNull(name)); + final Query query = pm.newQuery(Tag.class, "name == :name"); + query.setRange(0, 1); + return singleResult(query.execute(loweredTrimmedTag)); + } + + /** + * Creates a new Tag object with the specified name. + * @param name the name of the Tag to create + * @return the created Tag object + */ + @Override + public Tag createTag(final String name) { + final String loweredTrimmedTag = StringUtils.lowerCase(StringUtils.trimToNull(name)); + final Tag resolvedTag = getTagByName(loweredTrimmedTag); + if (resolvedTag != null) { + return resolvedTag; + } + final Tag tag = new Tag(); + tag.setName(loweredTrimmedTag); + return persist(tag); + } + + /** + * Creates one or more Tag objects from the specified name(s). + * @param names the name(s) of the Tag(s) to create + * @return the created Tag object(s) + */ + @Override + public List createTags(final List names) { + final List newTags = new ArrayList<>(); + for (final String name: names) { + final String loweredTrimmedTag = StringUtils.lowerCase(StringUtils.trimToNull(name)); + if (getTagByName(loweredTrimmedTag) == null) { + final Tag tag = new Tag(); + tag.setName(loweredTrimmedTag); + newTags.add(tag); + } + } + return new ArrayList<>(persist(newTags)); + } + + /** + * Returns a list of Tag objects what have been resolved. It resolved + * tags by querying the database to retrieve the tag. If the tag does + * not exist, the tag will be created and returned with other resolved + * tags. + * @param tags a List of Tags to resolve + * @return List of resolved Tags + */ + @Override + public synchronized List resolveTags(final List tags) { + if (tags == null) { + return new ArrayList<>(); + } + final List resolvedTags = new ArrayList<>(); + final List unresolvedTags = new ArrayList<>(); + for (final Tag tag: tags) { + final String trimmedTag = StringUtils.trimToNull(tag.getName()); + if (trimmedTag != null) { + final Tag resolvedTag = getTagByName(trimmedTag); + if (resolvedTag != null) { + resolvedTags.add(resolvedTag); + } else { + unresolvedTags.add(trimmedTag); + } + } + } + resolvedTags.addAll(createTags(unresolvedTags)); + return resolvedTags; + } + /** * @since 4.12.0 */ - public record TagListRow(String name, long projectCount, long policyCount, long totalCount) { + public record TagListRow( + String name, + long projectCount, + long policyCount, + long notificationRuleCount, + long totalCount + ) { @SuppressWarnings("unused") // DataNucleus will use this for MSSQL. - public TagListRow(String name, int projectCount, int policyCount, int totalCount) { - this(name, (long) projectCount, (long) policyCount, (long) totalCount); + public TagListRow( + final String name, + final int projectCount, + final int policyCount, + final int notificationRuleCount, + final int totalCount + ) { + this(name, (long) projectCount, (long) policyCount, (long) notificationRuleCount, (long) totalCount); } } @@ -99,12 +192,13 @@ public List getTags() { INNER JOIN "PROJECT" ON "PROJECT"."ID" = "PROJECTS_TAGS"."PROJECT_ID" WHERE "PROJECTS_TAGS"."TAG_ID" = "TAG"."ID" - AND %s - ) AS "projectCount" + AND %s) AS "projectCount" , (SELECT COUNT(*) FROM "POLICY_TAGS" - WHERE "POLICY_TAGS"."TAG_ID" = "TAG"."ID" - ) AS "policyCount" + WHERE "POLICY_TAGS"."TAG_ID" = "TAG"."ID") AS "policyCount" + , (SELECT COUNT(*) + FROM "NOTIFICATIONRULE_TAGS" + WHERE "NOTIFICATIONRULE_TAGS"."TAG_ID" = "TAG"."ID") AS "notificationRuleCount" , COUNT(*) OVER() AS "totalCount" FROM "TAG" """.formatted(projectAclCondition); @@ -118,7 +212,10 @@ public List getTags() { if (orderBy == null) { sqlQuery += " ORDER BY \"name\" ASC"; - } else if ("name".equals(orderBy) || "projectCount".equals(orderBy) || "policyCount".equals(orderBy)) { + } else if ("name".equals(orderBy) + || "projectCount".equals(orderBy) + || "policyCount".equals(orderBy) + || "notificationRuleCount".equals(orderBy)) { sqlQuery += " ORDER BY \"%s\" %s, \"ID\" ASC".formatted(orderBy, orderDirection == OrderDirection.DESCENDING ? "DESC" : "ASC"); } else { @@ -139,7 +236,8 @@ public record TagDeletionCandidateRow( String name, long projectCount, long accessibleProjectCount, - long policyCount + long policyCount, + long notificationRuleCount ) { @SuppressWarnings("unused") // DataNucleus will use this for MSSQL. @@ -147,9 +245,10 @@ public TagDeletionCandidateRow( final String name, final int projectCount, final int accessibleProjectCount, - final int policyCount + final int policyCount, + final int notificationRuleCount ) { - this(name, (long) projectCount, (long) accessibleProjectCount, (long) policyCount); + this(name, (long) projectCount, (long) accessibleProjectCount, (long) policyCount, (long) notificationRuleCount); } } @@ -192,6 +291,11 @@ public void deleteTags(final Collection tagNames) { INNER JOIN "POLICY" ON "POLICY"."ID" = "POLICY_TAGS"."POLICY_ID" WHERE "POLICY_TAGS"."TAG_ID" = "TAG"."ID") AS "policyCount" + , (SELECT COUNT(*) + FROM "NOTIFICATIONRULE_TAGS" + INNER JOIN "NOTIFICATIONRULE" + ON "NOTIFICATIONRULE"."ID" = "NOTIFICATIONRULE_TAGS"."NOTIFICATIONRULE_ID" + WHERE "NOTIFICATIONRULE_TAGS"."TAG_ID" = "TAG"."ID") AS "notificationRuleCount" FROM "TAG" WHERE %s """.formatted(projectAclCondition, String.join(" OR ", tagNameFilters))); @@ -216,16 +320,20 @@ public void deleteTags(final Collection tagNames) { boolean hasPortfolioManagementPermission = false; boolean hasPolicyManagementPermission = false; + boolean hasSystemConfigurationPermission = false; if (principal == null) { hasPortfolioManagementPermission = true; hasPolicyManagementPermission = true; + hasSystemConfigurationPermission = true; } else { if (principal instanceof final ApiKey apiKey) { hasPortfolioManagementPermission = hasPermission(apiKey, Permissions.Constants.PORTFOLIO_MANAGEMENT); hasPolicyManagementPermission = hasPermission(apiKey, Permissions.Constants.POLICY_MANAGEMENT); + hasSystemConfigurationPermission = hasPermission(apiKey, Permissions.Constants.SYSTEM_CONFIGURATION); } else if (principal instanceof final UserPrincipal user) { hasPortfolioManagementPermission = hasPermission(user, Permissions.Constants.PORTFOLIO_MANAGEMENT, /* includeTeams */ true); hasPolicyManagementPermission = hasPermission(user, Permissions.Constants.POLICY_MANAGEMENT, /* includeTeams */ true); + hasSystemConfigurationPermission = hasPermission(user, Permissions.Constants.SYSTEM_CONFIGURATION, /* includeTeams */ true); } } @@ -251,6 +359,12 @@ public void deleteTags(final Collection tagNames) { The tag is assigned to %d policies, but the authenticated principal \ is missing the %s permission.""".formatted(row.policyCount(), Permissions.POLICY_MANAGEMENT)); } + + if (row.notificationRuleCount() > 0 && !hasSystemConfigurationPermission) { + errorByTagName.put(row.name(), """ + The tag is assigned to %d notification rules, but the authenticated principal \ + is missing the %s permission.""".formatted(row.notificationRuleCount(), Permissions.SYSTEM_CONFIGURATION)); + } } if (!errorByTagName.isEmpty()) { @@ -509,4 +623,106 @@ public PaginatedResult getTagsForPolicy(String policyUuid) { return (new PaginatedResult()).objects(tagsToShow).total(tagsToShow.size()); } + /** + * @since 4.12.0 + */ + public record TaggedNotificationRuleRow(String uuid, String name, long totalCount) { + + @SuppressWarnings("unused") // DataNucleus will use this for MSSQL. + public TaggedNotificationRuleRow(final String uuid, final String name, final int totalCount) { + this(uuid, name, (long) totalCount); + } + + } + + /** + * @since 4.12.0 + */ + @Override + public List getTaggedNotificationRules(final String tagName) { + // language=SQL + var sqlQuery = """ + SELECT "NOTIFICATIONRULE"."UUID" AS "uuid" + , "NOTIFICATIONRULE"."NAME" AS "name" + , COUNT(*) OVER() AS "totalCount" + FROM "NOTIFICATIONRULE" + INNER JOIN "NOTIFICATIONRULE_TAGS" + ON "NOTIFICATIONRULE_TAGS"."NOTIFICATIONRULE_ID" = "NOTIFICATIONRULE"."ID" + INNER JOIN "TAG" + ON "TAG"."ID" = "NOTIFICATIONRULE_TAGS"."TAG_ID" + WHERE "TAG"."NAME" = :tag + """; + + final var params = new HashMap(); + params.put("tag", tagName); + + if (filter != null) { + sqlQuery += " AND \"NOTIFICATIONRULE\".\"NAME\" LIKE :nameFilter"; + params.put("nameFilter", "%" + filter + "%"); + } + + if (orderBy == null) { + sqlQuery += " ORDER BY \"name\" ASC"; + } else if ("name".equals(orderBy)) { + sqlQuery += " ORDER BY \"%s\" %s".formatted(orderBy, + orderDirection == OrderDirection.DESCENDING ? "DESC" : "ASC"); + } else { + throw new NotSortableException("TaggedNotificationRule", orderBy, "Field does not exist or is not sortable"); + } + + sqlQuery += " " + getOffsetLimitSqlClause(); + + final Query query = pm.newQuery(Query.SQL, sqlQuery); + query.setNamedParameters(params); + return executeAndCloseResultList(query, TaggedNotificationRuleRow.class); + } + + /** + * @since 4.12.0 + */ + @Override + public void tagNotificationRules(final String tagName, final Collection notificationRuleUuids) { + runInTransaction(() -> { + final Tag tag = getTagByName(tagName); + if (tag == null) { + throw new NoSuchElementException("A tag with name %s does not exist".formatted(tagName)); + } + + final Query notificationRulesQuery = pm.newQuery(NotificationRule.class); + notificationRulesQuery.setFilter(":uuids.contains(uuid)"); + notificationRulesQuery.setParameters(notificationRuleUuids); + final List notificationRules = executeAndCloseList(notificationRulesQuery); + + for (final NotificationRule notificationRule : notificationRules) { + bind(notificationRule, List.of(tag)); + } + }); + } + + /** + * @since 4.12.0 + */ + @Override + public void untagNotificationRules(final String tagName, final Collection notificationRuleUuids) { + runInTransaction(() -> { + final Tag tag = getTagByName(tagName); + if (tag == null) { + throw new NoSuchElementException("A tag with name %s does not exist".formatted(tagName)); + } + + final Query notificationRulesQuery = pm.newQuery(NotificationRule.class); + notificationRulesQuery.setFilter(":uuids.contains(uuid)"); + notificationRulesQuery.setParameters(notificationRuleUuids); + final List notificationRules = executeAndCloseList(notificationRulesQuery); + + for (final NotificationRule notificationRule : notificationRules) { + if (notificationRule.getTags() == null || notificationRule.getTags().isEmpty()) { + continue; + } + + notificationRule.getTags().remove(tag); + } + }); + } + } diff --git a/src/main/java/org/dependencytrack/resources/v1/TagResource.java b/src/main/java/org/dependencytrack/resources/v1/TagResource.java index 8d891f5404..08a5ae82a1 100644 --- a/src/main/java/org/dependencytrack/resources/v1/TagResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/TagResource.java @@ -36,12 +36,14 @@ import org.dependencytrack.model.validation.ValidUuid; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.persistence.TagQueryManager.TagListRow; +import org.dependencytrack.persistence.TagQueryManager.TaggedNotificationRuleRow; import org.dependencytrack.persistence.TagQueryManager.TaggedPolicyRow; import org.dependencytrack.persistence.TagQueryManager.TaggedProjectRow; import org.dependencytrack.resources.v1.openapi.PaginatedApi; import org.dependencytrack.resources.v1.problems.ProblemDetails; import org.dependencytrack.resources.v1.problems.TagOperationProblemDetails; import org.dependencytrack.resources.v1.vo.TagListResponseItem; +import org.dependencytrack.resources.v1.vo.TaggedNotificationRuleListResponseItem; import org.dependencytrack.resources.v1.vo.TaggedPolicyListResponseItem; import org.dependencytrack.resources.v1.vo.TaggedProjectListResponseItem; @@ -91,7 +93,12 @@ public Response getAllTags() { } final List tags = tagListRows.stream() - .map(row -> new TagListResponseItem(row.name(), row.projectCount(), row.policyCount())) + .map(row -> new TagListResponseItem( + row.name(), + row.projectCount(), + row.policyCount(), + row.notificationRuleCount() + )) .toList(); final long totalCount = tagListRows.isEmpty() ? 0 : tagListRows.getFirst().totalCount(); return Response.ok(tags).header(TOTAL_COUNT_HEADER, totalCount).build(); @@ -418,4 +425,115 @@ public Response getTags( return getTagsForPolicy(String.valueOf(policyUuid)); } + @GET + @Path("/{name}/notificationRule") + @Produces(MediaType.APPLICATION_JSON) + @Operation( + summary = "Returns a list of all notification rules assigned to the given tag.", + description = "

    Requires permission SYSTEM_CONFIGURATION

    " + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "A list of all notification rules assigned to the given tag", + headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of notification rules", schema = @Schema(format = "integer")), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = TaggedPolicyListResponseItem.class))) + ) + }) + @PaginatedApi + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response getTaggedNotificationRules( + @Parameter(description = "Name of the tag to get notification rules for", required = true) + @PathParam("name") final String tagName + ) { + // TODO: Should enforce lowercase for tagName once we are sure that + // users don't have any mixed-case tags in their system anymore. + // Will likely need a migration to cleanup existing tags for this. + + final List taggedNotificationRuleRows; + try (final var qm = new QueryManager(getAlpineRequest())) { + taggedNotificationRuleRows = qm.getTaggedNotificationRules(tagName); + } + + final List tags = taggedNotificationRuleRows.stream() + .map(row -> new TaggedNotificationRuleListResponseItem(UUID.fromString(row.uuid()), row.name())) + .toList(); + final long totalCount = taggedNotificationRuleRows.isEmpty() ? 0 : taggedNotificationRuleRows.getFirst().totalCount(); + return Response.ok(tags).header(TOTAL_COUNT_HEADER, totalCount).build(); + } + + @POST + @Path("/{name}/notificationRule") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation( + summary = "Tags one or more notification rules.", + description = "

    Requires permission SYSTEM_CONFIGURATION

    " + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "204", + description = "Notification rules tagged successfully." + ), + @ApiResponse( + responseCode = "404", + description = "A tag with the provided name does not exist.", + content = @Content(schema = @Schema(implementation = ProblemDetails.class), mediaType = ProblemDetails.MEDIA_TYPE_JSON) + ) + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response tagNotificationRules( + @Parameter(description = "Name of the tag to assign", required = true) + @PathParam("name") final String tagName, + @Parameter( + description = "UUIDs of notification rules to tag", + required = true, + array = @ArraySchema(schema = @Schema(type = "string", format = "uuid")) + ) + @Size(min = 1, max = 100) final Set<@ValidUuid String> notificationRuleUuids + ) { + try (final var qm = new QueryManager(getAlpineRequest())) { + qm.tagNotificationRules(tagName, notificationRuleUuids); + } + + return Response.noContent().build(); + } + + @DELETE + @Path("/{name}/notificationRule") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation( + summary = "Untags one or more notification rules.", + description = "

    Requires permission SYSTEM_CONFIGURATION

    " + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "204", + description = "Notification rules untagged successfully." + ), + @ApiResponse( + responseCode = "404", + description = "A tag with the provided name does not exist.", + content = @Content(schema = @Schema(implementation = ProblemDetails.class), mediaType = ProblemDetails.MEDIA_TYPE_JSON) + ) + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response untagNotificationRules( + @Parameter(description = "Name of the tag", required = true) + @PathParam("name") final String tagName, + @Parameter( + description = "UUIDs of notification rules to untag", + required = true, + array = @ArraySchema(schema = @Schema(type = "string", format = "uuid")) + ) + @Size(min = 1, max = 100) final Set<@ValidUuid String> policyUuids + ) { + try (final var qm = new QueryManager(getAlpineRequest())) { + qm.untagNotificationRules(tagName, policyUuids); + } + + return Response.noContent().build(); + } + } diff --git a/src/main/java/org/dependencytrack/resources/v1/vo/TagListResponseItem.java b/src/main/java/org/dependencytrack/resources/v1/vo/TagListResponseItem.java index 2ddbfc32d0..716397f7c5 100644 --- a/src/main/java/org/dependencytrack/resources/v1/vo/TagListResponseItem.java +++ b/src/main/java/org/dependencytrack/resources/v1/vo/TagListResponseItem.java @@ -26,6 +26,7 @@ public record TagListResponseItem( @Parameter(description = "Name of the tag", required = true) String name, @Parameter(description = "Number of projects assigned to this tag") long projectCount, - @Parameter(description = "Number of policies assigned to this tag") long policyCount + @Parameter(description = "Number of policies assigned to this tag") long policyCount, + @Parameter(description = "Number of notification rules assigned to this tag") long notificationRuleCount ) { } diff --git a/src/main/java/org/dependencytrack/resources/v1/vo/TaggedNotificationRuleListResponseItem.java b/src/main/java/org/dependencytrack/resources/v1/vo/TaggedNotificationRuleListResponseItem.java new file mode 100644 index 0000000000..296221a0ec --- /dev/null +++ b/src/main/java/org/dependencytrack/resources/v1/vo/TaggedNotificationRuleListResponseItem.java @@ -0,0 +1,32 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.resources.v1.vo; + +import io.swagger.v3.oas.annotations.Parameter; + +import java.util.UUID; + +/** + * @since 4.12.0 + */ +public record TaggedNotificationRuleListResponseItem( + @Parameter(description = "UUID of the notification rule", required = true) UUID uuid, + @Parameter(description = "Name of the notification rule", required = true) String name +) { +} diff --git a/src/test/java/org/dependencytrack/notification/NotificationRouterTest.java b/src/test/java/org/dependencytrack/notification/NotificationRouterTest.java index 944b59bcf3..0ab6e2a3e7 100644 --- a/src/test/java/org/dependencytrack/notification/NotificationRouterTest.java +++ b/src/test/java/org/dependencytrack/notification/NotificationRouterTest.java @@ -27,6 +27,7 @@ import org.dependencytrack.model.NotificationPublisher; import org.dependencytrack.model.NotificationRule; import org.dependencytrack.model.Project; +import org.dependencytrack.model.Tag; import org.dependencytrack.model.Vex; import org.dependencytrack.model.Vulnerability; import org.dependencytrack.notification.publisher.DefaultNotificationPublishers; @@ -52,7 +53,6 @@ import static org.assertj.core.api.Assertions.assertThat; -@SuppressWarnings("unchecked") public class NotificationRouterTest extends PersistenceCapableTest { @Test @@ -96,7 +96,10 @@ public void testValidMatchingRule() { notification.setGroup(NotificationGroup.NEW_VULNERABILITY.name()); notification.setLevel(NotificationLevel.INFORMATIONAL); // Notification should not be limited to any projects - so set projects to null - NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), new Component(), null, null); + Project affectedProject = qm.createProject("Test Project", null, "1.0", null, null, null, true, false); + Component affectedComponent = new Component(); + affectedComponent.setProject(affectedProject); + NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), affectedComponent, null, null); notification.setSubject(subject); // Ok, let's test this NotificationRouter router = new NotificationRouter(); @@ -125,7 +128,9 @@ public void testValidMatchingProjectLimitingRule() { // Notification should be limited to only specific projects - Set the projects which are affected by the notification event Set affectedProjects = new HashSet<>(); affectedProjects.add(project); - NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), new Component(), affectedProjects, null); + Component affectedComponent = new Component(); + affectedComponent.setProject(project); + NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), affectedComponent, affectedProjects, null); notification.setSubject(subject); // Ok, let's test this NotificationRouter router = new NotificationRouter(); @@ -155,12 +160,14 @@ public void testValidNonMatchingProjectLimitingRule() { Set affectedProjects = new HashSet<>(); Project affectedProject = qm.createProject("Affected Project", null, "1.0", null, null, null, true, false); affectedProjects.add(affectedProject); - NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), new Component(), affectedProjects, null); + Component affectedComponent = new Component(); + affectedComponent.setProject(affectedProject); + NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), affectedComponent, affectedProjects, null); notification.setSubject(subject); // Ok, let's test this NotificationRouter router = new NotificationRouter(); List rules = router.resolveRules(PublishContext.from(notification), notification); - Assert.assertEquals(1, rules.size()); + Assert.assertEquals(0, rules.size()); } @Test @@ -178,7 +185,10 @@ public void testValidMatchingRuleAndPublisherInform() { notification.setGroup(NotificationGroup.NEW_VULNERABILITY.name()); notification.setLevel(NotificationLevel.INFORMATIONAL); // Notification should not be limited to any projects - so set projects to null - NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), new Component(), null, null); + Project affectedProject = qm.createProject("Test Project", null, "1.0", null, null, null, true, false); + Component affectedComponent = new Component(); + affectedComponent.setProject(affectedProject); + NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), affectedComponent, null, null); notification.setSubject(subject); // Ok, let's test this NotificationRouter router = new NotificationRouter(); @@ -213,7 +223,9 @@ public void testValidMatchingProjectLimitingRuleAndPublisherInform() { Set affectedProjects = new HashSet<>(); affectedProjects.add(firstProject); affectedProjects.add(secondProject); - NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), new Component(), affectedProjects, null); + Component affectedComponent = new Component(); + affectedComponent.setProject(firstProject); + NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), affectedComponent, affectedProjects, null); notification.setSubject(subject); // Ok, let's test this NotificationRouter router = new NotificationRouter(); @@ -242,7 +254,10 @@ public void testValidNonMatchingRule() { notification.setGroup(NotificationGroup.NEW_VULNERABILITY.name()); notification.setLevel(NotificationLevel.INFORMATIONAL); // Notification should not be limited to any projects - so set projects to null - NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), new Component(), null, null); + Project affectedProject = qm.createProject("Test Project 1", null, "1.0", null, null, null, true, false); + Component affectedComponent = new Component(); + affectedComponent.setProject(affectedProject); + NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), affectedComponent, null, null); notification.setSubject(subject); // Ok, let's test this NotificationRouter router = new NotificationRouter(); @@ -720,6 +735,110 @@ public void testAffectedActiveNullChild() { Assert.assertEquals(1, rules.size()); } + @Test + public void testValidMatchingTagLimitingRule() { + NotificationPublisher publisher = createSlackPublisher(); + // Creates a new rule and defines when the rule should be triggered (notifyOn) + NotificationRule rule = qm.createNotificationRule("Test Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Set notifyOn = new HashSet<>(); + notifyOn.add(NotificationGroup.NEW_VULNERABILITY); + rule.setNotifyOn(notifyOn); + // Creates a tag which will later be matched on + Tag tag = qm.createTag("test"); + List tags = List.of(tag); + Project project = qm.createProject("Test Project", null, "1.0", tags, null, null, true, false); + qm.bind(rule, tags); + // Creates a new notification + Notification notification = new Notification(); + notification.setScope(NotificationScope.PORTFOLIO.name()); + notification.setGroup(NotificationGroup.NEW_VULNERABILITY.name()); + notification.setLevel(NotificationLevel.INFORMATIONAL); + // Notification should be limited to only specific projects - Set the projects which are affected by the notification event + Set affectedProjects = new HashSet<>(); + affectedProjects.add(project); + Component affectedComponent = new Component(); + affectedComponent.setProject(project); + NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), affectedComponent, affectedProjects, null); + notification.setSubject(subject); + // Ok, let's test this + NotificationRouter router = new NotificationRouter(); + List rules = router.resolveRules(PublishContext.from(notification), notification); + Assert.assertEquals(1, rules.size()); + } + + @Test + public void testValidNonMatchingTagLimitingRule() { + NotificationPublisher publisher = createSlackPublisher(); + // Creates a new rule and defines when the rule should be triggered (notifyOn) + NotificationRule rule = qm.createNotificationRule("Test Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Set notifyOn = new HashSet<>(); + notifyOn.add(NotificationGroup.NEW_VULNERABILITY); + rule.setNotifyOn(notifyOn); + // Creates a tag to limit the notifications + Tag tag = qm.createTag("test"); + List tags = List.of(tag); + qm.createProject("Test Project", null, "1.0", tags, null, null, true, false); + // Rule should apply only for specific tag + qm.bind(rule, tags); + // Creates a new notification + Notification notification = new Notification(); + notification.setScope(NotificationScope.PORTFOLIO.name()); + notification.setGroup(NotificationGroup.NEW_VULNERABILITY.name()); + notification.setLevel(NotificationLevel.INFORMATIONAL); + // Set the projects which are affected by the notification event + Set affectedProjects = new HashSet<>(); + Project affectedProject = qm.createProject("Affected Project", null, "1.0", null, null, null, true, false); + affectedProjects.add(affectedProject); + Component affectedComponent = new Component(); + affectedComponent.setProject(affectedProject); + NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), affectedComponent, affectedProjects, null); + notification.setSubject(subject); + // Ok, let's test this + NotificationRouter router = new NotificationRouter(); + List rules = router.resolveRules(PublishContext.from(notification), notification); + // Affected project is not tagged, rule should be empty + Assert.assertEquals(0, rules.size()); + } + + @Test + public void testValidMatchingProjectAndTagLimitingRule() { + NotificationPublisher publisher = createMockPublisher(); + // Creates a new rule and defines when the rule should be triggered (notifyOn) + NotificationRule rule = qm.createNotificationRule("Test Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Set notifyOn = new HashSet<>(); + notifyOn.add(NotificationGroup.NEW_VULNERABILITY); + rule.setNotifyOn(notifyOn); + // Creates a tag to limit the notifications + Tag tag = qm.createTag("test"); + List tags = List.of(tag); + Project taggedProject = qm.createProject("Test Project", null, "1.0", tags, null, null, true, false); + // Rule should apply for specific tag + qm.bind(rule, tags); + // Rule should also apply for specific project + Project otherAffectedProject = qm.createProject("Affected Project", null, "1.0", null, null, null, true, false); + rule.setProjects(List.of(otherAffectedProject)); + // Creates a new notification + Notification notification = new Notification(); + notification.setScope(NotificationScope.PORTFOLIO.name()); + notification.setGroup(NotificationGroup.NEW_VULNERABILITY.name()); + notification.setLevel(NotificationLevel.INFORMATIONAL); + // Set the projects which are affected by the notification event + Set affectedProjects = new HashSet<>(); + affectedProjects.add(taggedProject); + affectedProjects.add(otherAffectedProject); + Component affectedComponent = new Component(); + affectedComponent.setProject(taggedProject); + NewVulnerabilityIdentified subject = new NewVulnerabilityIdentified(new Vulnerability(), affectedComponent, affectedProjects, null); + notification.setSubject(subject); + // Ok, let's test this + NotificationRouter router = new NotificationRouter(); + router.inform(notification); + // Affected project is not tagged, rules should be empty + Notification providedNotification = MockPublisher.getNotification(); + NewVulnerabilityIdentified providedSubject = (NewVulnerabilityIdentified) providedNotification.getSubject(); + Assert.assertEquals(2, providedSubject.getAffectedProjects().size()); + } + private NotificationPublisher createSlackPublisher() { return qm.createNotificationPublisher( DefaultNotificationPublishers.SLACK.getPublisherName(), diff --git a/src/test/java/org/dependencytrack/resources/v1/NotificationRuleResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/NotificationRuleResourceTest.java index 03d5b1fb86..9906483b4a 100644 --- a/src/test/java/org/dependencytrack/resources/v1/NotificationRuleResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/NotificationRuleResourceTest.java @@ -175,6 +175,112 @@ public void updateNotificationRuleInvalidTest() { Assert.assertEquals("The UUID of the notification rule could not be found.", body); } + @Test + public void updateNotificationRuleWithTagsTest() { + final NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + final NotificationRule rule = qm.createNotificationRule("Rule 1", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + + // Tag the rule with "foo" and "bar". + Response response = jersey.target(V1_NOTIFICATION_RULE).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(/* language=JSON */ """ + { + "uuid": "%s", + "name": "Rule 1", + "scope": "PORTFOLIO", + "notificationLevel": "INFORMATIONAL", + "tags": [ + { + "name": "foo" + }, + { + "name": "bar" + } + ] + } + """.formatted(rule.getUuid()), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(200); + assertThatJson(getPlainTextBody(response)) + .withMatcher("ruleUuid", equalTo(rule.getUuid().toString())) + .isEqualTo(/* language=JSON */ """ + { + "name": "Rule 1", + "enabled": false, + "notifyChildren": false, + "logSuccessfulPublish": false, + "scope": "PORTFOLIO", + "notificationLevel": "INFORMATIONAL", + "projects": [], + "tags": [ + { + "name": "foo" + }, + { + "name": "bar" + } + ], + "teams": [], + "notifyOn": [], + "publisher": { + "name": "${json-unit.any-string}", + "description": "${json-unit.any-string}", + "publisherClass": "${json-unit.any-string}", + "templateMimeType": "${json-unit.any-string}", + "defaultPublisher": true, + "uuid": "${json-unit.any-string}" + }, + "uuid": "${json-unit.matches:ruleUuid}" + } + """); + + // Replace the previous tags with only "baz". + response = jersey.target(V1_NOTIFICATION_RULE).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(/* language=JSON */ """ + { + "uuid": "%s", + "name": "Rule 1", + "scope": "PORTFOLIO", + "notificationLevel": "INFORMATIONAL", + "tags": [ + { + "name": "baz" + } + ] + } + """.formatted(rule.getUuid()), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(200); + assertThatJson(getPlainTextBody(response)) + .withMatcher("ruleUuid", equalTo(rule.getUuid().toString())) + .isEqualTo(/* language=JSON */ """ + { + "name": "Rule 1", + "enabled": false, + "notifyChildren": false, + "logSuccessfulPublish": false, + "scope": "PORTFOLIO", + "notificationLevel": "INFORMATIONAL", + "projects": [], + "tags": [ + { + "name": "baz" + } + ], + "teams": [], + "notifyOn": [], + "publisher": { + "name": "${json-unit.any-string}", + "description": "${json-unit.any-string}", + "publisherClass": "${json-unit.any-string}", + "templateMimeType": "${json-unit.any-string}", + "defaultPublisher": true, + "uuid": "${json-unit.any-string}" + }, + "uuid": "${json-unit.matches:ruleUuid}" + } + """); + } + @Test public void deleteNotificationRuleTest() { NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); @@ -424,6 +530,7 @@ public void addTeamToRuleWithCustomEmailPublisherTest() { "scope": "PORTFOLIO", "notificationLevel": "INFORMATIONAL", "projects": [], + "tags": [], "teams": [ { "uuid": "${json-unit.matches:teamUuid}", diff --git a/src/test/java/org/dependencytrack/resources/v1/TagResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/TagResourceTest.java index cf82720a1a..6982f2d68b 100644 --- a/src/test/java/org/dependencytrack/resources/v1/TagResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/TagResourceTest.java @@ -6,9 +6,11 @@ import org.dependencytrack.JerseyTestRule; import org.dependencytrack.ResourceTest; import org.dependencytrack.auth.Permissions; +import org.dependencytrack.model.NotificationRule; import org.dependencytrack.model.Policy; import org.dependencytrack.model.Project; import org.dependencytrack.model.Tag; +import org.dependencytrack.notification.NotificationScope; import org.dependencytrack.resources.v1.exception.ConstraintViolationExceptionMapper; import org.dependencytrack.resources.v1.exception.NoSuchElementExceptionMapper; import org.dependencytrack.resources.v1.exception.TagOperationFailedExceptionMapper; @@ -87,6 +89,19 @@ public void getTagsTest() { qm.bind(policy, List.of(tagBar)); + final var notificationRuleA = new NotificationRule(); + notificationRuleA.setName("rule-a"); + notificationRuleA.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRuleA); + + final var notificationRuleB = new NotificationRule(); + notificationRuleB.setName("rule-b"); + notificationRuleB.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRuleB); + + qm.bind(notificationRuleA, List.of(tagFoo)); + // NB: Not assigning notificationRuleB + final Response response = jersey.target(V1_TAG) .request() .header(X_API_KEY, apiKey) @@ -98,12 +113,14 @@ public void getTagsTest() { { "name": "bar", "projectCount": 1, - "policyCount": 1 + "policyCount": 1, + "notificationRuleCount": 0 }, { "name": "foo", "projectCount": 2, - "policyCount": 0 + "policyCount": 0, + "notificationRuleCount": 1 } ] """); @@ -130,17 +147,20 @@ public void getTagsWithPaginationTest() { { "name": "tag-1", "projectCount": 0, - "policyCount": 0 + "policyCount": 0, + "notificationRuleCount": 0 }, { "name": "tag-2", "projectCount": 0, - "policyCount": 0 + "policyCount": 0, + "notificationRuleCount": 0 }, { "name": "tag-3", "projectCount": 0, - "policyCount": 0 + "policyCount": 0, + "notificationRuleCount": 0 } ] """); @@ -158,12 +178,14 @@ public void getTagsWithPaginationTest() { { "name": "tag-4", "projectCount": 0, - "policyCount": 0 + "policyCount": 0, + "notificationRuleCount": 0 }, { "name": "tag-5", "projectCount": 0, - "policyCount": 0 + "policyCount": 0, + "notificationRuleCount": 0 } ] """); @@ -188,7 +210,8 @@ public void getTagsWithFilterTest() { { "name": "foo", "projectCount": 0, - "policyCount": 0 + "policyCount": 0, + "notificationRuleCount": 0 } ] """); @@ -225,12 +248,14 @@ public void getTagsSortByProjectCountTest() { { "name": "foo", "projectCount": 2, - "policyCount": 0 + "policyCount": 0, + "notificationRuleCount": 0 }, { "name": "bar", "projectCount": 1, - "policyCount": 0 + "policyCount": 0, + "notificationRuleCount": 0 } ] """); @@ -451,6 +476,69 @@ public void deleteTagsWhenAssignedToPolicyWithoutPolicyManagementPermissionTest( assertThat(qm.getTagByName("bar")).isNotNull(); } + @Test + public void deleteTagsWhenAssignedToNotificationRuleTest() { + initializeWithPermissions(Permissions.TAG_MANAGEMENT, Permissions.SYSTEM_CONFIGURATION); + + final Tag unusedTag = qm.createTag("foo"); + final Tag usedTag = qm.createTag("bar"); + + final var notificationRule = new NotificationRule(); + notificationRule.setName("rule"); + notificationRule.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRule); + + qm.bind(notificationRule, List.of(usedTag)); + + final Response response = jersey.target(V1_TAG) + .request() + .header(X_API_KEY, apiKey) + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true) + .method(HttpMethod.DELETE, Entity.json(List.of(unusedTag.getName(), usedTag.getName()))); + assertThat(response.getStatus()).isEqualTo(204); + + qm.getPersistenceManager().evictAll(); + assertThat(qm.getTagByName("foo")).isNull(); + assertThat(qm.getTagByName("bar")).isNull(); + } + + @Test + public void deleteTagsWhenAssignedToNotificationRuleWithoutSystemConfigurationPermissionTest() { + initializeWithPermissions(Permissions.TAG_MANAGEMENT); + + final Tag unusedTag = qm.createTag("foo"); + final Tag usedTag = qm.createTag("bar"); + + final var notificationRule = new NotificationRule(); + notificationRule.setName("rule"); + notificationRule.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRule); + + qm.bind(notificationRule, List.of(usedTag)); + + final Response response = jersey.target(V1_TAG) + .request() + .header(X_API_KEY, apiKey) + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true) + .method(HttpMethod.DELETE, Entity.json(List.of(unusedTag.getName(), usedTag.getName()))); + assertThat(response.getStatus()).isEqualTo(400); + assertThat(response.getHeaderString("Content-Type")).isEqualTo("application/problem+json"); + assertThatJson(getPlainTextBody(response)).isEqualTo(""" + { + "status": 400, + "title": "Tag operation failed", + "detail": "The tag(s) bar could not be deleted", + "errors": { + "bar": "The tag is assigned to 1 notification rules, but the authenticated principal is missing the SYSTEM_CONFIGURATION permission." + } + } + """); + + qm.getPersistenceManager().evictAll(); + assertThat(qm.getTagByName("foo")).isNotNull(); + assertThat(qm.getTagByName("bar")).isNotNull(); + } + @Test public void getTaggedProjectsTest() { initializeWithPermissions(Permissions.VIEW_PORTFOLIO); @@ -587,6 +675,8 @@ public void getTaggedProjectsWithTagNotExistsTest() { public void getTaggedProjectsWithNonLowerCaseTagNameTest() { initializeWithPermissions(Permissions.VIEW_PORTFOLIO); + qm.createTag("foo"); + final Response response = jersey.target(V1_TAG + "/Foo/project") .request() .header(X_API_KEY, apiKey) @@ -1007,6 +1097,8 @@ public void getTaggedPoliciesWithTagNotExistsTest() { public void getTaggedPoliciesWithNonLowerCaseTagNameTest() { initializeWithPermissions(Permissions.VIEW_PORTFOLIO); + qm.createTag("foo"); + final Response response = jersey.target(V1_TAG + "/Foo/policy") .request() .header(X_API_KEY, apiKey) @@ -1278,6 +1370,338 @@ public void getTagsForPolicyWithPolicyProjectsFilterTest() { Assert.assertEquals("tag 1", json.getJsonObject(0).getString("name")); } + @Test + public void getTaggedNotificationRulesTest() { + initializeWithPermissions(Permissions.SYSTEM_CONFIGURATION); + + final Tag tagFoo = qm.createTag("foo"); + final Tag tagBar = qm.createTag("bar"); + + final var notificationRuleA = new NotificationRule(); + notificationRuleA.setName("rule-a"); + notificationRuleA.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRuleA); + + final var notificationRuleB = new NotificationRule(); + notificationRuleB.setName("rule-b"); + notificationRuleB.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRuleB); + + qm.bind(notificationRuleA, List.of(tagFoo)); + qm.bind(notificationRuleB, List.of(tagBar)); + + final Response response = jersey.target(V1_TAG + "/foo/notificationRule") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(200); + assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("1"); + assertThatJson(getPlainTextBody(response)) + .withMatcher("notificationRuleUuidA", equalTo(notificationRuleA.getUuid().toString())) + .isEqualTo(""" + [ + { + "uuid": "${json-unit.matches:notificationRuleUuidA}", + "name": "rule-a" + } + ] + """); + } + + @Test + public void getTaggedNotificationRulesWithPaginationTest() { + initializeWithPermissions(Permissions.SYSTEM_CONFIGURATION); + + final Tag tag = qm.createTag("foo"); + + for (int i = 0; i < 5; i++) { + final var notificationRule = new NotificationRule(); + notificationRule.setName("rule-" + (i+1)); + notificationRule.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRule); + + qm.bind(notificationRule, List.of(tag)); + } + + Response response = jersey.target(V1_TAG + "/foo/notificationRule") + .queryParam("pageNumber", "1") + .queryParam("pageSize", "3") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(200); + assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("5"); + assertThatJson(getPlainTextBody(response)).isEqualTo(""" + [ + { + "uuid": "${json-unit.any-string}", + "name": "rule-1" + }, + { + "uuid": "${json-unit.any-string}", + "name": "rule-2" + }, + { + "uuid": "${json-unit.any-string}", + "name": "rule-3" + } + ] + """); + + response = jersey.target(V1_TAG + "/foo/notificationRule") + .queryParam("pageNumber", "2") + .queryParam("pageSize", "3") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(200); + assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("5"); + assertThatJson(getPlainTextBody(response)).isEqualTo(""" + [ + { + "uuid": "${json-unit.any-string}", + "name": "rule-4" + }, + { + "uuid": "${json-unit.any-string}", + "name": "rule-5" + } + ] + """); + } + + @Test + public void getTaggedNotificationRulesWithTagNotExistsTest() { + initializeWithPermissions(Permissions.SYSTEM_CONFIGURATION); + + final Response response = jersey.target(V1_TAG + "/foo/notificationRule") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(200); + assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("0"); + assertThat(getPlainTextBody(response)).isEqualTo("[]"); + } + + @Test + public void getTaggedNotificationRulesWithNonLowerCaseTagNameTest() { + initializeWithPermissions(Permissions.SYSTEM_CONFIGURATION); + + qm.createTag("foo"); + + final Response response = jersey.target(V1_TAG + "/Foo/notificationRule") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(200); + assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("0"); + assertThat(getPlainTextBody(response)).isEqualTo("[]"); + } + + @Test + public void tagNotificationRulesTest() { + initializeWithPermissions(Permissions.SYSTEM_CONFIGURATION); + + final var notificationRuleA = new NotificationRule(); + notificationRuleA.setName("rule-a"); + notificationRuleA.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRuleA); + + final var notificationRuleB = new NotificationRule(); + notificationRuleB.setName("rule-b"); + notificationRuleB.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRuleB); + + qm.createTag("foo"); + + final Response response = jersey.target(V1_TAG + "/foo/notificationRule") + .request() + .header(X_API_KEY, apiKey) + .post(Entity.json(List.of(notificationRuleA.getUuid(), notificationRuleB.getUuid()))); + assertThat(response.getStatus()).isEqualTo(204); + + qm.getPersistenceManager().evictAll(); + assertThat(notificationRuleA.getTags()).satisfiesExactly(ruleTag -> assertThat(ruleTag.getName()).isEqualTo("foo")); + assertThat(notificationRuleB.getTags()).satisfiesExactly(ruleTag -> assertThat(ruleTag.getName()).isEqualTo("foo")); + } + + @Test + public void tagNotificationRulesWithTagNotExistsTest() { + initializeWithPermissions(Permissions.SYSTEM_CONFIGURATION); + + final var notificationRule = new NotificationRule(); + notificationRule.setName("rule"); + notificationRule.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRule); + + final Response response = jersey.target(V1_TAG + "/foo/notificationRule") + .request() + .header(X_API_KEY, apiKey) + .post(Entity.json(List.of(notificationRule.getUuid()))); + assertThat(response.getStatus()).isEqualTo(404); + assertThat(response.getHeaderString("Content-Type")).isEqualTo("application/problem+json"); + assertThatJson(getPlainTextBody(response)).isEqualTo(""" + { + "status": 404, + "title": "Resource does not exist", + "detail": "A tag with name foo does not exist" + } + """); + } + + @Test + public void tagNotificationRulesWithNoRuleUuidsTest() { + initializeWithPermissions(Permissions.SYSTEM_CONFIGURATION); + + qm.createTag("foo"); + + final Response response = jersey.target(V1_TAG + "/foo/notificationRule") + .request() + .header(X_API_KEY, apiKey) + .post(Entity.json(Collections.emptyList())); + assertThat(response.getStatus()).isEqualTo(400); + assertThatJson(getPlainTextBody(response)).isEqualTo(""" + [ + { + "message": "size must be between 1 and 100", + "messageTemplate": "{jakarta.validation.constraints.Size.message}", + "path": "tagNotificationRules.arg1", + "invalidValue": "[]" + } + ] + """); + } + + @Test + public void untagNotificationRulesTest() { + initializeWithPermissions(Permissions.SYSTEM_CONFIGURATION); + + final var notificationRuleA = new NotificationRule(); + notificationRuleA.setName("rule-a"); + notificationRuleA.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRuleA); + + final var notificationRuleB = new NotificationRule(); + notificationRuleB.setName("rule-b"); + notificationRuleB.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRuleB); + + final Tag tag = qm.createTag("foo"); + qm.bind(notificationRuleA, List.of(tag)); + qm.bind(notificationRuleB, List.of(tag)); + + final Response response = jersey.target(V1_TAG + "/foo/notificationRule") + .request() + .header(X_API_KEY, apiKey) + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true) + .method(HttpMethod.DELETE, Entity.json(List.of(notificationRuleA.getUuid(), notificationRuleB.getUuid()))); + assertThat(response.getStatus()).isEqualTo(204); + + qm.getPersistenceManager().evictAll(); + assertThat(notificationRuleA.getTags()).isEmpty(); + assertThat(notificationRuleB.getTags()).isEmpty(); + } + + @Test + public void untagNotificationRulesWithTagNotExistsTest() { + initializeWithPermissions(Permissions.SYSTEM_CONFIGURATION); + + final var notificationRule = new NotificationRule(); + notificationRule.setName("rule"); + notificationRule.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRule); + + final Response response = jersey.target(V1_TAG + "/foo/notificationRule") + .request() + .header(X_API_KEY, apiKey) + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true) + .method(HttpMethod.DELETE, Entity.json(List.of(notificationRule.getUuid()))); + assertThat(response.getStatus()).isEqualTo(404); + assertThat(response.getHeaderString("Content-Type")).isEqualTo("application/problem+json"); + assertThatJson(getPlainTextBody(response)).isEqualTo(""" + { + "status": 404, + "title": "Resource does not exist", + "detail": "A tag with name foo does not exist" + } + """); + } + + @Test + public void untagNotificationRulesWithNoProjectUuidsTest() { + initializeWithPermissions(Permissions.SYSTEM_CONFIGURATION); + + qm.createTag("foo"); + + final Response response = jersey.target(V1_TAG + "/foo/notificationRule") + .request() + .header(X_API_KEY, apiKey) + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true) + .method(HttpMethod.DELETE, Entity.json(Collections.emptyList())); + assertThat(response.getStatus()).isEqualTo(400); + assertThatJson(getPlainTextBody(response)).isEqualTo(""" + [ + { + "message": "size must be between 1 and 100", + "messageTemplate": "{jakarta.validation.constraints.Size.message}", + "path": "untagNotificationRules.arg1", + "invalidValue": "[]" + } + ] + """); + } + + @Test + public void untagNotificationRulesWithTooManyRuleUuidsTest() { + initializeWithPermissions(Permissions.SYSTEM_CONFIGURATION); + + qm.createTag("foo"); + + final List policyUuids = IntStream.range(0, 101) + .mapToObj(ignored -> UUID.randomUUID()) + .map(UUID::toString) + .toList(); + + final Response response = jersey.target(V1_TAG + "/foo/notificationRule") + .request() + .header(X_API_KEY, apiKey) + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true) + .method(HttpMethod.DELETE, Entity.json(policyUuids)); + assertThat(response.getStatus()).isEqualTo(400); + assertThatJson(getPlainTextBody(response)).isEqualTo(""" + [ + { + "message": "size must be between 1 and 100", + "messageTemplate": "{jakarta.validation.constraints.Size.message}", + "path": "untagNotificationRules.arg1", + "invalidValue": "${json-unit.any-string}" + } + ] + """); + } + + @Test + public void untagNotificationRulesWhenNotTaggedTest() { + initializeWithPermissions(Permissions.SYSTEM_CONFIGURATION); + + final var notificationRule = new NotificationRule(); + notificationRule.setName("rule"); + notificationRule.setScope(NotificationScope.PORTFOLIO); + qm.persist(notificationRule); + + qm.createTag("foo"); + + final Response response = jersey.target(V1_TAG + "/foo/notificationRule") + .request() + .header(X_API_KEY, apiKey) + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true) + .method(HttpMethod.DELETE, Entity.json(List.of(notificationRule.getUuid()))); + assertThat(response.getStatus()).isEqualTo(204); + + qm.getPersistenceManager().evictAll(); + assertThat(notificationRule.getTags()).isEmpty(); + } + @Test public void getTagWithNonUuidNameTest() { initializeWithPermissions(Permissions.VIEW_PORTFOLIO); From 31cc33ef5b289bf3e26360e5337162fa11c3ef0b Mon Sep 17 00:00:00 2001 From: nscuro Date: Sat, 31 Aug 2024 23:35:57 +0200 Subject: [PATCH 099/429] Ensure URL-encoding of repository URL path segments PURLs containing special characters such as spaces could render the constructed repository URL invalid. Fixes #3688 Signed-off-by: nscuro --- .../dependencytrack/tasks/repositories/CargoMetaAnalyzer.java | 2 +- .../tasks/repositories/ComposerMetaAnalyzer.java | 2 +- .../dependencytrack/tasks/repositories/CpanMetaAnalyzer.java | 4 ++-- .../dependencytrack/tasks/repositories/GemMetaAnalyzer.java | 2 +- .../tasks/repositories/GithubMetaAnalyzer.java | 2 +- .../tasks/repositories/GoModulesMetaAnalyzer.java | 2 +- .../tasks/repositories/HackageMetaAnalyzer.java | 2 +- .../dependencytrack/tasks/repositories/HexMetaAnalyzer.java | 4 ++-- .../dependencytrack/tasks/repositories/MavenMetaAnalyzer.java | 2 +- .../dependencytrack/tasks/repositories/NugetMetaAnalyzer.java | 4 ++-- .../dependencytrack/tasks/repositories/PypiMetaAnalyzer.java | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/repositories/CargoMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/CargoMetaAnalyzer.java index d1c65e1bf3..272903e832 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/CargoMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/CargoMetaAnalyzer.java @@ -69,7 +69,7 @@ public RepositoryType supportedRepositoryType() { public MetaModel analyze(final Component component) { final MetaModel meta = new MetaModel(component); if (component.getPurl() != null) { - final String url = String.format(baseUrl + API_URL, component.getPurl().getName()); + final String url = String.format(baseUrl + API_URL, urlEncode(component.getPurl().getName())); try (final CloseableHttpResponse response = processHttpRequest(url)) { if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { final HttpEntity entity = response.getEntity(); diff --git a/src/main/java/org/dependencytrack/tasks/repositories/ComposerMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/ComposerMetaAnalyzer.java index bb94b022c2..7b16722af7 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/ComposerMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/ComposerMetaAnalyzer.java @@ -77,7 +77,7 @@ public MetaModel analyze(final Component component) { return meta; } - final String url = String.format(baseUrl + API_URL, component.getPurl().getNamespace(), component.getPurl().getName()); + final String url = String.format(baseUrl + API_URL, urlEncode(component.getPurl().getNamespace()), urlEncode(component.getPurl().getName())); try (final CloseableHttpResponse response = processHttpRequest(url)) { if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { handleUnexpectedHttpResponse(LOGGER, url, response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase(), component); diff --git a/src/main/java/org/dependencytrack/tasks/repositories/CpanMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/CpanMetaAnalyzer.java index 9c18c78f2d..8559201a96 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/CpanMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/CpanMetaAnalyzer.java @@ -66,9 +66,9 @@ public MetaModel analyze(final Component component) { final String packageName; if (component.getPurl().getNamespace() != null) { - packageName = component.getPurl().getNamespace() + "%2F" + component.getPurl().getName(); + packageName = urlEncode(component.getPurl().getNamespace() + "/" + component.getPurl().getName()); } else { - packageName = component.getPurl().getName(); + packageName = urlEncode(component.getPurl().getName()); } final String url = String.format(baseUrl + API_URL, packageName); diff --git a/src/main/java/org/dependencytrack/tasks/repositories/GemMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/GemMetaAnalyzer.java index a9b10c9f3b..4e05a36bbf 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/GemMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/GemMetaAnalyzer.java @@ -66,7 +66,7 @@ public RepositoryType supportedRepositoryType() { public MetaModel analyze(final Component component) { final MetaModel meta = new MetaModel(component); if (component.getPurl() != null) { - final String url = String.format(baseUrl + API_URL, component.getPurl().getName()); + final String url = String.format(baseUrl + API_URL, urlEncode(component.getPurl().getName())); try (final CloseableHttpResponse response = processHttpRequest(url)) { if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){ if(response.getEntity()!=null){ diff --git a/src/main/java/org/dependencytrack/tasks/repositories/GithubMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/GithubMetaAnalyzer.java index d3f0b86be2..a717801862 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/GithubMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/GithubMetaAnalyzer.java @@ -126,7 +126,7 @@ public MetaModel analyze(final Component component) { github = GitHub.connectAnonymously(); } - GHRepository repository = github.getRepository(String.format("%s/%s", component.getPurl().getNamespace(), component.getPurl().getName())); + GHRepository repository = github.getRepository(String.format("%s/%s", urlEncode(component.getPurl().getNamespace()), urlEncode(component.getPurl().getName()))); LOGGER.debug(String.format("Repos is at %s", repository.getUrl())); final VersionType version_type = get_version_type(component, repository); diff --git a/src/main/java/org/dependencytrack/tasks/repositories/GoModulesMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/GoModulesMetaAnalyzer.java index 7e1b5ce7b4..ff605c9190 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/GoModulesMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/GoModulesMetaAnalyzer.java @@ -64,7 +64,7 @@ public MetaModel analyze(final Component component) { if (component.getPurl() == null || component.getPurl().getNamespace() == null) { return meta; } - final String url = String.format(baseUrl + API_URL, caseEncode(component.getPurl().getNamespace()), caseEncode(component.getPurl().getName())); + final String url = String.format(baseUrl + API_URL, urlEncode(caseEncode(component.getPurl().getNamespace())), urlEncode(caseEncode(component.getPurl().getName()))); try (final CloseableHttpResponse response = processHttpRequest(url)) { if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { diff --git a/src/main/java/org/dependencytrack/tasks/repositories/HackageMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/HackageMetaAnalyzer.java index a65c21b8b3..8c21a90635 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/HackageMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/HackageMetaAnalyzer.java @@ -59,7 +59,7 @@ public MetaModel analyze(final Component component) { final var meta = new MetaModel(component); final var purl = component.getPurl(); if (purl != null) { - final var url = baseUrl + "/package/" + purl.getName() + "/preferred"; + final var url = baseUrl + "/package/" + urlEncode(purl.getName()) + "/preferred"; try (final CloseableHttpResponse response = processHttpRequest(url)) { if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { final var entity = response.getEntity(); diff --git a/src/main/java/org/dependencytrack/tasks/repositories/HexMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/HexMetaAnalyzer.java index f0b7b9753e..9babd67c9c 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/HexMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/HexMetaAnalyzer.java @@ -74,9 +74,9 @@ public MetaModel analyze(final Component component) { final String packageName; if (component.getPurl().getNamespace() != null) { - packageName = component.getPurl().getNamespace().replace("@", "%40") + "%2F" + component.getPurl().getName(); + packageName = urlEncode(component.getPurl().getNamespace() + "/" + component.getPurl().getName()); } else { - packageName = component.getPurl().getName(); + packageName = urlEncode(component.getPurl().getName()); } final String url = String.format(baseUrl + API_URL, packageName); diff --git a/src/main/java/org/dependencytrack/tasks/repositories/MavenMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/MavenMetaAnalyzer.java index bf50bc73c2..3a23786df5 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/MavenMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/MavenMetaAnalyzer.java @@ -76,7 +76,7 @@ public RepositoryType supportedRepositoryType() { public MetaModel analyze(final Component component) { final MetaModel meta = new MetaModel(component); if (component.getPurl() != null) { - final String mavenGavUrl = component.getPurl().getNamespace().replaceAll("\\.", "/") + "/" + component.getPurl().getName(); + final String mavenGavUrl = urlEncode(component.getPurl().getNamespace().replaceAll("\\.", "/")) + "/" + urlEncode(component.getPurl().getName()); final String url = String.format(baseUrl + REPO_METADATA_URL, mavenGavUrl); try (final CloseableHttpResponse response = processHttpRequest(url)) { if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { diff --git a/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java index b8559741ae..b15198b5b7 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java @@ -105,7 +105,7 @@ public MetaModel analyze(final Component component) { } private boolean performVersionCheck(final MetaModel meta, final Component component) { - final String url = String.format(versionQueryUrl, component.getPurl().getName().toLowerCase()); + final String url = String.format(versionQueryUrl, urlEncode(component.getPurl().getName().toLowerCase())); try (final CloseableHttpResponse response = processHttpRequest(url)) { if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { if (response.getEntity() != null) { @@ -145,7 +145,7 @@ private String findLatestVersion(JSONArray versions) { } private boolean performLastPublishedCheck(final MetaModel meta, final Component component) { - final String url = String.format(registrationUrl, component.getPurl().getName().toLowerCase(), meta.getLatestVersion()); + final String url = String.format(registrationUrl, urlEncode(component.getPurl().getName().toLowerCase()), urlEncode(meta.getLatestVersion())); try (final CloseableHttpResponse response = processHttpRequest(url)) { if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { if (response.getEntity() != null) { diff --git a/src/main/java/org/dependencytrack/tasks/repositories/PypiMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/PypiMetaAnalyzer.java index 4ebc199d69..115653ff97 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/PypiMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/PypiMetaAnalyzer.java @@ -71,7 +71,7 @@ public RepositoryType supportedRepositoryType() { public MetaModel analyze(final Component component) { final MetaModel meta = new MetaModel(component); if (component.getPurl() != null) { - final String url = String.format(baseUrl + API_URL, component.getPurl().getName()); + final String url = String.format(baseUrl + API_URL, urlEncode(component.getPurl().getName())); try (final CloseableHttpResponse response = processHttpRequest(url)) { if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { if (response.getEntity() != null) { From ee89410319ccd805a430a52fe2ff226430600304 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sat, 31 Aug 2024 23:44:20 +0200 Subject: [PATCH 100/429] Fix project being rendered as PURL in email notifications See https://github.com/DependencyTrack/dependency-track/issues/3978#issuecomment-2323046771 for details. Fixes #3978 Signed-off-by: nscuro --- src/main/resources/templates/notification/publisher/email.peb | 4 ++-- .../notification/publisher/SendMailPublisherTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/templates/notification/publisher/email.peb b/src/main/resources/templates/notification/publisher/email.peb index 2e09ce5fca..eb435d439d 100644 --- a/src/main/resources/templates/notification/publisher/email.peb +++ b/src/main/resources/templates/notification/publisher/email.peb @@ -20,7 +20,7 @@ Other affected projects: Project: [{{ affectedProject.name }} : {{ affectedProject.version }}] Project URL: {{ baseUrl }}/projects/{{ affectedProject.uuid }} {% endif %}{% endfor %}{% endif %}{% elseif notification.group == "NEW_VULNERABLE_DEPENDENCY" %} -Project: {{ subject.component.project.toString }} +Project: [{{ subject.component.project.name }} : {{ subject.component.project.version }}] Project URL: {{ baseUrl }}/projects/{{ subject.component.project.uuid }} Component: {{ subject.component.toString }} Component URL: {{ baseUrl }}/component/?uuid={{ subject.component.uuid }} @@ -55,7 +55,7 @@ Source: {{ subject.vulnerability.source }} {% endif %} Component: {{ subject.component.toString }} Component URL: {{ baseUrl }}/component/?uuid={{ subject.component.uuid }} -Project: {{ subject.component.project.toString }} +Project: [{{ subject.component.project.name }} : {{ subject.component.project.version }}] Description: {{ subject.component.project.description }} Project URL: {{ baseUrl }}/projects/{{ subject.component.project.uuid }} {% if notification.subject.affectedProjects|length > 1%} diff --git a/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java b/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java index 4b038b2ec8..192578f1af 100644 --- a/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java +++ b/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java @@ -327,7 +327,7 @@ public void testInformWithNewVulnerableDependencyNotification() { -------------------------------------------------------------------------------- - Project: pkg:maven/org.acme/projectName@projectVersion + Project: [projectName : projectVersion] Project URL: /projects/c9c9539a-e381-4b36-ac52-6a7ab83b2c95 Component: componentName : componentVersion Component URL: /component/?uuid=94f87321-a5d1-4c2f-b2fe-95165debebc6 @@ -380,7 +380,7 @@ public void testInformWithProjectAuditChangeNotification() { Component: componentName : componentVersion Component URL: /component/?uuid=94f87321-a5d1-4c2f-b2fe-95165debebc6 - Project: pkg:maven/org.acme/projectName@projectVersion + Project: [projectName : projectVersion] Description: projectDescription Project URL: /projects/c9c9539a-e381-4b36-ac52-6a7ab83b2c95 From 702e43b07e02d4e728714c837de262e193da070c Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 1 Sep 2024 16:08:51 +0200 Subject: [PATCH 101/429] Support inclusion/exclusion of projects from BOM validation with tags Closes #3891 Signed-off-by: nscuro --- .../model/BomValidationMode.java | 34 +++ .../model/ConfigPropertyConstants.java | 14 +- .../v1/AbstractConfigPropertyResource.java | 32 ++ .../resources/v1/BomResource.java | 72 ++++- .../upgrade/v4120/v4120Updater.java | 85 ++++++ .../resources/v1/BomResourceTest.java | 275 +++++++++++++++++- .../v1/ConfigPropertyResourceTest.java | 133 +++++++++ .../resources/v1/VexResourceTest.java | 206 ++++++++++++- .../tasks/BomUploadProcessingTaskTest.java | 12 +- 9 files changed, 820 insertions(+), 43 deletions(-) create mode 100644 src/main/java/org/dependencytrack/model/BomValidationMode.java diff --git a/src/main/java/org/dependencytrack/model/BomValidationMode.java b/src/main/java/org/dependencytrack/model/BomValidationMode.java new file mode 100644 index 0000000000..f155b4c567 --- /dev/null +++ b/src/main/java/org/dependencytrack/model/BomValidationMode.java @@ -0,0 +1,34 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.model; + +/** + * @since 4.12.0 + */ +public enum BomValidationMode { + + ENABLED, + + DISABLED, + + ENABLED_FOR_TAGS, + + DISABLED_FOR_TAGS + +} diff --git a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java index ea1a7151d5..ced4a71cc2 100644 --- a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java +++ b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java @@ -109,13 +109,15 @@ public enum ConfigPropertyConstants { SEARCH_INDEXES_CONSISTENCY_CHECK_ENABLED("search-indexes", "consistency.check.enabled", "true", PropertyType.BOOLEAN, "Flag to enable lucene indexes periodic consistency check"), SEARCH_INDEXES_CONSISTENCY_CHECK_CADENCE("search-indexes", "consistency.check.cadence", "4320", PropertyType.INTEGER, "Lucene indexes consistency check cadence (in minutes)"), SEARCH_INDEXES_CONSISTENCY_CHECK_DELTA_THRESHOLD("search-indexes", "consistency.check.delta.threshold", "20", PropertyType.INTEGER, "Threshold used to trigger an index rebuild when comparing database table and corresponding lucene index (in percentage). It must be an integer between 1 and 100"), - BOM_VALIDATION_ENABLED("artifact", "bom.validation.enabled", "true", PropertyType.BOOLEAN, "Flag to control bom validation"); + BOM_VALIDATION_MODE("artifact", "bom.validation.mode", BomValidationMode.ENABLED.name(), PropertyType.STRING, "Flag to control the BOM validation mode"), + BOM_VALIDATION_TAGS_INCLUSIVE("artifact", "bom.validation.tags.inclusive", "[]", PropertyType.STRING, "JSON array of tags for which BOM validation shall be performed"), + BOM_VALIDATION_TAGS_EXCLUSIVE("artifact", "bom.validation.tags.exclusive", "[]", PropertyType.STRING, "JSON array of tags for which BOM validation shall NOT be performed"); - private String groupName; - private String propertyName; - private String defaultPropertyValue; - private PropertyType propertyType; - private String description; + private final String groupName; + private final String propertyName; + private final String defaultPropertyValue; + private final PropertyType propertyType; + private final String description; ConfigPropertyConstants(String groupName, String propertyName, String defaultPropertyValue, PropertyType propertyType, String description) { this.groupName = groupName; diff --git a/src/main/java/org/dependencytrack/resources/v1/AbstractConfigPropertyResource.java b/src/main/java/org/dependencytrack/resources/v1/AbstractConfigPropertyResource.java index 3c9fb89bd2..440fa28ab5 100644 --- a/src/main/java/org/dependencytrack/resources/v1/AbstractConfigPropertyResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/AbstractConfigPropertyResource.java @@ -24,10 +24,16 @@ import alpine.model.IConfigProperty; import alpine.security.crypto.DataEncryption; import alpine.server.resources.AlpineResource; +import org.dependencytrack.model.BomValidationMode; import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.persistence.QueryManager; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonReader; +import jakarta.json.JsonString; import jakarta.ws.rs.core.Response; +import java.io.StringReader; import java.math.BigDecimal; import java.net.MalformedURLException; import java.net.URI; @@ -121,6 +127,32 @@ private Response updatePropertyValueInternal(IConfigProperty json, IConfigProper } else { property.setPropertyValue(propertyValue); } + } else if (ConfigPropertyConstants.BOM_VALIDATION_MODE.getPropertyName().equals(json.getPropertyName())) { + try { + BomValidationMode.valueOf(json.getPropertyValue()); + property.setPropertyValue(json.getPropertyValue()); + } catch (IllegalArgumentException e) { + return Response + .status(Response.Status.BAD_REQUEST) + .entity("Value must be any of: %s".formatted(Arrays.stream(BomValidationMode.values()).map(Enum::name).collect(Collectors.joining(", ")))) + .build(); + } + } else if (ConfigPropertyConstants.BOM_VALIDATION_TAGS_INCLUSIVE.getPropertyName().equals(json.getPropertyName()) + || ConfigPropertyConstants.BOM_VALIDATION_TAGS_EXCLUSIVE.getPropertyName().equals(json.getPropertyName())) { + try { + final JsonReader jsonReader = Json.createReader(new StringReader(json.getPropertyValue())); + final JsonArray jsonArray = jsonReader.readArray(); + jsonArray.getValuesAs(JsonString::getString); + + // NB: Storing the string representation of the parsed array instead of the original value, + // since this removes any unnecessary whitespace. + property.setPropertyValue(jsonArray.toString()); + } catch (RuntimeException e) { + return Response + .status(Response.Status.BAD_REQUEST) + .entity("Value must be a valid JSON array of strings") + .build(); + } } else { property.setPropertyValue(json.getPropertyValue()); } diff --git a/src/main/java/org/dependencytrack/resources/v1/BomResource.java b/src/main/java/org/dependencytrack/resources/v1/BomResource.java index 0da5464604..9c83022282 100644 --- a/src/main/java/org/dependencytrack/resources/v1/BomResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/BomResource.java @@ -20,6 +20,7 @@ import alpine.common.logging.Logger; import alpine.event.framework.Event; +import alpine.model.ConfigProperty; import alpine.notification.Notification; import alpine.notification.NotificationLevel; import alpine.server.auth.PermissionRequired; @@ -41,6 +42,7 @@ import org.dependencytrack.event.BomUploadEvent; import org.dependencytrack.model.Bom; import org.dependencytrack.model.Bom.Format; +import org.dependencytrack.model.BomValidationMode; import org.dependencytrack.model.Component; import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.Project; @@ -63,6 +65,10 @@ import org.glassfish.jersey.media.multipart.FormDataBodyPart; import org.glassfish.jersey.media.multipart.FormDataParam; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonReader; +import jakarta.json.JsonString; import jakarta.validation.Validator; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DefaultValue; @@ -79,14 +85,19 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.StringReader; import java.security.Principal; import java.util.Arrays; import java.util.Base64; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.UUID; import static java.util.function.Predicate.not; +import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_MODE; +import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_TAGS_EXCLUSIVE; +import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_TAGS_INCLUSIVE; /** * JAX-RS resources for processing bill-of-material (bom) documents. @@ -524,10 +535,8 @@ private Response process(QueryManager qm, Project project, List validationModeTags; + try { + final JsonReader jsonParser = Json.createReader(new StringReader(tagsProperty.getPropertyValue())); + final JsonArray jsonArray = jsonParser.readArray(); + validationModeTags = Set.copyOf(jsonArray.getValuesAs(JsonString::getString)); + } catch (RuntimeException e) { + LOGGER.warn("Tags of property %s:%s could not be parsed as JSON array" + .formatted(tagsPropertyConstant.getGroupName(), tagsPropertyConstant.getPropertyName()), e); + return validationMode == BomValidationMode.DISABLED_FOR_TAGS; + } + + final boolean doTagsMatch = project.getTags().stream() + .map(Tag::getName) + .anyMatch(validationModeTags::contains); + return (validationMode == BomValidationMode.ENABLED_FOR_TAGS && doTagsMatch) + || (validationMode == BomValidationMode.DISABLED_FOR_TAGS && !doTagsMatch); + } + } private static void dispatchBomValidationFailedNotification(final Project project, final String bom, final List errors, final Bom.Format bomFormat) { Notification.dispatch(new Notification() diff --git a/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java b/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java index 3502100f8e..99f97b16f8 100644 --- a/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java +++ b/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java @@ -21,11 +21,15 @@ import alpine.common.logging.Logger; import alpine.persistence.AlpineQueryManager; import alpine.server.upgrade.AbstractUpgradeItem; +import org.dependencytrack.model.BomValidationMode; import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; +import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_MODE; + public class v4120Updater extends AbstractUpgradeItem { private static final Logger LOGGER = Logger.getLogger(v4120Updater.class); @@ -38,6 +42,7 @@ public String getSchemaVersion() { @Override public void executeUpgrade(final AlpineQueryManager qm, final Connection connection) throws Exception { removeExperimentalBomUploadProcessingV2ConfigProperty(connection); + migrateBomValidationConfigProperty(connection); } private static void removeExperimentalBomUploadProcessingV2ConfigProperty(final Connection connection) throws SQLException { @@ -58,4 +63,84 @@ private static void removeExperimentalBomUploadProcessingV2ConfigProperty(final } } + private static void migrateBomValidationConfigProperty(final Connection connection) throws SQLException { + final boolean shouldReEnableAutoCommit = connection.getAutoCommit(); + connection.setAutoCommit(false); + boolean committed = false; + + final String bomValidationEnabledGroupName = "artifact"; + final String bomValidationEnabledPropertyName = "bom.validation.enabled"; + + LOGGER.info("Migrating ConfigProperty %s:%s to %s:%s" + .formatted(bomValidationEnabledGroupName, bomValidationEnabledPropertyName, + BOM_VALIDATION_MODE.getGroupName(), BOM_VALIDATION_MODE.getPropertyName())); + + try { + LOGGER.debug("Determining current value of ConfigProperty %s:%s" + .formatted(bomValidationEnabledGroupName, bomValidationEnabledPropertyName)); + final String validationEnabledValue; + try (final PreparedStatement ps = connection.prepareStatement(""" + SELECT "PROPERTYVALUE" + FROM "CONFIGPROPERTY" + WHERE "GROUPNAME" = ? + AND "PROPERTYNAME" = ? + """)) { + ps.setString(1, bomValidationEnabledGroupName); + ps.setString(2, bomValidationEnabledPropertyName); + final ResultSet rs = ps.executeQuery(); + if (rs.next()) { + validationEnabledValue = rs.getString(1); + } else { + validationEnabledValue = "true"; + } + } + + final BomValidationMode validationModeValue = "false".equals(validationEnabledValue) + ? BomValidationMode.DISABLED + : BomValidationMode.ENABLED; + + LOGGER.debug("Creating ConfigProperty %s:%s with value %s" + .formatted(BOM_VALIDATION_MODE.getGroupName(), BOM_VALIDATION_MODE.getPropertyName(), validationModeValue)); + try (final PreparedStatement ps = connection.prepareStatement(""" + INSERT INTO "CONFIGPROPERTY" ( + "DESCRIPTION" + , "GROUPNAME" + , "PROPERTYNAME" + , "PROPERTYTYPE" + , "PROPERTYVALUE" + ) VALUES (?, ?, ?, ?, ?) + """)) { + ps.setString(1, BOM_VALIDATION_MODE.getDescription()); + ps.setString(2, BOM_VALIDATION_MODE.getGroupName()); + ps.setString(3, BOM_VALIDATION_MODE.getPropertyName()); + ps.setString(4, BOM_VALIDATION_MODE.getPropertyType().name()); + ps.setString(5, validationModeValue.name()); + ps.executeUpdate(); + } + + LOGGER.debug("Removing ConfigProperty %s:%s".formatted(bomValidationEnabledGroupName, bomValidationEnabledPropertyName)); + try (final PreparedStatement ps = connection.prepareStatement(""" + DELETE + FROM "CONFIGPROPERTY" + WHERE "GROUPNAME" = ? + AND "PROPERTYNAME" = ? + """)) { + ps.setString(1, bomValidationEnabledGroupName); + ps.setString(2, bomValidationEnabledPropertyName); + ps.executeUpdate(); + } + + connection.commit(); + committed = true; + } finally { + if (!committed) { + connection.rollback(); + } + + if (shouldReEnableAutoCommit) { + connection.setAutoCommit(true); + } + } + } + } diff --git a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java index ecbbccfcd8..48b5015910 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java @@ -29,6 +29,7 @@ import org.dependencytrack.auth.Permissions; import org.dependencytrack.model.AnalysisResponse; import org.dependencytrack.model.AnalysisState; +import org.dependencytrack.model.BomValidationMode; import org.dependencytrack.model.Classifier; import org.dependencytrack.model.Component; import org.dependencytrack.model.ComponentProperty; @@ -61,6 +62,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Base64; +import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @@ -72,7 +74,9 @@ import static org.apache.commons.io.IOUtils.resourceToString; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_ENABLED; +import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_MODE; +import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_TAGS_EXCLUSIVE; +import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_TAGS_INCLUSIVE; import static org.hamcrest.CoreMatchers.equalTo; public class BomResourceTest extends ResourceTest { @@ -1018,11 +1022,11 @@ public void uploadBomInvalidJsonTest() { initializeWithPermissions(Permissions.BOM_UPLOAD); qm.createConfigProperty( - BOM_VALIDATION_ENABLED.getGroupName(), - BOM_VALIDATION_ENABLED.getPropertyName(), - "true", - BOM_VALIDATION_ENABLED.getPropertyType(), - null + BOM_VALIDATION_MODE.getGroupName(), + BOM_VALIDATION_MODE.getPropertyName(), + BomValidationMode.ENABLED.name(), + BOM_VALIDATION_MODE.getPropertyType(), + BOM_VALIDATION_MODE.getDescription() ); final var project = new Project(); @@ -1074,11 +1078,11 @@ public void uploadBomInvalidXmlTest() { initializeWithPermissions(Permissions.BOM_UPLOAD); qm.createConfigProperty( - BOM_VALIDATION_ENABLED.getGroupName(), - BOM_VALIDATION_ENABLED.getPropertyName(), - "true", - BOM_VALIDATION_ENABLED.getPropertyType(), - null + BOM_VALIDATION_MODE.getGroupName(), + BOM_VALIDATION_MODE.getPropertyName(), + BomValidationMode.ENABLED.name(), + BOM_VALIDATION_MODE.getPropertyType(), + BOM_VALIDATION_MODE.getDescription() ); final var project = new Project(); @@ -1122,6 +1126,255 @@ public void uploadBomInvalidXmlTest() { """); } + @Test + public void uploadBomWithValidationModeDisabledTest() { + initializeWithPermissions(Permissions.BOM_UPLOAD); + + qm.createConfigProperty( + BOM_VALIDATION_MODE.getGroupName(), + BOM_VALIDATION_MODE.getPropertyName(), + BomValidationMode.DISABLED.name(), + BOM_VALIDATION_MODE.getPropertyType(), + BOM_VALIDATION_MODE.getDescription() + ); + + final var project = new Project(); + project.setName("acme-app"); + project.setVersion("1.0.0"); + qm.persist(project); + + final String encodedBom = Base64.getEncoder().encodeToString(""" + { + "bomFormat": "CycloneDX", + "specVersion": "1.2", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "foo", + "name": "acme-library", + "version": "1.0.0" + } + ] + } + """.getBytes()); + + final Response response = jersey.target(V1_BOM).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(""" + { + "projectName": "acme-app", + "projectVersion": "1.0.0", + "bom": "%s" + } + """.formatted(encodedBom), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(200); + } + + @Test + public void uploadBomWithValidationModeEnabledForTagsTest() { + initializeWithPermissions(Permissions.BOM_UPLOAD); + + qm.createConfigProperty( + BOM_VALIDATION_MODE.getGroupName(), + BOM_VALIDATION_MODE.getPropertyName(), + BomValidationMode.ENABLED_FOR_TAGS.name(), + BOM_VALIDATION_MODE.getPropertyType(), + BOM_VALIDATION_MODE.getDescription() + ); + qm.createConfigProperty( + BOM_VALIDATION_TAGS_INCLUSIVE.getGroupName(), + BOM_VALIDATION_TAGS_INCLUSIVE.getPropertyName(), + "[\"foo\"]", + BOM_VALIDATION_TAGS_INCLUSIVE.getPropertyType(), + BOM_VALIDATION_TAGS_INCLUSIVE.getDescription() + ); + + final var project = new Project(); + project.setName("acme-app"); + project.setVersion("1.0.0"); + qm.persist(project); + + qm.bind(project, List.of(qm.createTag("foo"))); + + final String encodedBom = Base64.getEncoder().encodeToString(""" + { + "bomFormat": "CycloneDX", + "specVersion": "1.2", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "foo", + "name": "acme-library", + "version": "1.0.0" + } + ] + } + """.getBytes()); + + Response response = jersey.target(V1_BOM).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(""" + { + "projectName": "acme-app", + "projectVersion": "1.0.0", + "bom": "%s" + } + """.formatted(encodedBom), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(400); + + qm.bind(project, Collections.emptyList()); + + response = jersey.target(V1_BOM).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(""" + { + "projectName": "acme-app", + "projectVersion": "1.0.0", + "bom": "%s" + } + """.formatted(encodedBom), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(200); + } + + @Test + public void uploadBomWithValidationModeDisabledForTagsTest() { + initializeWithPermissions(Permissions.BOM_UPLOAD); + + qm.createConfigProperty( + BOM_VALIDATION_MODE.getGroupName(), + BOM_VALIDATION_MODE.getPropertyName(), + BomValidationMode.DISABLED_FOR_TAGS.name(), + BOM_VALIDATION_MODE.getPropertyType(), + BOM_VALIDATION_MODE.getDescription() + ); + qm.createConfigProperty( + BOM_VALIDATION_TAGS_EXCLUSIVE.getGroupName(), + BOM_VALIDATION_TAGS_EXCLUSIVE.getPropertyName(), + "[\"foo\"]", + BOM_VALIDATION_TAGS_EXCLUSIVE.getPropertyType(), + BOM_VALIDATION_TAGS_EXCLUSIVE.getDescription() + ); + + final var project = new Project(); + project.setName("acme-app"); + project.setVersion("1.0.0"); + qm.persist(project); + + qm.bind(project, List.of(qm.createTag("foo"))); + + final String encodedBom = Base64.getEncoder().encodeToString(""" + { + "bomFormat": "CycloneDX", + "specVersion": "1.2", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "foo", + "name": "acme-library", + "version": "1.0.0" + } + ] + } + """.getBytes()); + + Response response = jersey.target(V1_BOM).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(""" + { + "projectName": "acme-app", + "projectVersion": "1.0.0", + "bom": "%s" + } + """.formatted(encodedBom), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(200); + + qm.bind(project, Collections.emptyList()); + + response = jersey.target(V1_BOM).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(""" + { + "projectName": "acme-app", + "projectVersion": "1.0.0", + "bom": "%s" + } + """.formatted(encodedBom), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(400); + } + + @Test + public void uploadBomWithValidationTagsInvalidTest() { + initializeWithPermissions(Permissions.BOM_UPLOAD); + + qm.createConfigProperty( + BOM_VALIDATION_MODE.getGroupName(), + BOM_VALIDATION_MODE.getPropertyName(), + BomValidationMode.ENABLED_FOR_TAGS.name(), + BOM_VALIDATION_MODE.getPropertyType(), + BOM_VALIDATION_MODE.getDescription() + ); + qm.createConfigProperty( + BOM_VALIDATION_TAGS_INCLUSIVE.getGroupName(), + BOM_VALIDATION_TAGS_INCLUSIVE.getPropertyName(), + "invalid", + BOM_VALIDATION_TAGS_INCLUSIVE.getPropertyType(), + BOM_VALIDATION_TAGS_INCLUSIVE.getDescription() + ); + + final var project = new Project(); + project.setName("acme-app"); + project.setVersion("1.0.0"); + qm.persist(project); + + qm.bind(project, List.of(qm.createTag("foo"))); + + final String encodedBom = Base64.getEncoder().encodeToString(""" + { + "bomFormat": "CycloneDX", + "specVersion": "1.2", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "foo", + "name": "acme-library", + "version": "1.0.0" + } + ] + } + """.getBytes()); + + // With validation mode ENABLED_FOR_TAGS, and invalid tags, + // should fall back to NOT validating. + Response response = jersey.target(V1_BOM).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(""" + { + "projectName": "acme-app", + "projectVersion": "1.0.0", + "bom": "%s" + } + """.formatted(encodedBom), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(200); + + qm.bind(project, Collections.emptyList()); + + // Removal of the project tag should not make a difference. + response = jersey.target(V1_BOM).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(""" + { + "projectName": "acme-app", + "projectVersion": "1.0.0", + "bom": "%s" + } + """.formatted(encodedBom), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(200); + } + @Test public void uploadBomTooLargeViaPutTest() { initializeWithPermissions(Permissions.BOM_UPLOAD); diff --git a/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java index ed2a63d868..b316c03b79 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java @@ -37,6 +37,9 @@ import jakarta.ws.rs.core.Response; import java.util.Arrays; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; +import static org.assertj.core.api.Assertions.assertThat; + public class ConfigPropertyResourceTest extends ResourceTest { @ClassRule @@ -250,4 +253,134 @@ public void updateConfigPropertyOsvEcosystemTest() { Assert.assertEquals(ConfigPropertyConstants.VULNERABILITY_SOURCE_GOOGLE_OSV_ENABLED.getPropertyName(), json.getString("propertyName")); Assert.assertEquals("maven;npm", json.getString("propertyValue")); } + + @Test + public void updateConfigPropertyBomValidationModeTest() { + qm.createConfigProperty( + ConfigPropertyConstants.BOM_VALIDATION_MODE.getGroupName(), + ConfigPropertyConstants.BOM_VALIDATION_MODE.getPropertyName(), + ConfigPropertyConstants.BOM_VALIDATION_MODE.getDefaultPropertyValue(), + ConfigPropertyConstants.BOM_VALIDATION_MODE.getPropertyType(), + ConfigPropertyConstants.BOM_VALIDATION_MODE.getDescription() + ); + + Response response = jersey.target(V1_CONFIG_PROPERTY).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(/* language=JSON */ """ + { + "groupName": "artifact", + "propertyName": "bom.validation.mode", + "propertyValue": "ENABLED_FOR_TAGS" + } + """, MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(200); + assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """ + { + "groupName": "artifact", + "propertyName": "bom.validation.mode", + "propertyValue": "ENABLED_FOR_TAGS", + "propertyType": "STRING", + "description": "${json-unit.any-string}" + } + """); + + response = jersey.target(V1_CONFIG_PROPERTY).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(/* language=JSON */ """ + { + "groupName": "artifact", + "propertyName": "bom.validation.mode", + "propertyValue": "foo" + } + """, MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(400); + assertThat(getPlainTextBody(response)).isEqualTo("Value must be any of: ENABLED, DISABLED, ENABLED_FOR_TAGS, DISABLED_FOR_TAGS"); + } + + @Test + public void updateConfigPropertyBomValidationTagsExclusiveTest() { + qm.createConfigProperty( + ConfigPropertyConstants.BOM_VALIDATION_TAGS_EXCLUSIVE.getGroupName(), + ConfigPropertyConstants.BOM_VALIDATION_TAGS_EXCLUSIVE.getPropertyName(), + ConfigPropertyConstants.BOM_VALIDATION_TAGS_EXCLUSIVE.getDefaultPropertyValue(), + ConfigPropertyConstants.BOM_VALIDATION_TAGS_EXCLUSIVE.getPropertyType(), + ConfigPropertyConstants.BOM_VALIDATION_TAGS_EXCLUSIVE.getDescription() + ); + + Response response = jersey.target(V1_CONFIG_PROPERTY).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(/* language=JSON */ """ + { + "groupName": "artifact", + "propertyName": "bom.validation.tags.exclusive", + "propertyValue": "[\\"foo\\"]" + } + """, MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(200); + assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """ + { + "groupName": "artifact", + "propertyName": "bom.validation.tags.exclusive", + "propertyValue": "[\\"foo\\"]", + "propertyType": "STRING", + "description": "${json-unit.any-string}" + } + """); + + response = jersey.target(V1_CONFIG_PROPERTY).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(/* language=JSON */ """ + { + "groupName": "artifact", + "propertyName": "bom.validation.tags.exclusive", + "propertyValue": "foo" + } + """, MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(400); + assertThat(getPlainTextBody(response)).isEqualTo("Value must be a valid JSON array of strings"); + } + + @Test + public void updateConfigPropertyBomValidationTagsInclusiveTest() { + qm.createConfigProperty( + ConfigPropertyConstants.BOM_VALIDATION_TAGS_INCLUSIVE.getGroupName(), + ConfigPropertyConstants.BOM_VALIDATION_TAGS_INCLUSIVE.getPropertyName(), + ConfigPropertyConstants.BOM_VALIDATION_TAGS_INCLUSIVE.getDefaultPropertyValue(), + ConfigPropertyConstants.BOM_VALIDATION_TAGS_INCLUSIVE.getPropertyType(), + ConfigPropertyConstants.BOM_VALIDATION_TAGS_INCLUSIVE.getDescription() + ); + + Response response = jersey.target(V1_CONFIG_PROPERTY).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(/* language=JSON */ """ + { + "groupName": "artifact", + "propertyName": "bom.validation.tags.inclusive", + "propertyValue": "[\\"foo\\"]" + } + """, MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(200); + assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """ + { + "groupName": "artifact", + "propertyName": "bom.validation.tags.inclusive", + "propertyValue": "[\\"foo\\"]", + "propertyType": "STRING", + "description": "${json-unit.any-string}" + } + """); + + response = jersey.target(V1_CONFIG_PROPERTY).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(/* language=JSON */ """ + { + "groupName": "artifact", + "propertyName": "bom.validation.tags.inclusive", + "propertyValue": "foo" + } + """, MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(400); + assertThat(getPlainTextBody(response)).isEqualTo("Value must be a valid JSON array of strings"); + } + } diff --git a/src/test/java/org/dependencytrack/resources/v1/VexResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/VexResourceTest.java index fbe8eead8a..c34c62b82d 100644 --- a/src/test/java/org/dependencytrack/resources/v1/VexResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/VexResourceTest.java @@ -26,6 +26,7 @@ import org.dependencytrack.auth.Permissions; import org.dependencytrack.model.AnalysisResponse; import org.dependencytrack.model.AnalysisState; +import org.dependencytrack.model.BomValidationMode; import org.dependencytrack.model.Classifier; import org.dependencytrack.model.Component; import org.dependencytrack.model.Project; @@ -43,11 +44,15 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.util.Base64; +import java.util.Collections; +import java.util.List; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_ENABLED; +import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_MODE; +import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_TAGS_EXCLUSIVE; +import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_TAGS_INCLUSIVE; import static org.hamcrest.CoreMatchers.equalTo; public class VexResourceTest extends ResourceTest { @@ -438,11 +443,11 @@ public void uploadVexInvalidJsonTest() { initializeWithPermissions(Permissions.BOM_UPLOAD); qm.createConfigProperty( - BOM_VALIDATION_ENABLED.getGroupName(), - BOM_VALIDATION_ENABLED.getPropertyName(), - "true", - BOM_VALIDATION_ENABLED.getPropertyType(), - null + BOM_VALIDATION_MODE.getGroupName(), + BOM_VALIDATION_MODE.getPropertyName(), + BomValidationMode.ENABLED.name(), + BOM_VALIDATION_MODE.getPropertyType(), + BOM_VALIDATION_MODE.getDescription() ); final var project = new Project(); @@ -494,11 +499,11 @@ public void uploadVexInvalidXmlTest() { initializeWithPermissions(Permissions.BOM_UPLOAD); qm.createConfigProperty( - BOM_VALIDATION_ENABLED.getGroupName(), - BOM_VALIDATION_ENABLED.getPropertyName(), - "true", - BOM_VALIDATION_ENABLED.getPropertyType(), - null + BOM_VALIDATION_MODE.getGroupName(), + BOM_VALIDATION_MODE.getPropertyName(), + BomValidationMode.ENABLED.name(), + BOM_VALIDATION_MODE.getPropertyType(), + BOM_VALIDATION_MODE.getDescription() ); final var project = new Project(); @@ -542,6 +547,185 @@ public void uploadVexInvalidXmlTest() { """); } + @Test + public void uploadVexWithValidationModeDisabledTest() { + initializeWithPermissions(Permissions.BOM_UPLOAD); + + qm.createConfigProperty( + BOM_VALIDATION_MODE.getGroupName(), + BOM_VALIDATION_MODE.getPropertyName(), + BomValidationMode.DISABLED.name(), + BOM_VALIDATION_MODE.getPropertyType(), + BOM_VALIDATION_MODE.getDescription() + ); + + final var project = new Project(); + project.setName("acme-app"); + project.setVersion("1.0.0"); + qm.persist(project); + + final String encodedBom = Base64.getEncoder().encodeToString(""" + { + "bomFormat": "CycloneDX", + "specVersion": "1.2", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "foo", + "name": "acme-library", + "version": "1.0.0" + } + ] + } + """.getBytes()); + + final Response response = jersey.target(V1_VEX).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(""" + { + "projectName": "acme-app", + "projectVersion": "1.0.0", + "vex": "%s" + } + """.formatted(encodedBom), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(200); + } + + @Test + public void uploadVexWithValidationModeEnabledForTagsTest() { + initializeWithPermissions(Permissions.BOM_UPLOAD); + + qm.createConfigProperty( + BOM_VALIDATION_MODE.getGroupName(), + BOM_VALIDATION_MODE.getPropertyName(), + BomValidationMode.ENABLED_FOR_TAGS.name(), + BOM_VALIDATION_MODE.getPropertyType(), + BOM_VALIDATION_MODE.getDescription() + ); + qm.createConfigProperty( + BOM_VALIDATION_TAGS_INCLUSIVE.getGroupName(), + BOM_VALIDATION_TAGS_INCLUSIVE.getPropertyName(), + "[\"foo\"]", + BOM_VALIDATION_TAGS_INCLUSIVE.getPropertyType(), + BOM_VALIDATION_TAGS_INCLUSIVE.getDescription() + ); + + final var project = new Project(); + project.setName("acme-app"); + project.setVersion("1.0.0"); + qm.persist(project); + + qm.bind(project, List.of(qm.createTag("foo"))); + + final String encodedBom = Base64.getEncoder().encodeToString(""" + { + "bomFormat": "CycloneDX", + "specVersion": "1.2", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "foo", + "name": "acme-library", + "version": "1.0.0" + } + ] + } + """.getBytes()); + + Response response = jersey.target(V1_VEX).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(""" + { + "projectName": "acme-app", + "projectVersion": "1.0.0", + "vex": "%s" + } + """.formatted(encodedBom), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(400); + + qm.bind(project, Collections.emptyList()); + + response = jersey.target(V1_VEX).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(""" + { + "projectName": "acme-app", + "projectVersion": "1.0.0", + "vex": "%s" + } + """.formatted(encodedBom), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(200); + } + + @Test + public void uploadVexWithValidationModeDisabledForTagsTest() { + initializeWithPermissions(Permissions.BOM_UPLOAD); + + qm.createConfigProperty( + BOM_VALIDATION_MODE.getGroupName(), + BOM_VALIDATION_MODE.getPropertyName(), + BomValidationMode.DISABLED_FOR_TAGS.name(), + BOM_VALIDATION_MODE.getPropertyType(), + BOM_VALIDATION_MODE.getDescription() + ); + qm.createConfigProperty( + BOM_VALIDATION_TAGS_EXCLUSIVE.getGroupName(), + BOM_VALIDATION_TAGS_EXCLUSIVE.getPropertyName(), + "[\"foo\"]", + BOM_VALIDATION_TAGS_EXCLUSIVE.getPropertyType(), + BOM_VALIDATION_TAGS_EXCLUSIVE.getDescription() + ); + + final var project = new Project(); + project.setName("acme-app"); + project.setVersion("1.0.0"); + qm.persist(project); + + qm.bind(project, List.of(qm.createTag("foo"))); + + final String encodedBom = Base64.getEncoder().encodeToString(""" + { + "bomFormat": "CycloneDX", + "specVersion": "1.2", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "foo", + "name": "acme-library", + "version": "1.0.0" + } + ] + } + """.getBytes()); + + Response response = jersey.target(V1_VEX).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(""" + { + "projectName": "acme-app", + "projectVersion": "1.0.0", + "vex": "%s" + } + """.formatted(encodedBom), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(200); + + qm.bind(project, Collections.emptyList()); + + response = jersey.target(V1_VEX).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(""" + { + "projectName": "acme-app", + "projectVersion": "1.0.0", + "vex": "%s" + } + """.formatted(encodedBom), MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(400); + } + @Test public void uploadVexTooLargeViaPutTest() { final var project = new Project(); diff --git a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java index 9bb3896501..03d128be9f 100644 --- a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java @@ -25,8 +25,6 @@ import alpine.notification.NotificationLevel; import alpine.notification.NotificationService; import alpine.notification.Subscription; -import java.util.Arrays; - import org.awaitility.core.ConditionTimeoutException; import org.dependencytrack.PersistenceCapableTest; import org.dependencytrack.event.BomUploadEvent; @@ -58,6 +56,7 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -70,7 +69,6 @@ import static org.assertj.core.api.Assertions.fail; import static org.awaitility.Awaitility.await; import static org.dependencytrack.assertion.Assertions.assertConditionWithTimeout; -import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_ENABLED; public class BomUploadProcessingTaskTest extends PersistenceCapableTest { @@ -293,14 +291,6 @@ public void informWithEmptyBomTest() throws Exception { @Test public void informWithInvalidCycloneDxBomTest() throws Exception { - qm.createConfigProperty( - BOM_VALIDATION_ENABLED.getGroupName(), - BOM_VALIDATION_ENABLED.getPropertyName(), - "true", - BOM_VALIDATION_ENABLED.getPropertyType(), - null - ); - final Project project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); final byte[] bomBytes = """ From ea1bf331afca25a7f2927887f12caea497dc2ded Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 1 Sep 2024 18:51:00 +0200 Subject: [PATCH 102/429] Exclude `upgrade` package from test coverage Signed-off-by: nscuro --- pom.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pom.xml b/pom.xml index 5ba49c6618..cf0b79cf18 100644 --- a/pom.xml +++ b/pom.xml @@ -580,6 +580,15 @@
    + + org.jacoco + jacoco-maven-plugin + + + org/dependencytrack/upgrade/**/* + + + org.codehaus.mojo exec-maven-plugin From 98442424f57d057a334ffbaa4724128c028fa93f Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 1 Sep 2024 20:16:19 +0200 Subject: [PATCH 103/429] Bump generated BOM to CycloneDX v1.5; Add external references Closes #3602 Signed-off-by: nscuro --- pom.xml | 57 +++++++++++++++++++++++++++- src/main/resources/services.bom.json | 2 +- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index cf0b79cf18..420dd2c85b 100644 --- a/pom.xml +++ b/pom.xml @@ -135,8 +135,6 @@ 8.0.33 42.7.3 - application - json false 12.0.12 @@ -589,6 +587,61 @@ + + org.cyclonedx + cyclonedx-maven-plugin + + + cyclonedx-aggregate + prepare-package + + makeAggregateBom + + + + + application + 1.5 + true + true + true + true + false + false + true + json + + + advisories + https://github.com/DependencyTrack/dependency-track/security/advisories + + + chat + https://dependencytrack.org/slack + + + documentation + https://docs.dependencytrack.org/ + + + release-notes + https://docs.dependencytrack.org/changelog/ + + + security-contact + mailto:security@dependencytrack.org + + + social + https://www.linkedin.com/company/owasp-dependency-track + + + social + https://x.com/dependencytrack + + + + org.codehaus.mojo exec-maven-plugin diff --git a/src/main/resources/services.bom.json b/src/main/resources/services.bom.json index 7125613e1d..58661d2ff0 100644 --- a/src/main/resources/services.bom.json +++ b/src/main/resources/services.bom.json @@ -1,6 +1,6 @@ { "bomFormat": "CycloneDX", - "specVersion": "1.4", + "specVersion": "1.5", "serialNumber": "urn:uuid:d8e07202-6817-42c1-8cae-20254567672f", "version": 1, "services": [ From dd2af16ef32bc5e135968678b17865a534c18504 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 1 Sep 2024 20:18:31 +0200 Subject: [PATCH 104/429] Bump CycloneDX CLI to v0.26.0 Signed-off-by: nscuro --- .github/workflows/_meta-build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 27789a742c..0b80928f94 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -41,8 +41,8 @@ jobs: run: | mkdir -p "$HOME/.local/bin" echo "$HOME/.local/bin" >> $GITHUB_PATH - wget -O "$HOME/.local/bin/cyclonedx" https://github.com/CycloneDX/cyclonedx-cli/releases/download/v0.24.2/cyclonedx-linux-x64 - echo "ef0d3b31d176e02bc594f83e19cfcea053c6bc5b197351f71696e189390f851d $HOME/.local/bin/cyclonedx" | sha256sum -c + wget -O "$HOME/.local/bin/cyclonedx" https://github.com/CycloneDX/cyclonedx-cli/releases/download/v0.26.0/cyclonedx-linux-x64 + echo "207c82fbbaed96642a033a4da1c20eb4c6d4b53acccf37619c8d4183803ccbf4 $HOME/.local/bin/cyclonedx" | sha256sum -c chmod +x "$HOME/.local/bin/cyclonedx" - name: Build with Maven From 5b93ffe98a8a95fd5f69e695967802e8dc129407 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 1 Sep 2024 21:13:33 +0200 Subject: [PATCH 105/429] Update changelog for v4.12.0 with recent changes Signed-off-by: nscuro --- docs/_posts/2024-xx-xx-v4.12.0.md | 52 ++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/docs/_posts/2024-xx-xx-v4.12.0.md b/docs/_posts/2024-xx-xx-v4.12.0.md index f3a32d45ff..398f03b8bd 100644 --- a/docs/_posts/2024-xx-xx-v4.12.0.md +++ b/docs/_posts/2024-xx-xx-v4.12.0.md @@ -25,8 +25,21 @@ type: major * Add REST endpoint for tag deletion - [apiserver/#3896] * Add OIDC Documentation for OneLogin - [apiserver/#3921] * Add REST endpoints to tag and untag policies in bulk - [apiserver/#3924] +* Support the `component.authors` field of CycloneDX v1.6 - [apiserver/#3969] * Make project cloning an atomic operation - [apiserver/#3982] +* Add option to test notifications - [apiserver/#3983] * Log warning when dependency graph is missing the root node - [apiserver/#3990] +* Add ability to limit notifications to projects with specific tags - [apiserver/#4031] +* Disable redundant shutdown hook of the embedded H2 database - [apiserver/#4106] +* Support inclusion and exclusion of projects from BOM validation with tags - [apiserver/#4109] +* Update Dependency-Track's own BOM to CycloneDX v1.5 - [apiserver/#4110] +* Support for serving the frontend from a custom path - [frontend/#801] +* Add dynamic policy violation badges - [frontend/#810] +* Add quick search for projects also using a component - [frontend/#848] +* Add database name and version to *About* dialog - [frontend/#870] +* Make *Severity* and *CWE* columns of findings table sortable - [frontend/#907] +* Raise baseline Node version to 20 - [frontend/#927] +* Add autocomplete support for tag inputs - [frontend/#936] **Fixes:** @@ -36,6 +49,9 @@ type: major * Fix BOM validation failing for XML with multiple namespaces - [apiserver/#4020] * Handle breaking change in Trivy 0.54.0 server API - [apiserver/#4023] * Fix project link for new vulnerable dependency for email - [apiserver/#4026] +* Fix occasional `column list index is out of range` exceptions - [apiserver/#4104] +* Fix missing URL encoding for repository metadata analyzers - [apiserver/#4107] +* Fix project being rendered as PURL in email notifications - [apiserver/#4108] **Upgrade Notes:** @@ -48,6 +64,9 @@ the OpenAPI v3 specification, the Swagger v2 format is no longer provided. * The `/api/v1/tag/{policyUuid}` REST API endpoint has been deprecated in favor of `/api/v1/tag/policy/{uuid}`. Users relying on the outdated endpoint for their custom integrations are encouraged to migrate to the new endpoint. +* The legacy BOM processing logic was removed. The *BOM Processing V2* option introduced in v4.11 is now the default +and the only available option. To gauge the impact of this change, consider enabling the experimental option in +an existing v4.11 deployment first. For a complete list of changes, refer to the respective GitHub milestones: @@ -57,6 +76,8 @@ For a complete list of changes, refer to the respective GitHub milestones: We thank all organizations and individuals who contributed to this release, from logging issues to taking part in discussions on GitHub & Slack to testing of fixes. Special thanks to everyone who contributed code to implement enhancements and fix defects: +[@2000rosser], [@JCHacking], [@SaberStrat], [@Squixx], [@aravindparappil46], [@fupgang], [@gbonnefille], [@mehab], +[@rcsilva83], [@rh0dy], [@setchy] ###### dependency-track-apiserver.jar @@ -107,8 +128,37 @@ Special thanks to everyone who contributed code to implement enhancements and fi [apiserver/#3924]: https://github.com/DependencyTrack/dependency-track/pull/3924 [apiserver/#3958]: https://github.com/DependencyTrack/dependency-track/pull/3958 [apiserver/#3965]: https://github.com/DependencyTrack/dependency-track/pull/3965 +[apiserver/#3969]: https://github.com/DependencyTrack/dependency-track/pull/3969 [apiserver/#3982]: https://github.com/DependencyTrack/dependency-track/pull/3982 +[apiserver/#3983]: https://github.com/DependencyTrack/dependency-track/pull/3983 [apiserver/#3990]: https://github.com/DependencyTrack/dependency-track/pull/3990 [apiserver/#4020]: https://github.com/DependencyTrack/dependency-track/pull/4020 [apiserver/#4023]: https://github.com/DependencyTrack/dependency-track/pull/4023 -[apiserver/#4026]: https://github.com/DependencyTrack/dependency-track/pull/4026 \ No newline at end of file +[apiserver/#4026]: https://github.com/DependencyTrack/dependency-track/pull/4026 +[apiserver/#4031]: https://github.com/DependencyTrack/dependency-track/pull/4031 +[apiserver/#4104]: https://github.com/DependencyTrack/dependency-track/pull/4104 +[apiserver/#4106]: https://github.com/DependencyTrack/dependency-track/pull/4106 +[apiserver/#4107]: https://github.com/DependencyTrack/dependency-track/pull/4107 +[apiserver/#4108]: https://github.com/DependencyTrack/dependency-track/pull/4108 +[apiserver/#4109]: https://github.com/DependencyTrack/dependency-track/pull/4109 +[apiserver/#4110]: https://github.com/DependencyTrack/dependency-track/pull/4110 + +[frontend/#801]: https://github.com/DependencyTrack/frontend/pull/801 +[frontend/#810]: https://github.com/DependencyTrack/frontend/pull/810 +[frontend/#848]: https://github.com/DependencyTrack/frontend/pull/848 +[frontend/#870]: https://github.com/DependencyTrack/frontend/pull/870 +[frontend/#907]: https://github.com/DependencyTrack/frontend/pull/907 +[frontend/#927]: https://github.com/DependencyTrack/frontend/pull/927 +[frontend/#936]: https://github.com/DependencyTrack/frontend/pull/936 + +[@2000rosser]: https://github.com/2000rosser +[@JCHacking]: https://github.com/JCHacking +[@SaberStrat]: https://github.com/SaberStrat +[@Squixx]: https://github.com/Squixx +[@aravindparappil46]: https://github.com/aravindparappil46 +[@fupgang]: https://github.com/fupgang +[@gbonnefille]: https://github.com/gbonnefille +[@mehab]: https://github.com/mehab +[@rcsilva83]: https://github.com/rcsilva83 +[@rh0dy]: https://github.com/rh0dy +[@setchy]: https://github.com/setchy \ No newline at end of file From 922e3e286e885b6394f1f6b86ea99d6cf1d21e9a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 08:28:40 +0000 Subject: [PATCH 106/429] Bump io.github.jeremylong:open-vulnerability-clients from 6.1.7 to 6.2.0 Bumps [io.github.jeremylong:open-vulnerability-clients](https://github.com/jeremylong/vuln-tools) from 6.1.7 to 6.2.0. - [Release notes](https://github.com/jeremylong/vuln-tools/releases) - [Commits](https://github.com/jeremylong/vuln-tools/compare/v6.1.7...v6.2.0) --- updated-dependencies: - dependency-name: io.github.jeremylong:open-vulnerability-clients dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cf0b79cf18..5877d1d18a 100644 --- a/pom.xml +++ b/pom.xml @@ -115,7 +115,7 @@ 8.11.3 3.9.9 5.15.0 - 6.1.7 + 6.2.0 1.5.0 3.2.2 2.2.0 From 454ca033beb46a9388705cb5dcb82a025330a63a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 08:29:22 +0000 Subject: [PATCH 107/429] Bump github/codeql-action from 3.26.5 to 3.26.6 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.5 to 3.26.6. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/2c779ab0d087cd7fe7b826087247c2c81f27bfa6...4dd16135b69a43b6c8efb853346f8437d92d3c93) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 27789a742c..d64e3f0f9a 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -145,6 +145,6 @@ jobs: - name: Upload Trivy Scan Results to GitHub Security Tab if: ${{ inputs.publish-container }} - uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # tag=v3.26.5 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # tag=v3.26.6 with: sarif_file: 'trivy-results.sarif' From 9a57e2b8ce390b0bf76fec63545f403401f726b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 08:29:27 +0000 Subject: [PATCH 108/429] Bump actions/upload-artifact from 4.3.6 to 4.4.0 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.6 to 4.4.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/834a144ee995460fba8ed112a2fc961b36a5ec5a...50769540e7f4bd5e21e526ee35c689e35e0d6874) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- .github/workflows/ci-test.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 27789a742c..6681596ddb 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -55,7 +55,7 @@ jobs: mvn -B --no-transfer-progress cyclonedx:makeBom -Dservices.bom.merge.skip=false org.codehaus.mojo:exec-maven-plugin:exec@merge-services-bom - name: Upload Artifacts - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # tag=v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # tag=v4.4.0 with: name: assembled-wars path: |- diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml index 978d9cf7f1..3e7325f0cb 100644 --- a/.github/workflows/ci-test.yaml +++ b/.github/workflows/ci-test.yaml @@ -66,7 +66,7 @@ jobs: - name: Upload PR test coverage report if: ${{ github.event_name == 'pull_request' }} - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # tag=v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # tag=v4.4.0 with: name: pr-test-coverage-report path: |- From af64eeff287457742b907239d07f16562f9ee6bc Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 1 Sep 2024 19:42:31 +0200 Subject: [PATCH 109/429] Migrate Trivy integration to use Protobuf instead of JSON Closes #4065 Signed-off-by: nscuro --- docs/_posts/2024-xx-xx-v4.12.0.md | 2 + pom.xml | 41 +++ .../parser/trivy/TrivyParser.java | 69 ++-- .../parser/trivy/model/Application.java | 69 ---- .../parser/trivy/model/BlobInfo.java | 47 --- .../parser/trivy/model/CVSS.java | 44 --- .../parser/trivy/model/DataSource.java | 34 -- .../parser/trivy/model/DeleteRequest.java | 31 -- .../parser/trivy/model/Layer.java | 38 --- .../parser/trivy/model/OS.java | 44 --- .../parser/trivy/model/Options.java | 52 --- .../parser/trivy/model/Package.java | 57 ---- .../parser/trivy/model/PackageInfo.java | 36 -- .../parser/trivy/model/PutRequest.java | 34 -- .../parser/trivy/model/Result.java | 61 ---- .../parser/trivy/model/ScanRequest.java | 44 --- .../parser/trivy/model/TrivyResponse.java | 30 -- .../parser/trivy/model/Vulnerability.java | 134 -------- .../tasks/scanners/TrivyAnalysisTask.java | 267 ++++++++------- src/main/proto/trivy/cache/v1/service.proto | 71 ++++ src/main/proto/trivy/common/service.proto | 271 +++++++++++++++ src/main/proto/trivy/scanner/v1/service.proto | 53 +++ .../tasks/scanners/TrivyAnalysisTaskTest.java | 320 ++++++------------ 23 files changed, 691 insertions(+), 1158 deletions(-) delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/Application.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/BlobInfo.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/CVSS.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/DataSource.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/DeleteRequest.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/Layer.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/OS.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/Options.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/Package.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/PackageInfo.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/PutRequest.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/Result.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/ScanRequest.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/TrivyResponse.java delete mode 100644 src/main/java/org/dependencytrack/parser/trivy/model/Vulnerability.java create mode 100644 src/main/proto/trivy/cache/v1/service.proto create mode 100644 src/main/proto/trivy/common/service.proto create mode 100644 src/main/proto/trivy/scanner/v1/service.proto diff --git a/docs/_posts/2024-xx-xx-v4.12.0.md b/docs/_posts/2024-xx-xx-v4.12.0.md index 398f03b8bd..38399118bb 100644 --- a/docs/_posts/2024-xx-xx-v4.12.0.md +++ b/docs/_posts/2024-xx-xx-v4.12.0.md @@ -33,6 +33,7 @@ type: major * Disable redundant shutdown hook of the embedded H2 database - [apiserver/#4106] * Support inclusion and exclusion of projects from BOM validation with tags - [apiserver/#4109] * Update Dependency-Track's own BOM to CycloneDX v1.5 - [apiserver/#4110] +* Migrate Trivy integration to use Protobuf instead of JSON - [apiserver/#4116] * Support for serving the frontend from a custom path - [frontend/#801] * Add dynamic policy violation badges - [frontend/#810] * Add quick search for projects also using a component - [frontend/#848] @@ -142,6 +143,7 @@ Special thanks to everyone who contributed code to implement enhancements and fi [apiserver/#4108]: https://github.com/DependencyTrack/dependency-track/pull/4108 [apiserver/#4109]: https://github.com/DependencyTrack/dependency-track/pull/4109 [apiserver/#4110]: https://github.com/DependencyTrack/dependency-track/pull/4110 +[apiserver/#4116]: https://github.com/DependencyTrack/dependency-track/pull/4116 [frontend/#801]: https://github.com/DependencyTrack/frontend/pull/801 [frontend/#810]: https://github.com/DependencyTrack/frontend/pull/810 diff --git a/pom.xml b/pom.xml index 5877d1d18a..73251c93aa 100644 --- a/pom.xml +++ b/pom.xml @@ -118,6 +118,7 @@ 6.2.0 1.5.0 3.2.2 + 4.28.0 2.2.0 2.1.22 1.19.0 @@ -139,8 +140,11 @@ json false 12.0.12 + 3.11.4 src/main/webapp/** + + com.google.protobuf:protoc:${lib.protobuf-java.version} cyclonedx true @@ -203,6 +207,7 @@ cyclonedx-core-java ${lib.cyclonedx-java.version} + false - 12.0.12 + 12.0.13 3.11.4 src/main/webapp/** From b030fecc95d9e5f582bdfb12d8ed7a19e54dbad0 Mon Sep 17 00:00:00 2001 From: nscuro Date: Mon, 9 Sep 2024 20:34:19 +0200 Subject: [PATCH 113/429] Bump Alpine to 3.1.0 Signed-off-by: nscuro --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 84d9fdffcb..9e9c60a93c 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ us.springett alpine-parent - 3.0.1 + 3.1.0 4.0.0 From 478d6584d1ddd4b464fac62738f406df857feadc Mon Sep 17 00:00:00 2001 From: nscuro Date: Mon, 9 Sep 2024 20:34:54 +0200 Subject: [PATCH 114/429] Disable logback status listener in all default configurations Signed-off-by: nscuro --- src/main/docker/logback-json.xml | 2 ++ src/main/docker/logback.xml | 1 + 2 files changed, 3 insertions(+) diff --git a/src/main/docker/logback-json.xml b/src/main/docker/logback-json.xml index 510d884006..03c59e0f24 100644 --- a/src/main/docker/logback-json.xml +++ b/src/main/docker/logback-json.xml @@ -1,5 +1,7 @@ + + diff --git a/src/main/docker/logback.xml b/src/main/docker/logback.xml index 5b2910545e..089b7ce76a 100644 --- a/src/main/docker/logback.xml +++ b/src/main/docker/logback.xml @@ -1,5 +1,6 @@ + ${user.home}/.dependency-track/dependency-track.log From 3b92b440001e2aef4866d3252777083acbb76db1 Mon Sep 17 00:00:00 2001 From: nscuro Date: Mon, 9 Sep 2024 20:39:26 +0200 Subject: [PATCH 115/429] Disable Jetty classpath scanning for faster startup Signed-off-by: nscuro --- pom.xml | 8 ++++++-- src/main/webapp/WEB-INF/jetty-context.xml | 13 +++++++++++++ src/main/webapp/WEB-INF/web.xml | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 src/main/webapp/WEB-INF/jetty-context.xml diff --git a/pom.xml b/pom.xml index 9e9c60a93c..72df84de9a 100644 --- a/pom.xml +++ b/pom.xml @@ -734,7 +734,9 @@ ${plugin.jetty.version} - / + + ^$ + ^$ @@ -794,7 +796,9 @@ ${plugin.jetty.version} - / + + ^$ + ^$ src/test/webapp/WEB-INF/h2-console-web.xml diff --git a/src/main/webapp/WEB-INF/jetty-context.xml b/src/main/webapp/WEB-INF/jetty-context.xml new file mode 100644 index 0000000000..89d99c9451 --- /dev/null +++ b/src/main/webapp/WEB-INF/jetty-context.xml @@ -0,0 +1,13 @@ + + + + + + org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern + ^$ + + + org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern + ^$ + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 577ff7ba3b..339838a5f3 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -21,6 +21,7 @@ From f1f5d7610d6681e42e194a86375a5c9b664a68ba Mon Sep 17 00:00:00 2001 From: nscuro Date: Mon, 9 Sep 2024 21:19:23 +0200 Subject: [PATCH 116/429] Extend max length of `TEAM.NAME` column to 255 characters Signed-off-by: nscuro --- .../upgrade/v4120/v4120Updater.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java b/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java index 99f97b16f8..0b6615c481 100644 --- a/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java +++ b/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java @@ -21,12 +21,14 @@ import alpine.common.logging.Logger; import alpine.persistence.AlpineQueryManager; import alpine.server.upgrade.AbstractUpgradeItem; +import alpine.server.util.DbUtil; import org.dependencytrack.model.BomValidationMode; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; import static org.dependencytrack.model.ConfigPropertyConstants.BOM_VALIDATION_MODE; @@ -43,6 +45,7 @@ public String getSchemaVersion() { public void executeUpgrade(final AlpineQueryManager qm, final Connection connection) throws Exception { removeExperimentalBomUploadProcessingV2ConfigProperty(connection); migrateBomValidationConfigProperty(connection); + extendTeamNameColumnMaxLength(connection); } private static void removeExperimentalBomUploadProcessingV2ConfigProperty(final Connection connection) throws SQLException { @@ -143,4 +146,24 @@ private static void migrateBomValidationConfigProperty(final Connection connecti } } + private void extendTeamNameColumnMaxLength(final Connection connection) throws SQLException { + LOGGER.info("Extending max length of column TEAM.NAME to 255"); + + try (final Statement stmt = connection.createStatement()) { + if (DbUtil.isMssql()) { + stmt.executeUpdate(""" + ALTER TABLE "TEAM" ALTER COLUMN "NAME" VARChAR(255) NOT NULL + """); + } else if (DbUtil.isMysql()) { + stmt.executeUpdate(""" + ALTER TABLE "TEAM" MODIFY "NAME" VARCHAR(255) NOT NULL + """); + } else { + stmt.executeUpdate(""" + ALTER TABLE "TEAM" ALTER COLUMN "NAME" TYPE VARCHAR(255) + """); + } + } + } + } From 4a1ebc3ce0c5e00c19a7c893947e56346b21abec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schauer-K=C3=B6ckeis=20Thomas?= Date: Fri, 6 Sep 2024 11:29:46 +0200 Subject: [PATCH 117/429] feat: customizable-login-page Added new configProperty return and new configProperty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../model/ConfigPropertyConstants.java | 4 +- .../resources/v1/ConfigPropertyResource.java | 21 ++++++++ .../v1/ConfigPropertyResourceTest.java | 51 +++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java index ced4a71cc2..48986a88ef 100644 --- a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java +++ b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java @@ -111,7 +111,9 @@ public enum ConfigPropertyConstants { SEARCH_INDEXES_CONSISTENCY_CHECK_DELTA_THRESHOLD("search-indexes", "consistency.check.delta.threshold", "20", PropertyType.INTEGER, "Threshold used to trigger an index rebuild when comparing database table and corresponding lucene index (in percentage). It must be an integer between 1 and 100"), BOM_VALIDATION_MODE("artifact", "bom.validation.mode", BomValidationMode.ENABLED.name(), PropertyType.STRING, "Flag to control the BOM validation mode"), BOM_VALIDATION_TAGS_INCLUSIVE("artifact", "bom.validation.tags.inclusive", "[]", PropertyType.STRING, "JSON array of tags for which BOM validation shall be performed"), - BOM_VALIDATION_TAGS_EXCLUSIVE("artifact", "bom.validation.tags.exclusive", "[]", PropertyType.STRING, "JSON array of tags for which BOM validation shall NOT be performed"); + BOM_VALIDATION_TAGS_EXCLUSIVE("artifact", "bom.validation.tags.exclusive", "[]", PropertyType.STRING, "JSON array of tags for which BOM validation shall NOT be performed"), + WELCOME_MESSAGE("Message", "welcomeMessage", "%20%3Chtml%3E%3Ch1%3EYour%20Welcome%20Message%3C%2Fh1%3E%3C%2Fhtml%3E", PropertyType.STRING, "Custom HTML Code that is displayed before login"), + IS_WELCOME_MESSAGE("Message", "isWelcomeMessage", "false", PropertyType.BOOLEAN, "Bool that says wheter to show the welcome message or not"); private final String groupName; private final String propertyName; diff --git a/src/main/java/org/dependencytrack/resources/v1/ConfigPropertyResource.java b/src/main/java/org/dependencytrack/resources/v1/ConfigPropertyResource.java index 0e5d6ad825..739cf15bb6 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ConfigPropertyResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ConfigPropertyResource.java @@ -19,6 +19,7 @@ package org.dependencytrack.resources.v1; import alpine.model.ConfigProperty; +import alpine.server.auth.AuthenticationNotRequired; import alpine.server.auth.PermissionRequired; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; @@ -156,5 +157,25 @@ public Response updateConfigProperty(List list) { return Response.ok(returnList).build(); } + @GET + @Path("/welcomeMessage") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Returns if a welcome Message is given, and wich should be showed", description = "

    Requires permission SYSTEM_CONFIGURATION

    ") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "A list of isMessageWelcome and messageWelcome", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ConfigProperty.class)))), + @ApiResponse(responseCode = "401", description = "Unauthorized") + }) + @AuthenticationNotRequired + public Response getGreetingMessage() { + try (QueryManager qm = new QueryManager(getAlpineRequest())) { + ConfigProperty greetingMessage = qm.getConfigProperty("Message", "welcomeMessage"); + ConfigProperty isGreetingMessage = qm.getConfigProperty("Message", "isWelcomeMessage"); + qm.close(); + List combined = new ArrayList(); + combined.add(greetingMessage); + combined.add(isGreetingMessage); + return Response.ok(combined).build(); + } + } } diff --git a/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java index b316c03b79..8920921fc4 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java @@ -39,6 +39,7 @@ import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; public class ConfigPropertyResourceTest extends ResourceTest { @@ -383,4 +384,54 @@ public void updateConfigPropertyBomValidationTagsInclusiveTest() { assertThat(getPlainTextBody(response)).isEqualTo("Value must be a valid JSON array of strings"); } + @Test + public void getGreetingMessageTest() { + qm.createConfigProperty( + ConfigPropertyConstants.WELCOME_MESSAGE.getGroupName(), + ConfigPropertyConstants.WELCOME_MESSAGE.getPropertyName(), + ConfigPropertyConstants.WELCOME_MESSAGE.getDefaultPropertyValue(), + ConfigPropertyConstants.WELCOME_MESSAGE.getPropertyType(), + ConfigPropertyConstants.WELCOME_MESSAGE.getDescription()); + + qm.createConfigProperty( + ConfigPropertyConstants.IS_WELCOME_MESSAGE.getGroupName(), + ConfigPropertyConstants.IS_WELCOME_MESSAGE.getPropertyName(), + ConfigPropertyConstants.IS_WELCOME_MESSAGE.getDefaultPropertyValue(), + ConfigPropertyConstants.IS_WELCOME_MESSAGE.getPropertyType(), + ConfigPropertyConstants.IS_WELCOME_MESSAGE.getDescription()); + + Response response = jersey.target(V1_CONFIG_PROPERTY + "/greetingMessage").request() + .header(X_API_KEY, apiKey).get(); + JsonArray json = parseJsonArray(response); + assertEquals(json.size(), 2); + assertEquals(ConfigPropertyConstants.WELCOME_MESSAGE.getDefaultPropertyValue(), + json.getFirst().asJsonObject().getString("propertyValue")); + assertEquals("false", json.get(1).asJsonObject().getString("propertyValue")); + } + + @Test + public void getGreetingMessageEnabledTest() { + qm.createConfigProperty( + ConfigPropertyConstants.WELCOME_MESSAGE.getGroupName(), + ConfigPropertyConstants.WELCOME_MESSAGE.getPropertyName(), + ConfigPropertyConstants.WELCOME_MESSAGE.getDefaultPropertyValue(), + ConfigPropertyConstants.WELCOME_MESSAGE.getPropertyType(), + ConfigPropertyConstants.WELCOME_MESSAGE.getDescription()); + + qm.createConfigProperty( + ConfigPropertyConstants.IS_WELCOME_MESSAGE.getGroupName(), + ConfigPropertyConstants.IS_WELCOME_MESSAGE.getPropertyName(), + "true", + ConfigPropertyConstants.IS_WELCOME_MESSAGE.getPropertyType(), + ConfigPropertyConstants.IS_WELCOME_MESSAGE.getDescription()); + + Response response = jersey.target(V1_CONFIG_PROPERTY + "/greetingMessage").request() + .header(X_API_KEY, apiKey).get(); + JsonArray json = parseJsonArray(response); + assertEquals(json.size(), 2); + assertEquals(ConfigPropertyConstants.WELCOME_MESSAGE.getDefaultPropertyValue(), + json.getJsonObject(0).getString("propertyValue")); + assertEquals("true", json.getJsonObject(1).getString("propertyValue")); + } + } From 51f08b84d393e824b9f361665e513bcaa603e68c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Tue, 10 Sep 2024 10:00:19 +0200 Subject: [PATCH 118/429] Fixed Tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../resources/v1/ConfigPropertyResourceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java index 8920921fc4..6c676b9d1f 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java @@ -400,7 +400,7 @@ public void getGreetingMessageTest() { ConfigPropertyConstants.IS_WELCOME_MESSAGE.getPropertyType(), ConfigPropertyConstants.IS_WELCOME_MESSAGE.getDescription()); - Response response = jersey.target(V1_CONFIG_PROPERTY + "/greetingMessage").request() + Response response = jersey.target(V1_CONFIG_PROPERTY + "/welcomeMessage").request() .header(X_API_KEY, apiKey).get(); JsonArray json = parseJsonArray(response); assertEquals(json.size(), 2); @@ -425,7 +425,7 @@ public void getGreetingMessageEnabledTest() { ConfigPropertyConstants.IS_WELCOME_MESSAGE.getPropertyType(), ConfigPropertyConstants.IS_WELCOME_MESSAGE.getDescription()); - Response response = jersey.target(V1_CONFIG_PROPERTY + "/greetingMessage").request() + Response response = jersey.target(V1_CONFIG_PROPERTY + "/welcomeMessage").request() .header(X_API_KEY, apiKey).get(); JsonArray json = parseJsonArray(response); assertEquals(json.size(), 2); From 07641e7933b4878574a87bd87f26b53e2e7005be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Tue, 10 Sep 2024 14:24:16 +0200 Subject: [PATCH 119/429] Rewritten function into a generic function, that only return if the prop is marked public MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../model/ConfigPropertyConstants.java | 31 +++++++++++-- .../resources/v1/ConfigPropertyResource.java | 28 ++++++------ .../v1/ConfigPropertyResourceTest.java | 43 ++----------------- 3 files changed, 47 insertions(+), 55 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java index 48986a88ef..a7864276b3 100644 --- a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java +++ b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java @@ -18,9 +18,12 @@ */ package org.dependencytrack.model; +import alpine.model.IConfigProperty; import alpine.model.IConfigProperty.PropertyType; import org.apache.commons.lang3.SystemUtils; +import java.util.Arrays; + public enum ConfigPropertyConstants { GENERAL_BASE_URL("general", "base.url", null, PropertyType.URL, "URL used to construct links back to Dependency-Track from external systems"), @@ -112,21 +115,40 @@ public enum ConfigPropertyConstants { BOM_VALIDATION_MODE("artifact", "bom.validation.mode", BomValidationMode.ENABLED.name(), PropertyType.STRING, "Flag to control the BOM validation mode"), BOM_VALIDATION_TAGS_INCLUSIVE("artifact", "bom.validation.tags.inclusive", "[]", PropertyType.STRING, "JSON array of tags for which BOM validation shall be performed"), BOM_VALIDATION_TAGS_EXCLUSIVE("artifact", "bom.validation.tags.exclusive", "[]", PropertyType.STRING, "JSON array of tags for which BOM validation shall NOT be performed"), - WELCOME_MESSAGE("Message", "welcomeMessage", "%20%3Chtml%3E%3Ch1%3EYour%20Welcome%20Message%3C%2Fh1%3E%3C%2Fhtml%3E", PropertyType.STRING, "Custom HTML Code that is displayed before login"), - IS_WELCOME_MESSAGE("Message", "isWelcomeMessage", "false", PropertyType.BOOLEAN, "Bool that says wheter to show the welcome message or not"); + WELCOME_MESSAGE("general", "welcome.message.html", "%20%3Chtml%3E%3Ch1%3EYour%20Welcome%20Message%3C%2Fh1%3E%3C%2Fhtml%3E", PropertyType.STRING, "Custom HTML Code that is displayed before login", true), + IS_WELCOME_MESSAGE("general", "welcome.message.enabled", "false", PropertyType.BOOLEAN, "Bool that says wheter to show the welcome message or not", true); private final String groupName; private final String propertyName; private final String defaultPropertyValue; private final PropertyType propertyType; private final String description; + private final Boolean isPublic; + - ConfigPropertyConstants(String groupName, String propertyName, String defaultPropertyValue, PropertyType propertyType, String description) { + ConfigPropertyConstants(String groupName, String propertyName, String defaultPropertyValue, PropertyType propertyType, String description) { + this.groupName = groupName; + this.propertyName = propertyName; + this.defaultPropertyValue = defaultPropertyValue; + this.propertyType = propertyType; + this.description = description; + this.isPublic = false; + } + + ConfigPropertyConstants(String groupName, String propertyName, String defaultPropertyValue, PropertyType propertyType, String description, Boolean isPublic) { this.groupName = groupName; this.propertyName = propertyName; this.defaultPropertyValue = defaultPropertyValue; this.propertyType = propertyType; this.description = description; + this.isPublic = isPublic; + } + + public static ConfigPropertyConstants ofProperty(final IConfigProperty property) { + return Arrays.stream(values()) + .filter(value -> value.groupName.equals(property.getGroupName()) && value.propertyName.equals(property.getPropertyName())) + .findFirst() + .orElse(null); } public String getGroupName() { @@ -149,4 +171,7 @@ public String getDescription() { return description; } + public Boolean getIsPublic() { + return isPublic; + } } diff --git a/src/main/java/org/dependencytrack/resources/v1/ConfigPropertyResource.java b/src/main/java/org/dependencytrack/resources/v1/ConfigPropertyResource.java index 739cf15bb6..19f2738da1 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ConfigPropertyResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ConfigPropertyResource.java @@ -22,6 +22,7 @@ import alpine.server.auth.AuthenticationNotRequired; import alpine.server.auth.PermissionRequired; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -31,6 +32,7 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirements; import io.swagger.v3.oas.annotations.tags.Tag; import org.dependencytrack.auth.Permissions; +import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.persistence.QueryManager; import jakarta.validation.Validator; @@ -38,6 +40,7 @@ import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -158,24 +161,25 @@ public Response updateConfigProperty(List list) { } @GET - @Path("/welcomeMessage") + @Path("/public/{groupName}/{propertyName}") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns if a welcome Message is given, and wich should be showed", description = "

    Requires permission SYSTEM_CONFIGURATION

    ") + @Operation(summary = "Returns a public ConfigProperty", description = "

    ") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "A list of isMessageWelcome and messageWelcome", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ConfigProperty.class)))), - @ApiResponse(responseCode = "401", description = "Unauthorized") + @ApiResponse(responseCode = "200", description = "Public ConfigProperty returned", content = @Content(schema = @Schema(implementation = ConfigProperty.class))), + @ApiResponse(responseCode = "403", description = "This is not a public visible ConfigProperty") }) @AuthenticationNotRequired - public Response getGreetingMessage() { + public Response getGreetingMessage( + @Parameter(description = "The group name of the value to retrieve", required = true) @PathParam("groupName") String groupName, + @Parameter(description = "The property name of the value to retrieve", required = true) @PathParam("propertyName") String propertyName) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { - ConfigProperty greetingMessage = qm.getConfigProperty("Message", "welcomeMessage"); - ConfigProperty isGreetingMessage = qm.getConfigProperty("Message", "isWelcomeMessage"); + ConfigProperty property = qm.getConfigProperty(groupName, propertyName); qm.close(); - List combined = new ArrayList(); - combined.add(greetingMessage); - combined.add(isGreetingMessage); - return Response.ok(combined).build(); + ConfigPropertyConstants publicConfigProperty = ConfigPropertyConstants.ofProperty(property); + if (!publicConfigProperty.getIsPublic()) { + return Response.status(Response.Status.FORBIDDEN).entity("This is not a public visible ConfigProperty").build(); + } + return Response.ok(property).build(); } } - } diff --git a/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java index 6c676b9d1f..367241c536 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java @@ -385,7 +385,7 @@ public void updateConfigPropertyBomValidationTagsInclusiveTest() { } @Test - public void getGreetingMessageTest() { + public void getPublicForbiddenTest() { qm.createConfigProperty( ConfigPropertyConstants.WELCOME_MESSAGE.getGroupName(), ConfigPropertyConstants.WELCOME_MESSAGE.getPropertyName(), @@ -393,45 +393,8 @@ public void getGreetingMessageTest() { ConfigPropertyConstants.WELCOME_MESSAGE.getPropertyType(), ConfigPropertyConstants.WELCOME_MESSAGE.getDescription()); - qm.createConfigProperty( - ConfigPropertyConstants.IS_WELCOME_MESSAGE.getGroupName(), - ConfigPropertyConstants.IS_WELCOME_MESSAGE.getPropertyName(), - ConfigPropertyConstants.IS_WELCOME_MESSAGE.getDefaultPropertyValue(), - ConfigPropertyConstants.IS_WELCOME_MESSAGE.getPropertyType(), - ConfigPropertyConstants.IS_WELCOME_MESSAGE.getDescription()); - - Response response = jersey.target(V1_CONFIG_PROPERTY + "/welcomeMessage").request() - .header(X_API_KEY, apiKey).get(); - JsonArray json = parseJsonArray(response); - assertEquals(json.size(), 2); - assertEquals(ConfigPropertyConstants.WELCOME_MESSAGE.getDefaultPropertyValue(), - json.getFirst().asJsonObject().getString("propertyValue")); - assertEquals("false", json.get(1).asJsonObject().getString("propertyValue")); - } - - @Test - public void getGreetingMessageEnabledTest() { - qm.createConfigProperty( - ConfigPropertyConstants.WELCOME_MESSAGE.getGroupName(), - ConfigPropertyConstants.WELCOME_MESSAGE.getPropertyName(), - ConfigPropertyConstants.WELCOME_MESSAGE.getDefaultPropertyValue(), - ConfigPropertyConstants.WELCOME_MESSAGE.getPropertyType(), - ConfigPropertyConstants.WELCOME_MESSAGE.getDescription()); - - qm.createConfigProperty( - ConfigPropertyConstants.IS_WELCOME_MESSAGE.getGroupName(), - ConfigPropertyConstants.IS_WELCOME_MESSAGE.getPropertyName(), - "true", - ConfigPropertyConstants.IS_WELCOME_MESSAGE.getPropertyType(), - ConfigPropertyConstants.IS_WELCOME_MESSAGE.getDescription()); - - Response response = jersey.target(V1_CONFIG_PROPERTY + "/welcomeMessage").request() + Response response = jersey.target(V1_CONFIG_PROPERTY + "/public/generel/welcome.message.html").request() .header(X_API_KEY, apiKey).get(); - JsonArray json = parseJsonArray(response); - assertEquals(json.size(), 2); - assertEquals(ConfigPropertyConstants.WELCOME_MESSAGE.getDefaultPropertyValue(), - json.getJsonObject(0).getString("propertyValue")); - assertEquals("true", json.getJsonObject(1).getString("propertyValue")); + assertEquals(403, response.getStatus()); } - } From 1612613a31f813e64eab9eb34ffd3946a7b94406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Tue, 10 Sep 2024 15:49:06 +0200 Subject: [PATCH 120/429] Fixed issues from review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../resources/v1/ConfigPropertyResource.java | 14 +++++----- .../v1/ConfigPropertyResourceTest.java | 26 ++++++++++++------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/ConfigPropertyResource.java b/src/main/java/org/dependencytrack/resources/v1/ConfigPropertyResource.java index 19f2738da1..cc1327a666 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ConfigPropertyResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ConfigPropertyResource.java @@ -169,16 +169,18 @@ public Response updateConfigProperty(List list) { @ApiResponse(responseCode = "403", description = "This is not a public visible ConfigProperty") }) @AuthenticationNotRequired - public Response getGreetingMessage( + public Response getPublicConfigProperty( @Parameter(description = "The group name of the value to retrieve", required = true) @PathParam("groupName") String groupName, @Parameter(description = "The property name of the value to retrieve", required = true) @PathParam("propertyName") String propertyName) { + ConfigProperty sampleProperty = new ConfigProperty(); + sampleProperty.setGroupName(groupName); + sampleProperty.setPropertyName(propertyName); + ConfigPropertyConstants publicConfigProperty = ConfigPropertyConstants.ofProperty(sampleProperty); + if (!publicConfigProperty.getIsPublic()) { + return Response.status(Response.Status.FORBIDDEN).build(); + } try (QueryManager qm = new QueryManager(getAlpineRequest())) { ConfigProperty property = qm.getConfigProperty(groupName, propertyName); - qm.close(); - ConfigPropertyConstants publicConfigProperty = ConfigPropertyConstants.ofProperty(property); - if (!publicConfigProperty.getIsPublic()) { - return Response.status(Response.Status.FORBIDDEN).entity("This is not a public visible ConfigProperty").build(); - } return Response.ok(property).build(); } } diff --git a/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java index 367241c536..b7070c6c14 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ConfigPropertyResourceTest.java @@ -385,16 +385,22 @@ public void updateConfigPropertyBomValidationTagsInclusiveTest() { } @Test - public void getPublicForbiddenTest() { - qm.createConfigProperty( - ConfigPropertyConstants.WELCOME_MESSAGE.getGroupName(), - ConfigPropertyConstants.WELCOME_MESSAGE.getPropertyName(), - ConfigPropertyConstants.WELCOME_MESSAGE.getDefaultPropertyValue(), - ConfigPropertyConstants.WELCOME_MESSAGE.getPropertyType(), - ConfigPropertyConstants.WELCOME_MESSAGE.getDescription()); + public void getPublicAllPropertiesTest() { + for (ConfigPropertyConstants configProperty : ConfigPropertyConstants.values()) { + String groupName = configProperty.getGroupName(); + String propertyName = configProperty.getPropertyName(); + qm.createConfigProperty( + groupName, + propertyName, + configProperty.getDefaultPropertyValue(), + configProperty.getPropertyType(), + configProperty.getDescription()); - Response response = jersey.target(V1_CONFIG_PROPERTY + "/public/generel/welcome.message.html").request() - .header(X_API_KEY, apiKey).get(); - assertEquals(403, response.getStatus()); + Response response = jersey.target(V1_CONFIG_PROPERTY + "/public/" + groupName + "/" + propertyName) + .request() + .header(X_API_KEY, apiKey).get(); + int status = configProperty.getIsPublic() ? 200 : 403; + assertEquals(status, response.getStatus()); + } } } From 1737c455a29300cddcf971ffd531fe4c5ef3617a Mon Sep 17 00:00:00 2001 From: Thomas Schauer-Koeckeis Date: Fri, 23 Aug 2024 14:01:06 +0200 Subject: [PATCH 121/429] feat: Team Selection Added additional API point to get teams Initial Team is given to the project Signed-off-by: Thomas Schauer-Koeckeis --- .../dependencytrack/model/AvailableTeams.java | 56 +++ .../org/dependencytrack/model/LittleTeam.java | 53 +++ .../org/dependencytrack/model/Project.java | 39 +- .../resources/v1/ProjectResource.java | 448 ++++++++---------- .../resources/v1/TeamResource.java | 197 ++++---- 5 files changed, 444 insertions(+), 349 deletions(-) create mode 100644 src/main/java/org/dependencytrack/model/AvailableTeams.java create mode 100644 src/main/java/org/dependencytrack/model/LittleTeam.java diff --git a/src/main/java/org/dependencytrack/model/AvailableTeams.java b/src/main/java/org/dependencytrack/model/AvailableTeams.java new file mode 100644 index 0000000000..1a4522166f --- /dev/null +++ b/src/main/java/org/dependencytrack/model/AvailableTeams.java @@ -0,0 +1,56 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.model; + +import java.io.Serializable; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AvailableTeams implements Serializable { + private boolean required; + private List teams; + + public boolean isRequired() { + return required; + } + + public void setRequired(final boolean required) { + this.required = required; + } + + public List getTeams() { + return teams; + } + + public void setTeams(final List teams) { + this.teams = teams; + } + + @Override + public String toString() { + List strlistTeams = teams.stream() + .map(Object::toString) + .toList(); + String strTeams = String.join(",", strlistTeams); + return String.format("required: %s, teams: [ %s ]", required, strTeams); + } + +} diff --git a/src/main/java/org/dependencytrack/model/LittleTeam.java b/src/main/java/org/dependencytrack/model/LittleTeam.java new file mode 100644 index 0000000000..5355b5a0e0 --- /dev/null +++ b/src/main/java/org/dependencytrack/model/LittleTeam.java @@ -0,0 +1,53 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.model; + +import java.io.Serializable; + +import java.util.UUID; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class LittleTeam implements Serializable { + + private UUID value; + private String text; + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public UUID getValue() { + return value; + } + + public void setValue(UUID value) { + this.value = value; + } + + @Override + public String toString() { + return String.format("{value: %s, text: %s}", value.toString(), text); + } +} diff --git a/src/main/java/org/dependencytrack/model/Project.java b/src/main/java/org/dependencytrack/model/Project.java index 74fdef9446..74b8480b3c 100644 --- a/src/main/java/org/dependencytrack/model/Project.java +++ b/src/main/java/org/dependencytrack/model/Project.java @@ -190,7 +190,8 @@ public enum FetchGroup { @Index(name = "PROJECT_CPE_IDX") @Size(max = 255) @JsonDeserialize(using = TrimmedStringDeserializer.class) - //Patterns obtained from https://csrc.nist.gov/schema/cpe/2.3/cpe-naming_2.3.xsd + // Patterns obtained from + // https://csrc.nist.gov/schema/cpe/2.3/cpe-naming_2.3.xsd @Pattern(regexp = "(cpe:2\\.3:[aho\\*\\-](:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#$$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|}~]))+(\\?*|\\*?))|[\\*\\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\\*\\-]))(:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#$$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|}~]))+(\\?*|\\*?))|[\\*\\-])){4})|([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\\._\\-~%]*){0,6})", message = "The CPE must conform to the CPE v2.2 or v2.3 specification defined by NIST") private String cpe; @@ -223,7 +224,7 @@ public enum FetchGroup { @Persistent @Column(name = "PARENT_PROJECT_ID") - @JsonIncludeProperties(value = {"name", "version", "uuid"}) + @JsonIncludeProperties(value = { "name", "version", "uuid" }) private Project parent; @Persistent(mappedBy = "parent") @@ -240,7 +241,8 @@ public enum FetchGroup { private List tags; /** - * Convenience field which will contain the date of the last entry in the {@link Bom} table + * Convenience field which will contain the date of the last entry in the + * {@link Bom} table */ @Persistent @Index(name = "PROJECT_LASTBOMIMPORT_IDX") @@ -249,7 +251,8 @@ public enum FetchGroup { private Date lastBomImport; /** - * Convenience field which will contain the format of the last entry in the {@link Bom} table + * Convenience field which will contain the format of the last entry in the + * {@link Bom} table */ @Persistent @Index(name = "PROJECT_LASTBOMIMPORT_FORMAT_IDX") @@ -257,7 +260,8 @@ public enum FetchGroup { private String lastBomImportFormat; /** - * Convenience field which stores the Inherited Risk Score (IRS) of the last metric in the {@link ProjectMetrics} table + * Convenience field which stores the Inherited Risk Score (IRS) of the last + * metric in the {@link ProjectMetrics} table */ @Persistent @Index(name = "PROJECT_LAST_RISKSCORE_IDX") @@ -289,6 +293,7 @@ public enum FetchGroup { private transient ProjectMetrics metrics; private transient List versions; private transient List dependencyGraph; + private UUID initialTeam; public long getId() { return id; @@ -308,20 +313,22 @@ public void setAuthors(List authors) { @Deprecated @JsonInclude(JsonInclude.Include.NON_EMPTY) - public String getAuthor(){ + public String getAuthor() { return ModelConverter.convertContactsToString(this.authors); } @Deprecated - public void setAuthor(String author){ - if(this.authors==null){ + public void setAuthor(String author) { + if (this.authors == null) { this.authors = new ArrayList<>(); - } else{ + } else { this.authors.clear(); } - this.authors.add(new OrganizationalContact() {{ - setName(author); - }}); + this.authors.add(new OrganizationalContact() { + { + setName(author); + } + }); } public String getPublisher() { @@ -560,6 +567,14 @@ public void setMetadata(final ProjectMetadata metadata) { this.metadata = metadata; } + public UUID getInitialTeam() { + return this.initialTeam; + } + + public void setInitialTeam(UUID initialTeam) { + this.initialTeam = initialTeam; + } + @JsonIgnore public List getDependencyGraph() { return dependencyGraph; diff --git a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java index c9cd063908..ef8ed26d49 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java @@ -21,6 +21,9 @@ import alpine.common.logging.Logger; import alpine.event.framework.Event; import alpine.model.Team; +import alpine.model.UserPrincipal; +import alpine.model.ConfigProperty; +import alpine.model.Permission; import alpine.persistence.PaginatedResult; import alpine.server.auth.PermissionRequired; import alpine.server.resources.AlpineResource; @@ -65,6 +68,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.UUID; import java.util.function.BiConsumer; import java.util.function.Function; @@ -86,39 +90,31 @@ public class ProjectResource extends AlpineResource { @GET @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Returns a list of all projects", - description = "

    Requires permission VIEW_PORTFOLIO

    " - ) + @Operation(summary = "Returns a list of all projects", description = "

    Requires permission VIEW_PORTFOLIO

    ") @PaginatedApi @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "A list of all projects", - headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), - content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) - ), + @ApiResponse(responseCode = "200", description = "A list of all projects", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), @ApiResponse(responseCode = "401", description = "Unauthorized") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) - public Response getProjects(@Parameter(description = "The optional name of the project to query on", required = false) - @QueryParam("name") String name, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) - @QueryParam("excludeInactive") boolean excludeInactive, - @Parameter(description = "Optionally excludes children projects from being returned", required = false) - @QueryParam("onlyRoot") boolean onlyRoot, - @Parameter(description = "The UUID of the team which projects shall be excluded", schema = @Schema(type = "string", format = "uuid"), required = false) - @QueryParam("notAssignedToTeamWithUuid") @ValidUuid String notAssignedToTeamWithUuid) { + public Response getProjects( + @Parameter(description = "The optional name of the project to query on", required = false) @QueryParam("name") String name, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive, + @Parameter(description = "Optionally excludes children projects from being returned", required = false) @QueryParam("onlyRoot") boolean onlyRoot, + @Parameter(description = "The UUID of the team which projects shall be excluded", schema = @Schema(type = "string", format = "uuid"), required = false) @QueryParam("notAssignedToTeamWithUuid") @ValidUuid String notAssignedToTeamWithUuid) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { Team notAssignedToTeam = null; if (StringUtils.isNotEmpty(notAssignedToTeamWithUuid)) { notAssignedToTeam = qm.getObjectByUuid(Team.class, notAssignedToTeamWithUuid); if (notAssignedToTeam == null) { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the team could not be found.").build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the team could not be found.") + .build(); } } - final PaginatedResult result = (name != null) ? qm.getProjects(name, excludeInactive, onlyRoot, notAssignedToTeam) : qm.getProjects(true, excludeInactive, onlyRoot, notAssignedToTeam); + final PaginatedResult result = (name != null) + ? qm.getProjects(name, excludeInactive, onlyRoot, notAssignedToTeam) + : qm.getProjects(true, excludeInactive, onlyRoot, notAssignedToTeam); return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); } } @@ -126,31 +122,24 @@ public Response getProjects(@Parameter(description = "The optional name of the p @GET @Path("/{uuid}") @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Returns a specific project", - description = "

    Requires permission VIEW_PORTFOLIO

    " - ) + @Operation(summary = "Returns a specific project", description = "

    Requires permission VIEW_PORTFOLIO

    ") @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "A specific project", - content = @Content(schema = @Schema(implementation = Project.class)) - ), + @ApiResponse(responseCode = "200", description = "A specific project", content = @Content(schema = @Schema(implementation = Project.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified project is forbidden"), @ApiResponse(responseCode = "404", description = "The project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getProject( - @Parameter(description = "The UUID of the project to retrieve", schema = @Schema(type = "string", format = "uuid"), required = true) - @PathParam("uuid") @ValidUuid String uuid) { + @Parameter(description = "The UUID of the project to retrieve", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { final Project project = qm.getProject(uuid); if (project != null) { if (qm.hasAccess(super.getPrincipal(), project)) { return Response.ok(project).build(); } else { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden").build(); } } else { return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); @@ -161,34 +150,25 @@ public Response getProject( @GET @Path("/lookup") @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Returns a specific project by its name and version", - operationId = "getProjectByNameAndVersion", - description = "

    Requires permission VIEW_PORTFOLIO

    " - ) + @Operation(summary = "Returns a specific project by its name and version", operationId = "getProjectByNameAndVersion", description = "

    Requires permission VIEW_PORTFOLIO

    ") @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "A specific project by its name and version", - content = @Content(schema = @Schema(implementation = Project.class)) - ), + @ApiResponse(responseCode = "200", description = "A specific project by its name and version", content = @Content(schema = @Schema(implementation = Project.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified project is forbidden"), @ApiResponse(responseCode = "404", description = "The project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getProject( - @Parameter(description = "The name of the project to query on", required = true) - @QueryParam("name") String name, - @Parameter(description = "The version of the project to query on", required = true) - @QueryParam("version") String version) { + @Parameter(description = "The name of the project to query on", required = true) @QueryParam("name") String name, + @Parameter(description = "The version of the project to query on", required = true) @QueryParam("version") String version) { try (QueryManager qm = new QueryManager()) { final Project project = qm.getProject(name, version); if (project != null) { if (qm.hasAccess(super.getPrincipal(), project)) { return Response.ok(project).build(); } else { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden").build(); } } else { return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); @@ -199,28 +179,17 @@ public Response getProject( @GET @Path("/tag/{tag}") @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Returns a list of all projects by tag", - description = "

    Requires permission VIEW_PORTFOLIO

    " - ) + @Operation(summary = "Returns a list of all projects by tag", description = "

    Requires permission VIEW_PORTFOLIO

    ") @PaginatedApi @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "A list of all projects by tag", - headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), - content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) - ), + @ApiResponse(responseCode = "200", description = "A list of all projects by tag", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), @ApiResponse(responseCode = "401", description = "Unauthorized") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getProjectsByTag( - @Parameter(description = "The tag to query on", required = true) - @PathParam("tag") String tagString, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) - @QueryParam("excludeInactive") boolean excludeInactive, - @Parameter(description = "Optionally excludes children projects from being returned", required = false) - @QueryParam("onlyRoot") boolean onlyRoot) { + @Parameter(description = "The tag to query on", required = true) @PathParam("tag") String tagString, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive, + @Parameter(description = "Optionally excludes children projects from being returned", required = false) @QueryParam("onlyRoot") boolean onlyRoot) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final Tag tag = qm.getTagByName(tagString); final PaginatedResult result = qm.getProjects(tag, true, excludeInactive, onlyRoot); @@ -231,53 +200,36 @@ public Response getProjectsByTag( @GET @Path("/classifier/{classifier}") @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Returns a list of all projects by classifier", - description = "

    Requires permission VIEW_PORTFOLIO

    " - ) + @Operation(summary = "Returns a list of all projects by classifier", description = "

    Requires permission VIEW_PORTFOLIO

    ") @PaginatedApi @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "A list of all projects by classifier", - headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), - content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) - ), + @ApiResponse(responseCode = "200", description = "A list of all projects by classifier", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), @ApiResponse(responseCode = "401", description = "Unauthorized") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getProjectsByClassifier( - @Parameter(description = "The classifier to query on", required = true) - @PathParam("classifier") String classifierString, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) - @QueryParam("excludeInactive") boolean excludeInactive, - @Parameter(description = "Optionally excludes children projects from being returned", required = false) - @QueryParam("onlyRoot") boolean onlyRoot) { + @Parameter(description = "The classifier to query on", required = true) @PathParam("classifier") String classifierString, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive, + @Parameter(description = "Optionally excludes children projects from being returned", required = false) @QueryParam("onlyRoot") boolean onlyRoot) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final Classifier classifier = Classifier.valueOf(classifierString); final PaginatedResult result = qm.getProjects(classifier, true, excludeInactive, onlyRoot); return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); } catch (IllegalArgumentException e) { - return Response.status(Response.Status.BAD_REQUEST).entity("The classifier type specified is not valid.").build(); + return Response.status(Response.Status.BAD_REQUEST).entity("The classifier type specified is not valid.") + .build(); } } @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Creates a new project", - description = """ -

    If a parent project exists, parent.uuid is required

    -

    Requires permission PORTFOLIO_MANAGEMENT

    - """ - ) + @Operation(summary = "Creates a new project", description = """ +

    If a parent project exists, parent.uuid is required

    +

    Requires permission PORTFOLIO_MANAGEMENT

    + """) @ApiResponses(value = { - @ApiResponse( - responseCode = "201", - description = "The created project", - content = @Content(schema = @Schema(implementation = Project.class)) - ), + @ApiResponse(responseCode = "201", description = "The created project", content = @Content(schema = @Schema(implementation = Project.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "409", description = """
      @@ -299,30 +251,86 @@ public Response createProject(Project jsonProject) { validator.validateProperty(jsonProject, "classifier"), validator.validateProperty(jsonProject, "cpe"), validator.validateProperty(jsonProject, "purl"), - validator.validateProperty(jsonProject, "swidTagId") - ); + validator.validateProperty(jsonProject, "swidTagId"), + validator.validateProperty(jsonProject, "initialTeam")); if (jsonProject.getClassifier() == null) { jsonProject.setClassifier(Classifier.APPLICATION); } try (QueryManager qm = new QueryManager()) { if (jsonProject.getParent() != null && jsonProject.getParent().getUuid() != null) { Project parent = qm.getObjectByUuid(Project.class, jsonProject.getParent().getUuid()); - jsonProject.setParent(parent); + jsonProject.setParent(parent); } - if (!qm.doesProjectExist(StringUtils.trimToNull(jsonProject.getName()), StringUtils.trimToNull(jsonProject.getVersion()))) { + if (!qm.doesProjectExist(StringUtils.trimToNull(jsonProject.getName()), + StringUtils.trimToNull(jsonProject.getVersion()))) { final Project project; try { project = qm.createProject(jsonProject, jsonProject.getTags(), true); - } catch (IllegalArgumentException e){ + } catch (IllegalArgumentException e) { LOGGER.debug(e.getMessage()); - return Response.status(Response.Status.CONFLICT).entity("An inactive Parent cannot be selected as parent").build(); + return Response.status(Response.Status.CONFLICT) + .entity("An inactive Parent cannot be selected as parent") + .build(); + } + UserPrincipal user; + if (super.isLdapUser()) { + user = qm.getLdapUser(getPrincipal().getName()); + } else if (super.isManagedUser()) { + user = qm.getManagedUser(getPrincipal().getName()); + } else if (super.isOidcUser()) { + user = qm.getOidcUser(getPrincipal().getName()); + } else { + return Response.status(401).build(); + } + boolean isAdmin = false; + boolean required = false; + final List configProperties = qm.getConfigProperties(); + for (final ConfigProperty configProperty : configProperties) { + // Checks if User needs to supply a Team + if (configProperty.getGroupName().equals("access-management") + && configProperty.getPropertyName().equals("acl.enabled")) { + required = configProperty.getPropertyValue().equals("true"); + break; + } + } + List permissions = user.getPermissions(); + for (Permission permission : permissions) { + // Checks if user has Right to submit any team to the project + if (permission.getName().equals("ACCESS_MANAGEMENT")) { + isAdmin = true; + break; + } + } + if (required && jsonProject.getInitialTeam() == null) { + return Response.status(422).build(); + } + final UUID teamUuid = jsonProject.getInitialTeam(); + if (!isAdmin) { + boolean hasTeam = false; + List teams = user.getTeams(); + for (Team team : teams) { + if (team.getUuid().equals(teamUuid)) { + hasTeam = true; + break; + } + } + if (!hasTeam) { + return Response.status(403).build(); + } + + } + if (jsonProject.getInitialTeam() != null) { + final Team team = qm.getObjectByUuid(Team.class, teamUuid); + project.addAccessTeam(team); } Principal principal = getPrincipal(); qm.updateNewProjectACL(project, principal); LOGGER.info("Project " + project.toString() + " created by " + super.getPrincipal().getName()); return Response.status(Response.Status.CREATED).entity(project).build(); } else { - return Response.status(Response.Status.CONFLICT).entity("A project with the specified name already exists.").build(); + return Response.status(Response.Status.CONFLICT) + .entity("A project with the specified name already exists.") + .build(); } } } @@ -330,16 +338,9 @@ public Response createProject(Project jsonProject) { @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Updates a project", - description = "

      Requires permission PORTFOLIO_MANAGEMENT

      " - ) + @Operation(summary = "Updates a project", description = "

      Requires permission PORTFOLIO_MANAGEMENT

      ") @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "The updated project", - content = @Content(schema = @Schema(implementation = Project.class)) - ), + @ApiResponse(responseCode = "200", description = "The updated project", content = @Content(schema = @Schema(implementation = Project.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found"), @ApiResponse(responseCode = "409", description = """ @@ -363,8 +364,7 @@ public Response updateProject(Project jsonProject) { validator.validateProperty(jsonProject, "classifier"), validator.validateProperty(jsonProject, "cpe"), validator.validateProperty(jsonProject, "purl"), - validator.validateProperty(jsonProject, "swidTagId") - ); + validator.validateProperty(jsonProject, "swidTagId")); if (jsonProject.getClassifier() == null) { jsonProject.setClassifier(Classifier.APPLICATION); } @@ -372,7 +372,8 @@ public Response updateProject(Project jsonProject) { Project project = qm.getObjectByUuid(Project.class, jsonProject.getUuid()); if (project != null) { if (!qm.hasAccess(super.getPrincipal(), project)) { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden").build(); } final String name = StringUtils.trimToNull(jsonProject.getName()); final String version = StringUtils.trimToNull(jsonProject.getVersion()); @@ -384,17 +385,19 @@ public Response updateProject(Project jsonProject) { } try { project = qm.updateProject(jsonProject, true); - } catch (IllegalArgumentException e){ + } catch (IllegalArgumentException e) { LOGGER.debug(e.getMessage()); return Response.status(Response.Status.CONFLICT).entity(e.getMessage()).build(); } LOGGER.info("Project " + project.toString() + " updated by " + super.getPrincipal().getName()); return Response.ok(project).build(); } else { - return Response.status(Response.Status.CONFLICT).entity("A project with the specified name and version already exists.").build(); + return Response.status(Response.Status.CONFLICT) + .entity("A project with the specified name and version already exists.").build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") + .build(); } } } @@ -403,16 +406,9 @@ public Response updateProject(Project jsonProject) { @Path("/{uuid}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Partially updates a project", - description = "

      Requires permission PORTFOLIO_MANAGEMENT

      " - ) + @Operation(summary = "Partially updates a project", description = "

      Requires permission PORTFOLIO_MANAGEMENT

      ") @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "The updated project", - content = @Content(schema = @Schema(implementation = Project.class)) - ), + @ApiResponse(responseCode = "200", description = "The updated project", content = @Content(schema = @Schema(implementation = Project.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found"), @ApiResponse(responseCode = "409", description = """ @@ -425,8 +421,7 @@ public Response updateProject(Project jsonProject) { }) @PermissionRequired(Permissions.Constants.PORTFOLIO_MANAGEMENT) public Response patchProject( - @Parameter(description = "The UUID of the project to modify", schema = @Schema(type = "string", format = "uuid"), required = true) - @PathParam("uuid") @ValidUuid String uuid, + @Parameter(description = "The UUID of the project to modify", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid, Project jsonProject) { final Validator validator = getValidator(); failOnValidationError( @@ -440,22 +435,24 @@ public Response patchProject( validator.validateProperty(jsonProject, "classifier"), validator.validateProperty(jsonProject, "cpe"), validator.validateProperty(jsonProject, "purl"), - validator.validateProperty(jsonProject, "swidTagId") - ); + validator.validateProperty(jsonProject, "swidTagId")); try (QueryManager qm = new QueryManager()) { Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { if (!qm.hasAccess(super.getPrincipal(), project)) { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden").build(); } var modified = false; project = qm.detachWithGroups(project, List.of(FetchGroup.DEFAULT, Project.FetchGroup.PARENT.name())); modified |= setIfDifferent(jsonProject, project, Project::getName, Project::setName); modified |= setIfDifferent(jsonProject, project, Project::getVersion, Project::setVersion); - // if either name or version has been changed, verify that this new combination does not already exist + // if either name or version has been changed, verify that this new combination + // does not already exist if (modified && qm.doesProjectExist(project.getName(), project.getVersion())) { - return Response.status(Response.Status.CONFLICT).entity("A project with the specified name and version already exists.").build(); + return Response.status(Response.Status.CONFLICT) + .entity("A project with the specified name and version already exists.").build(); } modified |= setIfDifferent(jsonProject, project, Project::getAuthors, Project::setAuthors); modified |= setIfDifferent(jsonProject, project, Project::getPublisher, Project::setPublisher); @@ -471,10 +468,12 @@ public Response patchProject( if (jsonProject.getParent() != null && jsonProject.getParent().getUuid() != null) { final Project parent = qm.getObjectByUuid(Project.class, jsonProject.getParent().getUuid()); if (parent == null) { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the parent project could not be found.").build(); + return Response.status(Response.Status.NOT_FOUND) + .entity("The UUID of the parent project could not be found.").build(); } if (!qm.hasAccess(getPrincipal(), parent)) { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified parent project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN) + .entity("Access to the specified parent project is forbidden").build(); } modified |= project.getParent() == null || !parent.getUuid().equals(project.getParent().getUuid()); project.setParent(parent); @@ -484,13 +483,13 @@ public Response patchProject( project.setTags(jsonProject.getTags()); } if (isCollectionModified(jsonProject.getExternalReferences(), project.getExternalReferences())) { - modified = true; - project.setExternalReferences(jsonProject.getExternalReferences()); + modified = true; + project.setExternalReferences(jsonProject.getExternalReferences()); } if (modified) { try { project = qm.updateProject(project, true); - } catch (IllegalArgumentException e){ + } catch (IllegalArgumentException e) { LOGGER.debug(e.getMessage()); return Response.status(Response.Status.CONFLICT).entity(e.getMessage()).build(); } @@ -500,16 +499,18 @@ public Response patchProject( return Response.notModified().build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") + .build(); } } } /** - * returns `true` if the given [updated] collection should be considered an update of the [original] collection. + * returns `true` if the given [updated] collection should be considered an + * update of the [original] collection. */ private static boolean isCollectionModified(Collection updated, Collection original) { - return updated != null && (!Collections.isEmpty(updated) || !Collections.isEmpty(original)); + return updated != null && (!Collections.isEmpty(updated) || !Collections.isEmpty(original)); } /** @@ -518,16 +519,17 @@ private static boolean isCollectionModified(Collection updated, Collectio * only if the new value is not {@code null} and it is not * {@link Object#equals(java.lang.Object) equal to} the old value. * - * @param the type of the old and new value + * @param the type of the old and new value * @param source the source object that contains the new value * @param target the target object that should be updated * @param getter the method to retrieve the new value from {@code source} - * and the old value from {@code target} + * and the old value from {@code target} * @param setter the method to set the new value on {@code target} * @return {@code true} if {@code target} has been changed, else - * {@code false} + * {@code false} */ - private boolean setIfDifferent(final Project source, final Project target, final Function getter, final BiConsumer setter) { + private boolean setIfDifferent(final Project source, final Project target, final Function getter, + final BiConsumer setter) { final T newValue = getter.apply(source); if (newValue != null && !newValue.equals(getter.apply(target))) { setter.accept(target, newValue); @@ -541,10 +543,7 @@ private boolean setIfDifferent(final Project source, final Project target, f @Path("/{uuid}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Deletes a project", - description = "

      Requires permission PORTFOLIO_MANAGEMENT

      " - ) + @Operation(summary = "Deletes a project", description = "

      Requires permission PORTFOLIO_MANAGEMENT

      ") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "Project removed successfully"), @ApiResponse(responseCode = "401", description = "Unauthorized"), @@ -553,8 +552,7 @@ private boolean setIfDifferent(final Project source, final Project target, f }) @PermissionRequired(Permissions.Constants.PORTFOLIO_MANAGEMENT) public Response deleteProject( - @Parameter(description = "The UUID of the project to delete", schema = @Schema(type = "string", format = "uuid"), required = true) - @PathParam("uuid") @ValidUuid String uuid) { + @Parameter(description = "The UUID of the project to delete", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { final Project project = qm.getObjectByUuid(Project.class, uuid, Project.FetchGroup.ALL.name()); if (project != null) { @@ -563,10 +561,13 @@ public Response deleteProject( qm.recursivelyDelete(project, true); return Response.status(Response.Status.NO_CONTENT).build(); } else { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden") + .build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") + .build(); } } } @@ -575,16 +576,9 @@ public Response deleteProject( @Path("/clone") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Clones a project", - description = "

      Requires permission PORTFOLIO_MANAGEMENT

      " - ) + @Operation(summary = "Clones a project", description = "

      Requires permission PORTFOLIO_MANAGEMENT

      ") @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "Token to be used for checking cloning progress", - content = @Content(schema = @Schema(implementation = BomUploadResponse.class)) - ), + @ApiResponse(responseCode = "200", description = "Token to be used for checking cloning progress", content = @Content(schema = @Schema(implementation = BomUploadResponse.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found") }) @@ -593,16 +587,19 @@ public Response cloneProject(CloneProjectRequest jsonRequest) { final Validator validator = super.getValidator(); failOnValidationError( validator.validateProperty(jsonRequest, "project"), - validator.validateProperty(jsonRequest, "version") - ); + validator.validateProperty(jsonRequest, "version")); try (QueryManager qm = new QueryManager()) { - final Project sourceProject = qm.getObjectByUuid(Project.class, jsonRequest.getProject(), Project.FetchGroup.ALL.name()); + final Project sourceProject = qm.getObjectByUuid(Project.class, jsonRequest.getProject(), + Project.FetchGroup.ALL.name()); if (sourceProject != null) { if (!qm.hasAccess(super.getPrincipal(), sourceProject)) { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden") + .build(); } if (qm.doesProjectExist(sourceProject.getName(), StringUtils.trimToNull(jsonRequest.getVersion()))) { - return Response.status(Response.Status.CONFLICT).entity("A project with the specified name and version already exists.").build(); + return Response.status(Response.Status.CONFLICT) + .entity("A project with the specified name and version already exists.").build(); } LOGGER.info("Project " + sourceProject + " is being cloned by " + super.getPrincipal().getName()); @@ -610,36 +607,27 @@ public Response cloneProject(CloneProjectRequest jsonRequest) { Event.dispatch(event); return Response.ok(java.util.Collections.singletonMap("token", event.getChainIdentifier())).build(); } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") + .build(); } } } - @GET @Path("/{uuid}/children") @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Returns a list of all children for a project", - description = "

      Requires permission VIEW_PORTFOLIO

      " - ) + @Operation(summary = "Returns a list of all children for a project", description = "

      Requires permission VIEW_PORTFOLIO

      ") @PaginatedApi @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "A list of all children for a project", - headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), - content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) - ), + @ApiResponse(responseCode = "200", description = "A list of all children for a project", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified project is forbidden"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) - public Response getChildrenProjects(@Parameter(description = "The UUID of the project to get the children from", schema = @Schema(type = "string", format = "uuid"), required = true) - @PathParam("uuid") @ValidUuid String uuid, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) - @QueryParam("excludeInactive") boolean excludeInactive) { + public Response getChildrenProjects( + @Parameter(description = "The UUID of the project to get the children from", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { @@ -647,10 +635,13 @@ public Response getChildrenProjects(@Parameter(description = "The UUID of the pr if (qm.hasAccess(super.getPrincipal(), project)) { return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); } else { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden") + .build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") + .build(); } } } @@ -658,42 +649,35 @@ public Response getChildrenProjects(@Parameter(description = "The UUID of the pr @GET @Path("/{uuid}/children/classifier/{classifier}") @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Returns a list of all children for a project by classifier", - description = "

      Requires permission VIEW_PORTFOLIO

      " - ) + @Operation(summary = "Returns a list of all children for a project by classifier", description = "

      Requires permission VIEW_PORTFOLIO

      ") @PaginatedApi @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "A list of all children for a project by classifier", - headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), - content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) - ), + @ApiResponse(responseCode = "200", description = "A list of all children for a project by classifier", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified project is forbidden"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getChildrenProjectsByClassifier( - @Parameter(description = "The classifier to query on", required = true) - @PathParam("classifier") String classifierString, - @Parameter(description = "The UUID of the project to get the children from", schema = @Schema(type = "string", format = "uuid"), required = true) - @PathParam("uuid") @ValidUuid String uuid, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) - @QueryParam("excludeInactive") boolean excludeInactive) { + @Parameter(description = "The classifier to query on", required = true) @PathParam("classifier") String classifierString, + @Parameter(description = "The UUID of the project to get the children from", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { final Classifier classifier = Classifier.valueOf(classifierString); - final PaginatedResult result = qm.getChildrenProjects(classifier, project.getUuid(), true, excludeInactive); + final PaginatedResult result = qm.getChildrenProjects(classifier, project.getUuid(), true, + excludeInactive); if (qm.hasAccess(super.getPrincipal(), project)) { return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); } else { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden") + .build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") + .build(); } } } @@ -701,30 +685,19 @@ public Response getChildrenProjectsByClassifier( @GET @Path("/{uuid}/children/tag/{tag}") @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Returns a list of all children for a project by tag", - description = "

      Requires permission VIEW_PORTFOLIO

      " - ) + @Operation(summary = "Returns a list of all children for a project by tag", description = "

      Requires permission VIEW_PORTFOLIO

      ") @PaginatedApi @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "A list of all children for a project by tag", - headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), - content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) - ), + @ApiResponse(responseCode = "200", description = "A list of all children for a project by tag", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified project is forbidden"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getChildrenProjectsByTag( - @Parameter(description = "The tag to query on", required = true) - @PathParam("tag") String tagString, - @Parameter(description = "The UUID of the project to get the children from", schema = @Schema(type = "string", format = "uuid"), required = true) - @PathParam("uuid") @ValidUuid String uuid, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) - @QueryParam("excludeInactive") boolean excludeInactive) { + @Parameter(description = "The tag to query on", required = true) @PathParam("tag") String tagString, + @Parameter(description = "The UUID of the project to get the children from", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { @@ -733,10 +706,13 @@ public Response getChildrenProjectsByTag( if (qm.hasAccess(super.getPrincipal(), project)) { return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); } else { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden") + .build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") + .build(); } } } @@ -744,41 +720,35 @@ public Response getChildrenProjectsByTag( @GET @Path("/withoutDescendantsOf/{uuid}") @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Returns a list of all projects without the descendants of the selected project", - description = "

      Requires permission VIEW_PORTFOLIO

      " - ) + @Operation(summary = "Returns a list of all projects without the descendants of the selected project", description = "

      Requires permission VIEW_PORTFOLIO

      ") @PaginatedApi @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "A list of all projects without the descendants of the selected project", - headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), - content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) - ), + @ApiResponse(responseCode = "200", description = "A list of all projects without the descendants of the selected project", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified project is forbidden"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getProjectsWithoutDescendantsOf( - @Parameter(description = "The UUID of the project which descendants will be excluded", schema = @Schema(type = "string", format = "uuid"), required = true) - @PathParam("uuid") @ValidUuid String uuid, - @Parameter(description = "The optional name of the project to query on", required = false) - @QueryParam("name") String name, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) - @QueryParam("excludeInactive") boolean excludeInactive) { + @Parameter(description = "The UUID of the project which descendants will be excluded", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid, + @Parameter(description = "The optional name of the project to query on", required = false) @QueryParam("name") String name, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { if (qm.hasAccess(super.getPrincipal(), project)) { - final PaginatedResult result = (name != null) ? qm.getProjectsWithoutDescendantsOf(name, excludeInactive, project) : qm.getProjectsWithoutDescendantsOf(excludeInactive, project); + final PaginatedResult result = (name != null) + ? qm.getProjectsWithoutDescendantsOf(name, excludeInactive, project) + : qm.getProjectsWithoutDescendantsOf(excludeInactive, project); return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); - } else{ - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + } else { + return Response.status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden") + .build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") + .build(); } } } diff --git a/src/main/java/org/dependencytrack/resources/v1/TeamResource.java b/src/main/java/org/dependencytrack/resources/v1/TeamResource.java index b55b421988..a065858ea4 100644 --- a/src/main/java/org/dependencytrack/resources/v1/TeamResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/TeamResource.java @@ -20,8 +20,11 @@ import alpine.Config; import alpine.common.logging.Logger; +import alpine.model.ConfigProperty; import alpine.model.ApiKey; +import alpine.model.Permission; import alpine.model.Team; +import alpine.model.UserPrincipal; import alpine.server.auth.PermissionRequired; import alpine.server.resources.AlpineResource; import io.swagger.v3.oas.annotations.Operation; @@ -37,6 +40,8 @@ import io.swagger.v3.oas.annotations.tags.Tag; import org.dependencytrack.auth.Permissions; import org.dependencytrack.model.validation.ValidUuid; +import org.dependencytrack.model.AvailableTeams; +import org.dependencytrack.model.LittleTeam; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.resources.v1.vo.TeamSelfResponse; import org.owasp.security.logging.SecurityMarkers; @@ -52,6 +57,8 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; + +import java.util.ArrayList; import java.util.List; import static org.datanucleus.PropertyNames.PROPERTY_RETAIN_VALUES; @@ -74,17 +81,9 @@ public class TeamResource extends AlpineResource { @GET @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Returns a list of all teams", - description = "

      Requires permission ACCESS_MANAGEMENT

      " - ) + @Operation(summary = "Returns a list of all teams", description = "

      Requires permission ACCESS_MANAGEMENT

      ") @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "A list of all teams", - headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of teams", schema = @Schema(format = "integer")), - content = @Content(array = @ArraySchema(schema = @Schema(implementation = Team.class))) - ), + @ApiResponse(responseCode = "200", description = "A list of all teams", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of teams", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Team.class)))), @ApiResponse(responseCode = "401", description = "Unauthorized") }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) @@ -99,23 +98,15 @@ public Response getTeams() { @GET @Path("/{uuid}") @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Returns a specific team", - description = "

      Requires permission ACCESS_MANAGEMENT

      " - ) + @Operation(summary = "Returns a specific team", description = "

      Requires permission ACCESS_MANAGEMENT

      ") @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "A specific team", - content = @Content(schema = @Schema(implementation = Team.class)) - ), + @ApiResponse(responseCode = "200", description = "A specific team", content = @Content(schema = @Schema(implementation = Team.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The team could not be found") }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) public Response getTeam( - @Parameter(description = "The UUID of the team to retrieve", schema = @Schema(type = "string", format = "uuid"), required = true) - @PathParam("uuid") @ValidUuid String uuid) { + @Parameter(description = "The UUID of the team to retrieve", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { final Team team = qm.getObjectByUuid(Team.class, uuid); if (team != null) { @@ -129,26 +120,18 @@ public Response getTeam( @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Creates a new team", - description = "

      Requires permission ACCESS_MANAGEMENT

      " - ) + @Operation(summary = "Creates a new team", description = "

      Requires permission ACCESS_MANAGEMENT

      ") @ApiResponses(value = { - @ApiResponse( - responseCode = "201", - description = "The created team", - content = @Content(schema = @Schema(implementation = Team.class)) - ), + @ApiResponse(responseCode = "201", description = "The created team", content = @Content(schema = @Schema(implementation = Team.class))), @ApiResponse(responseCode = "401", description = "Unauthorized") }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) - //public Response createTeam(String jsonRequest) { + // public Response createTeam(String jsonRequest) { public Response createTeam(Team jsonTeam) { - //Team team = MapperUtil.readAsObjectOf(Team.class, jsonRequest); + // Team team = MapperUtil.readAsObjectOf(Team.class, jsonRequest); final Validator validator = super.getValidator(); failOnValidationError( - validator.validateProperty(jsonTeam, "name") - ); + validator.validateProperty(jsonTeam, "name")); try (QueryManager qm = new QueryManager()) { final Team team = qm.createTeam(jsonTeam.getName(), false); @@ -160,16 +143,9 @@ public Response createTeam(Team jsonTeam) { @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Updates a team's fields", - description = "

      Requires permission ACCESS_MANAGEMENT

      " - ) + @Operation(summary = "Updates a team's fields", description = "

      Requires permission ACCESS_MANAGEMENT

      ") @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "The updated team", - content = @Content(schema = @Schema(implementation = Team.class)) - ), + @ApiResponse(responseCode = "200", description = "The updated team", content = @Content(schema = @Schema(implementation = Team.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The team could not be found") }) @@ -177,13 +153,12 @@ public Response createTeam(Team jsonTeam) { public Response updateTeam(Team jsonTeam) { final Validator validator = super.getValidator(); failOnValidationError( - validator.validateProperty(jsonTeam, "name") - ); + validator.validateProperty(jsonTeam, "name")); try (QueryManager qm = new QueryManager()) { Team team = qm.getObjectByUuid(Team.class, jsonTeam.getUuid()); if (team != null) { team.setName(jsonTeam.getName()); - //todo: set permissions + // todo: set permissions team = qm.updateTeam(jsonTeam); super.logSecurityEvent(LOGGER, SecurityMarkers.SECURITY_AUDIT, "Team updated: " + team.getName()); return Response.ok(team).build(); @@ -196,10 +171,7 @@ public Response updateTeam(Team jsonTeam) { @DELETE @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Deletes a team", - description = "

      Requires permission ACCESS_MANAGEMENT

      " - ) + @Operation(summary = "Deletes a team", description = "

      Requires permission ACCESS_MANAGEMENT

      ") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "Team removed successfully"), @ApiResponse(responseCode = "401", description = "Unauthorized"), @@ -220,26 +192,77 @@ public Response deleteTeam(Team jsonTeam) { } } + @GET + @Path("/available-teams") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Returns a list of Teams what are available as selection", description = "Requires permission PORTFOLIO_MANAGEMENT

      ") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "The Available Teams", content = @Content(schema = @Schema(implementation = AvailableTeams.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "404", description = "Teams could not be found") + }) + @PermissionRequired(Permissions.Constants.PORTFOLIO_MANAGEMENT) + public Response availableTeams() { + UserPrincipal user; + boolean isAllTeams = false; + boolean required = false; + try (QueryManager qm = new QueryManager()) { + if (super.isLdapUser()) { + user = qm.getLdapUser(getPrincipal().getName()); + } else if (super.isManagedUser()) { + user = qm.getManagedUser(getPrincipal().getName()); + } else if (super.isOidcUser()) { + user = qm.getOidcUser(getPrincipal().getName()); + } else { + return Response.status(401).build(); + } + final List allTeams = qm.getTeams(); + final List configProperties = qm.getConfigProperties(); + for (final ConfigProperty configProperty : configProperties) { + // Replace the value of encrypted strings with the pre-defined placeholder + if (configProperty.getGroupName().equals("access-management") + && configProperty.getPropertyName().equals("acl.enabled")) { + required = configProperty.getPropertyValue().equals("true"); + break; + } + } + qm.getPersistenceManager().detachCopyAll(configProperties); + qm.close(); + List permissions = user.getPermissions(); + for (Permission permission : permissions) { + if (permission.getName().equals("ACCESS_MANAGEMENT")) { + isAllTeams = true; + break; + } + } + AvailableTeams response = new AvailableTeams(); + response.setRequired(required); + List availableTeams = new ArrayList(); + List teams = isAllTeams ? allTeams : user.getTeams(); + for (Team team : teams) { + LittleTeam newTeam = new LittleTeam(); + newTeam.setValue(team.getUuid()); + newTeam.setText(team.getName()); + availableTeams.add(newTeam); + + } + response.setTeams(availableTeams); + return Response.ok(response).build(); + } + } + @PUT @Path("/{uuid}/key") @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Generates an API key and returns its value", - description = "

      Requires permission ACCESS_MANAGEMENT

      " - ) + @Operation(summary = "Generates an API key and returns its value", description = "

      Requires permission ACCESS_MANAGEMENT

      ") @ApiResponses(value = { - @ApiResponse( - responseCode = "201", - description = "The created API key", - content = @Content(schema = @Schema(implementation = ApiKey.class)) - ), + @ApiResponse(responseCode = "201", description = "The created API key", content = @Content(schema = @Schema(implementation = ApiKey.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The team could not be found") }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) public Response generateApiKey( - @Parameter(description = "The UUID of the team to generate a key for", schema = @Schema(type = "string", format = "uuid"), required = true) - @PathParam("uuid") @ValidUuid String uuid) { + @Parameter(description = "The UUID of the team to generate a key for", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { final Team team = qm.getObjectByUuid(Team.class, uuid); if (team != null) { @@ -254,23 +277,15 @@ public Response generateApiKey( @POST @Path("/key/{apikey}") @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Regenerates an API key by removing the specified key, generating a new one and returning its value", - description = "

      Requires permission ACCESS_MANAGEMENT

      " - ) + @Operation(summary = "Regenerates an API key by removing the specified key, generating a new one and returning its value", description = "

      Requires permission ACCESS_MANAGEMENT

      ") @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "The re-generated API key", - content = @Content(schema = @Schema(implementation = ApiKey.class)) - ), + @ApiResponse(responseCode = "200", description = "The re-generated API key", content = @Content(schema = @Schema(implementation = ApiKey.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The API key could not be found") }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) public Response regenerateApiKey( - @Parameter(description = "The API key to regenerate", required = true) - @PathParam("apikey") String apikey) { + @Parameter(description = "The API key to regenerate", required = true) @PathParam("apikey") String apikey) { try (QueryManager qm = new QueryManager()) { ApiKey apiKey = qm.getApiKey(apikey); if (apiKey != null) { @@ -286,22 +301,15 @@ public Response regenerateApiKey( @Path("/key/{key}/comment") @Consumes(MediaType.TEXT_PLAIN) @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Updates an API key's comment", - description = "

      Requires permission ACCESS_MANAGEMENT

      " - ) + @Operation(summary = "Updates an API key's comment", description = "

      Requires permission ACCESS_MANAGEMENT

      ") @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "The updated API key", - content = @Content(schema = @Schema(implementation = ApiKey.class)) - ), + @ApiResponse(responseCode = "200", description = "The updated API key", content = @Content(schema = @Schema(implementation = ApiKey.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The API key could not be found") }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) public Response updateApiKeyComment(@PathParam("key") final String key, - final String comment) { + final String comment) { try (final var qm = new QueryManager()) { qm.getPersistenceManager().setProperty(PROPERTY_RETAIN_VALUES, "true"); @@ -322,10 +330,7 @@ public Response updateApiKeyComment(@PathParam("key") final String key, @DELETE @Path("/key/{apikey}") - @Operation( - summary = "Deletes the specified API key", - description = "

      Requires permission ACCESS_MANAGEMENT

      " - ) + @Operation(summary = "Deletes the specified API key", description = "

      Requires permission ACCESS_MANAGEMENT

      ") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "API key removed successfully"), @ApiResponse(responseCode = "401", description = "Unauthorized"), @@ -333,8 +338,7 @@ public Response updateApiKeyComment(@PathParam("key") final String key, }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) public Response deleteApiKey( - @Parameter(description = "The API key to delete", required = true) - @PathParam("apikey") String apikey) { + @Parameter(description = "The API key to delete", required = true) @PathParam("apikey") String apikey) { try (QueryManager qm = new QueryManager()) { final ApiKey apiKey = qm.getApiKey(apikey); if (apiKey != null) { @@ -349,14 +353,9 @@ public Response deleteApiKey( @GET @Path("self") @Produces(MediaType.APPLICATION_JSON) - @Operation( - summary = "Returns information about the current team.") + @Operation(summary = "Returns information about the current team.") @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "Information about the current team", - content = @Content(schema = @Schema(implementation = TeamSelfResponse.class)) - ), + @ApiResponse(responseCode = "200", description = "Information about the current team", content = @Content(schema = @Schema(implementation = TeamSelfResponse.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "400", description = "Invalid API key supplied"), @ApiResponse(responseCode = "404", description = "No Team for the given API key found") @@ -365,19 +364,21 @@ public Response getSelf() { if (Config.getInstance().getPropertyAsBoolean(Config.AlpineKey.ENFORCE_AUTHENTICATION)) { try (var qm = new QueryManager()) { if (isApiKey()) { - final var apiKey = qm.getApiKey(((ApiKey)getPrincipal()).getKey()); + final var apiKey = qm.getApiKey(((ApiKey) getPrincipal()).getKey()); final var team = apiKey.getTeams().stream().findFirst(); if (team.isPresent()) { return Response.ok(new TeamSelfResponse(team.get())).build(); } else { - return Response.status(Response.Status.NOT_FOUND).entity("No Team for the given API key found.").build(); + return Response.status(Response.Status.NOT_FOUND).entity("No Team for the given API key found.") + .build(); } } else { return Response.status(Response.Status.BAD_REQUEST).entity("Invalid API key supplied.").build(); } } } - // Authentication is not enabled, but we need to return a positive response without any principal data. + // Authentication is not enabled, but we need to return a positive response + // without any principal data. return Response.ok().build(); } } From 4af6f943eabf59183c164554fd00ffd2a833096e Mon Sep 17 00:00:00 2001 From: Thomas Schauer-Koeckeis Date: Tue, 27 Aug 2024 10:02:06 +0200 Subject: [PATCH 122/429] Fixed most stuff from review Signed-off-by: Thomas Schauer-Koeckeis --- .../org/dependencytrack/model/Project.java | 41 +- .../resources/v1/ProjectResource.java | 479 ++++++++++-------- .../resources/v1/TeamResource.java | 197 ++++--- .../resources/v1/vo/VisibleTeams.java | 27 + 4 files changed, 425 insertions(+), 319 deletions(-) create mode 100644 src/main/java/org/dependencytrack/resources/v1/vo/VisibleTeams.java diff --git a/src/main/java/org/dependencytrack/model/Project.java b/src/main/java/org/dependencytrack/model/Project.java index 74b8480b3c..6fc9dae62d 100644 --- a/src/main/java/org/dependencytrack/model/Project.java +++ b/src/main/java/org/dependencytrack/model/Project.java @@ -190,8 +190,7 @@ public enum FetchGroup { @Index(name = "PROJECT_CPE_IDX") @Size(max = 255) @JsonDeserialize(using = TrimmedStringDeserializer.class) - // Patterns obtained from - // https://csrc.nist.gov/schema/cpe/2.3/cpe-naming_2.3.xsd + //Patterns obtained from https://csrc.nist.gov/schema/cpe/2.3/cpe-naming_2.3.xsd @Pattern(regexp = "(cpe:2\\.3:[aho\\*\\-](:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#$$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|}~]))+(\\?*|\\*?))|[\\*\\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\\*\\-]))(:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#$$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|}~]))+(\\?*|\\*?))|[\\*\\-])){4})|([c][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\\._\\-~%]*){0,6})", message = "The CPE must conform to the CPE v2.2 or v2.3 specification defined by NIST") private String cpe; @@ -224,7 +223,7 @@ public enum FetchGroup { @Persistent @Column(name = "PARENT_PROJECT_ID") - @JsonIncludeProperties(value = { "name", "version", "uuid" }) + @JsonIncludeProperties(value = {"name", "version", "uuid"}) private Project parent; @Persistent(mappedBy = "parent") @@ -241,8 +240,7 @@ public enum FetchGroup { private List tags; /** - * Convenience field which will contain the date of the last entry in the - * {@link Bom} table + * Convenience field which will contain the date of the last entry in the {@link Bom} table */ @Persistent @Index(name = "PROJECT_LASTBOMIMPORT_IDX") @@ -251,8 +249,7 @@ public enum FetchGroup { private Date lastBomImport; /** - * Convenience field which will contain the format of the last entry in the - * {@link Bom} table + * Convenience field which will contain the format of the last entry in the {@link Bom} table */ @Persistent @Index(name = "PROJECT_LASTBOMIMPORT_FORMAT_IDX") @@ -260,8 +257,7 @@ public enum FetchGroup { private String lastBomImportFormat; /** - * Convenience field which stores the Inherited Risk Score (IRS) of the last - * metric in the {@link ProjectMetrics} table + * Convenience field which stores the Inherited Risk Score (IRS) of the last metric in the {@link ProjectMetrics} table */ @Persistent @Index(name = "PROJECT_LAST_RISKSCORE_IDX") @@ -277,7 +273,7 @@ public enum FetchGroup { @Join(column = "PROJECT_ID") @Element(column = "TEAM_ID") @Order(extensions = @Extension(vendorName = "datanucleus", key = "list-ordering", value = "name ASC")) - @JsonIgnore + //@JsonIgnore private List accessTeams; @Persistent(defaultFetchGroup = "true") @@ -293,7 +289,6 @@ public enum FetchGroup { private transient ProjectMetrics metrics; private transient List versions; private transient List dependencyGraph; - private UUID initialTeam; public long getId() { return id; @@ -313,22 +308,20 @@ public void setAuthors(List authors) { @Deprecated @JsonInclude(JsonInclude.Include.NON_EMPTY) - public String getAuthor() { + public String getAuthor(){ return ModelConverter.convertContactsToString(this.authors); } @Deprecated - public void setAuthor(String author) { - if (this.authors == null) { + public void setAuthor(String author){ + if(this.authors==null){ this.authors = new ArrayList<>(); - } else { + } else{ this.authors.clear(); } - this.authors.add(new OrganizationalContact() { - { - setName(author); - } - }); + this.authors.add(new OrganizationalContact() {{ + setName(author); + }}); } public String getPublisher() { @@ -567,14 +560,6 @@ public void setMetadata(final ProjectMetadata metadata) { this.metadata = metadata; } - public UUID getInitialTeam() { - return this.initialTeam; - } - - public void setInitialTeam(UUID initialTeam) { - this.initialTeam = initialTeam; - } - @JsonIgnore public List getDependencyGraph() { return dependencyGraph; diff --git a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java index ef8ed26d49..31c84c83e4 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java @@ -20,10 +20,9 @@ import alpine.common.logging.Logger; import alpine.event.framework.Event; +import alpine.model.ApiKey; import alpine.model.Team; import alpine.model.UserPrincipal; -import alpine.model.ConfigProperty; -import alpine.model.Permission; import alpine.persistence.PaginatedResult; import alpine.server.auth.PermissionRequired; import alpine.server.resources.AlpineResource; @@ -42,6 +41,7 @@ import org.dependencytrack.auth.Permissions; import org.dependencytrack.event.CloneProjectEvent; import org.dependencytrack.model.Classifier; +import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.Project; import org.dependencytrack.model.Tag; import org.dependencytrack.model.validation.ValidUuid; @@ -65,10 +65,10 @@ import jakarta.ws.rs.core.Response; import javax.jdo.FetchGroup; import java.security.Principal; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; -import java.util.UUID; import java.util.function.BiConsumer; import java.util.function.Function; @@ -90,31 +90,39 @@ public class ProjectResource extends AlpineResource { @GET @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns a list of all projects", description = "

      Requires permission VIEW_PORTFOLIO

      ") + @Operation( + summary = "Returns a list of all projects", + description = "

      Requires permission VIEW_PORTFOLIO

      " + ) @PaginatedApi @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "A list of all projects", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), + @ApiResponse( + responseCode = "200", + description = "A list of all projects", + headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) + ), @ApiResponse(responseCode = "401", description = "Unauthorized") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) - public Response getProjects( - @Parameter(description = "The optional name of the project to query on", required = false) @QueryParam("name") String name, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive, - @Parameter(description = "Optionally excludes children projects from being returned", required = false) @QueryParam("onlyRoot") boolean onlyRoot, - @Parameter(description = "The UUID of the team which projects shall be excluded", schema = @Schema(type = "string", format = "uuid"), required = false) @QueryParam("notAssignedToTeamWithUuid") @ValidUuid String notAssignedToTeamWithUuid) { + public Response getProjects(@Parameter(description = "The optional name of the project to query on", required = false) + @QueryParam("name") String name, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) + @QueryParam("excludeInactive") boolean excludeInactive, + @Parameter(description = "Optionally excludes children projects from being returned", required = false) + @QueryParam("onlyRoot") boolean onlyRoot, + @Parameter(description = "The UUID of the team which projects shall be excluded", schema = @Schema(type = "string", format = "uuid"), required = false) + @QueryParam("notAssignedToTeamWithUuid") @ValidUuid String notAssignedToTeamWithUuid) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { Team notAssignedToTeam = null; if (StringUtils.isNotEmpty(notAssignedToTeamWithUuid)) { notAssignedToTeam = qm.getObjectByUuid(Team.class, notAssignedToTeamWithUuid); if (notAssignedToTeam == null) { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the team could not be found.") - .build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the team could not be found.").build(); } } - final PaginatedResult result = (name != null) - ? qm.getProjects(name, excludeInactive, onlyRoot, notAssignedToTeam) - : qm.getProjects(true, excludeInactive, onlyRoot, notAssignedToTeam); + final PaginatedResult result = (name != null) ? qm.getProjects(name, excludeInactive, onlyRoot, notAssignedToTeam) : qm.getProjects(true, excludeInactive, onlyRoot, notAssignedToTeam); return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); } } @@ -122,24 +130,31 @@ public Response getProjects( @GET @Path("/{uuid}") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns a specific project", description = "

      Requires permission VIEW_PORTFOLIO

      ") + @Operation( + summary = "Returns a specific project", + description = "

      Requires permission VIEW_PORTFOLIO

      " + ) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "A specific project", content = @Content(schema = @Schema(implementation = Project.class))), + @ApiResponse( + responseCode = "200", + description = "A specific project", + content = @Content(schema = @Schema(implementation = Project.class)) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified project is forbidden"), @ApiResponse(responseCode = "404", description = "The project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getProject( - @Parameter(description = "The UUID of the project to retrieve", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { + @Parameter(description = "The UUID of the project to retrieve", schema = @Schema(type = "string", format = "uuid"), required = true) + @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { final Project project = qm.getProject(uuid); if (project != null) { if (qm.hasAccess(super.getPrincipal(), project)) { return Response.ok(project).build(); } else { - return Response.status(Response.Status.FORBIDDEN) - .entity("Access to the specified project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } } else { return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); @@ -150,25 +165,34 @@ public Response getProject( @GET @Path("/lookup") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns a specific project by its name and version", operationId = "getProjectByNameAndVersion", description = "

      Requires permission VIEW_PORTFOLIO

      ") + @Operation( + summary = "Returns a specific project by its name and version", + operationId = "getProjectByNameAndVersion", + description = "

      Requires permission VIEW_PORTFOLIO

      " + ) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "A specific project by its name and version", content = @Content(schema = @Schema(implementation = Project.class))), + @ApiResponse( + responseCode = "200", + description = "A specific project by its name and version", + content = @Content(schema = @Schema(implementation = Project.class)) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified project is forbidden"), @ApiResponse(responseCode = "404", description = "The project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getProject( - @Parameter(description = "The name of the project to query on", required = true) @QueryParam("name") String name, - @Parameter(description = "The version of the project to query on", required = true) @QueryParam("version") String version) { + @Parameter(description = "The name of the project to query on", required = true) + @QueryParam("name") String name, + @Parameter(description = "The version of the project to query on", required = true) + @QueryParam("version") String version) { try (QueryManager qm = new QueryManager()) { final Project project = qm.getProject(name, version); if (project != null) { if (qm.hasAccess(super.getPrincipal(), project)) { return Response.ok(project).build(); } else { - return Response.status(Response.Status.FORBIDDEN) - .entity("Access to the specified project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } } else { return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); @@ -179,17 +203,28 @@ public Response getProject( @GET @Path("/tag/{tag}") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns a list of all projects by tag", description = "

      Requires permission VIEW_PORTFOLIO

      ") + @Operation( + summary = "Returns a list of all projects by tag", + description = "

      Requires permission VIEW_PORTFOLIO

      " + ) @PaginatedApi @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "A list of all projects by tag", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), + @ApiResponse( + responseCode = "200", + description = "A list of all projects by tag", + headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) + ), @ApiResponse(responseCode = "401", description = "Unauthorized") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getProjectsByTag( - @Parameter(description = "The tag to query on", required = true) @PathParam("tag") String tagString, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive, - @Parameter(description = "Optionally excludes children projects from being returned", required = false) @QueryParam("onlyRoot") boolean onlyRoot) { + @Parameter(description = "The tag to query on", required = true) + @PathParam("tag") String tagString, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) + @QueryParam("excludeInactive") boolean excludeInactive, + @Parameter(description = "Optionally excludes children projects from being returned", required = false) + @QueryParam("onlyRoot") boolean onlyRoot) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final Tag tag = qm.getTagByName(tagString); final PaginatedResult result = qm.getProjects(tag, true, excludeInactive, onlyRoot); @@ -200,36 +235,53 @@ public Response getProjectsByTag( @GET @Path("/classifier/{classifier}") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns a list of all projects by classifier", description = "

      Requires permission VIEW_PORTFOLIO

      ") + @Operation( + summary = "Returns a list of all projects by classifier", + description = "

      Requires permission VIEW_PORTFOLIO

      " + ) @PaginatedApi @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "A list of all projects by classifier", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), + @ApiResponse( + responseCode = "200", + description = "A list of all projects by classifier", + headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) + ), @ApiResponse(responseCode = "401", description = "Unauthorized") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getProjectsByClassifier( - @Parameter(description = "The classifier to query on", required = true) @PathParam("classifier") String classifierString, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive, - @Parameter(description = "Optionally excludes children projects from being returned", required = false) @QueryParam("onlyRoot") boolean onlyRoot) { + @Parameter(description = "The classifier to query on", required = true) + @PathParam("classifier") String classifierString, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) + @QueryParam("excludeInactive") boolean excludeInactive, + @Parameter(description = "Optionally excludes children projects from being returned", required = false) + @QueryParam("onlyRoot") boolean onlyRoot) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final Classifier classifier = Classifier.valueOf(classifierString); final PaginatedResult result = qm.getProjects(classifier, true, excludeInactive, onlyRoot); return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); } catch (IllegalArgumentException e) { - return Response.status(Response.Status.BAD_REQUEST).entity("The classifier type specified is not valid.") - .build(); + return Response.status(Response.Status.BAD_REQUEST).entity("The classifier type specified is not valid.").build(); } } @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Creates a new project", description = """ -

      If a parent project exists, parent.uuid is required

      -

      Requires permission PORTFOLIO_MANAGEMENT

      - """) + @Operation( + summary = "Creates a new project", + description = """ +

      If a parent project exists, parent.uuid is required

      +

      Requires permission PORTFOLIO_MANAGEMENT

      + """ + ) @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "The created project", content = @Content(schema = @Schema(implementation = Project.class))), + @ApiResponse( + responseCode = "201", + description = "The created project", + content = @Content(schema = @Schema(implementation = Project.class)) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "409", description = """
        @@ -252,85 +304,58 @@ public Response createProject(Project jsonProject) { validator.validateProperty(jsonProject, "cpe"), validator.validateProperty(jsonProject, "purl"), validator.validateProperty(jsonProject, "swidTagId"), - validator.validateProperty(jsonProject, "initialTeam")); + validator.validateProperty(jsonProject, "accessTeams") + ); if (jsonProject.getClassifier() == null) { jsonProject.setClassifier(Classifier.APPLICATION); } try (QueryManager qm = new QueryManager()) { if (jsonProject.getParent() != null && jsonProject.getParent().getUuid() != null) { Project parent = qm.getObjectByUuid(Project.class, jsonProject.getParent().getUuid()); - jsonProject.setParent(parent); + jsonProject.setParent(parent); } - if (!qm.doesProjectExist(StringUtils.trimToNull(jsonProject.getName()), - StringUtils.trimToNull(jsonProject.getVersion()))) { - final Project project; - try { - project = qm.createProject(jsonProject, jsonProject.getTags(), true); - } catch (IllegalArgumentException e) { - LOGGER.debug(e.getMessage()); - return Response.status(Response.Status.CONFLICT) - .entity("An inactive Parent cannot be selected as parent") - .build(); - } - UserPrincipal user; - if (super.isLdapUser()) { - user = qm.getLdapUser(getPrincipal().getName()); - } else if (super.isManagedUser()) { - user = qm.getManagedUser(getPrincipal().getName()); - } else if (super.isOidcUser()) { - user = qm.getOidcUser(getPrincipal().getName()); - } else { - return Response.status(401).build(); - } - boolean isAdmin = false; - boolean required = false; - final List configProperties = qm.getConfigProperties(); - for (final ConfigProperty configProperty : configProperties) { - // Checks if User needs to supply a Team - if (configProperty.getGroupName().equals("access-management") - && configProperty.getPropertyName().equals("acl.enabled")) { - required = configProperty.getPropertyValue().equals("true"); - break; - } - } - List permissions = user.getPermissions(); - for (Permission permission : permissions) { - // Checks if user has Right to submit any team to the project - if (permission.getName().equals("ACCESS_MANAGEMENT")) { - isAdmin = true; + final List choosenTeams = jsonProject.getAccessTeams(); + LOGGER.info(choosenTeams.toString()); + Principal principal = getPrincipal(); + List userTeams = new ArrayList(); + if (principal instanceof final UserPrincipal userPrincipal) { + userTeams = userPrincipal.getTeams(); + } else if (principal instanceof final ApiKey apiKey) { + userTeams = apiKey.getTeams(); + } + boolean required = qm.isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED); + boolean isAdmin = qm.hasAccessManagementPermission(principal); + if (required && choosenTeams.size() == 0) { + return Response.status(422).build(); + } + List visibleTeams = isAdmin ? qm.getTeams() : userTeams; + boolean hasTeam; + for (Team choosenTeam : choosenTeams) { + hasTeam = false; + LOGGER.info(Boolean.toString(visibleTeams.contains(choosenTeam)) + choosenTeam.getName()); + for (Team team : visibleTeams) { + if (team.getUuid().equals(choosenTeam.getUuid())) { + hasTeam = true; break; } } - if (required && jsonProject.getInitialTeam() == null) { - return Response.status(422).build(); + if (!hasTeam) { + return Response.status(403).build(); } - final UUID teamUuid = jsonProject.getInitialTeam(); - if (!isAdmin) { - boolean hasTeam = false; - List teams = user.getTeams(); - for (Team team : teams) { - if (team.getUuid().equals(teamUuid)) { - hasTeam = true; - break; - } - } - if (!hasTeam) { - return Response.status(403).build(); - } - - } - if (jsonProject.getInitialTeam() != null) { - final Team team = qm.getObjectByUuid(Team.class, teamUuid); - project.addAccessTeam(team); + } + if (!qm.doesProjectExist(StringUtils.trimToNull(jsonProject.getName()), StringUtils.trimToNull(jsonProject.getVersion()))) { + final Project project; + try { + project = qm.createProject(jsonProject, jsonProject.getTags(), true); + } catch (IllegalArgumentException e){ + LOGGER.debug(e.getMessage()); + return Response.status(Response.Status.CONFLICT).entity("An inactive Parent cannot be selected as parent").build(); } - Principal principal = getPrincipal(); qm.updateNewProjectACL(project, principal); LOGGER.info("Project " + project.toString() + " created by " + super.getPrincipal().getName()); return Response.status(Response.Status.CREATED).entity(project).build(); } else { - return Response.status(Response.Status.CONFLICT) - .entity("A project with the specified name already exists.") - .build(); + return Response.status(Response.Status.CONFLICT).entity("A project with the specified name already exists.").build(); } } } @@ -338,9 +363,16 @@ public Response createProject(Project jsonProject) { @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Updates a project", description = "

        Requires permission PORTFOLIO_MANAGEMENT

        ") + @Operation( + summary = "Updates a project", + description = "

        Requires permission PORTFOLIO_MANAGEMENT

        " + ) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "The updated project", content = @Content(schema = @Schema(implementation = Project.class))), + @ApiResponse( + responseCode = "200", + description = "The updated project", + content = @Content(schema = @Schema(implementation = Project.class)) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found"), @ApiResponse(responseCode = "409", description = """ @@ -364,7 +396,8 @@ public Response updateProject(Project jsonProject) { validator.validateProperty(jsonProject, "classifier"), validator.validateProperty(jsonProject, "cpe"), validator.validateProperty(jsonProject, "purl"), - validator.validateProperty(jsonProject, "swidTagId")); + validator.validateProperty(jsonProject, "swidTagId") + ); if (jsonProject.getClassifier() == null) { jsonProject.setClassifier(Classifier.APPLICATION); } @@ -372,8 +405,7 @@ public Response updateProject(Project jsonProject) { Project project = qm.getObjectByUuid(Project.class, jsonProject.getUuid()); if (project != null) { if (!qm.hasAccess(super.getPrincipal(), project)) { - return Response.status(Response.Status.FORBIDDEN) - .entity("Access to the specified project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } final String name = StringUtils.trimToNull(jsonProject.getName()); final String version = StringUtils.trimToNull(jsonProject.getVersion()); @@ -385,19 +417,17 @@ public Response updateProject(Project jsonProject) { } try { project = qm.updateProject(jsonProject, true); - } catch (IllegalArgumentException e) { + } catch (IllegalArgumentException e){ LOGGER.debug(e.getMessage()); return Response.status(Response.Status.CONFLICT).entity(e.getMessage()).build(); } LOGGER.info("Project " + project.toString() + " updated by " + super.getPrincipal().getName()); return Response.ok(project).build(); } else { - return Response.status(Response.Status.CONFLICT) - .entity("A project with the specified name and version already exists.").build(); + return Response.status(Response.Status.CONFLICT).entity("A project with the specified name and version already exists.").build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") - .build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); } } } @@ -406,9 +436,16 @@ public Response updateProject(Project jsonProject) { @Path("/{uuid}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Partially updates a project", description = "

        Requires permission PORTFOLIO_MANAGEMENT

        ") + @Operation( + summary = "Partially updates a project", + description = "

        Requires permission PORTFOLIO_MANAGEMENT

        " + ) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "The updated project", content = @Content(schema = @Schema(implementation = Project.class))), + @ApiResponse( + responseCode = "200", + description = "The updated project", + content = @Content(schema = @Schema(implementation = Project.class)) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found"), @ApiResponse(responseCode = "409", description = """ @@ -421,7 +458,8 @@ public Response updateProject(Project jsonProject) { }) @PermissionRequired(Permissions.Constants.PORTFOLIO_MANAGEMENT) public Response patchProject( - @Parameter(description = "The UUID of the project to modify", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid, + @Parameter(description = "The UUID of the project to modify", schema = @Schema(type = "string", format = "uuid"), required = true) + @PathParam("uuid") @ValidUuid String uuid, Project jsonProject) { final Validator validator = getValidator(); failOnValidationError( @@ -435,24 +473,22 @@ public Response patchProject( validator.validateProperty(jsonProject, "classifier"), validator.validateProperty(jsonProject, "cpe"), validator.validateProperty(jsonProject, "purl"), - validator.validateProperty(jsonProject, "swidTagId")); + validator.validateProperty(jsonProject, "swidTagId") + ); try (QueryManager qm = new QueryManager()) { Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { if (!qm.hasAccess(super.getPrincipal(), project)) { - return Response.status(Response.Status.FORBIDDEN) - .entity("Access to the specified project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } var modified = false; project = qm.detachWithGroups(project, List.of(FetchGroup.DEFAULT, Project.FetchGroup.PARENT.name())); modified |= setIfDifferent(jsonProject, project, Project::getName, Project::setName); modified |= setIfDifferent(jsonProject, project, Project::getVersion, Project::setVersion); - // if either name or version has been changed, verify that this new combination - // does not already exist + // if either name or version has been changed, verify that this new combination does not already exist if (modified && qm.doesProjectExist(project.getName(), project.getVersion())) { - return Response.status(Response.Status.CONFLICT) - .entity("A project with the specified name and version already exists.").build(); + return Response.status(Response.Status.CONFLICT).entity("A project with the specified name and version already exists.").build(); } modified |= setIfDifferent(jsonProject, project, Project::getAuthors, Project::setAuthors); modified |= setIfDifferent(jsonProject, project, Project::getPublisher, Project::setPublisher); @@ -468,12 +504,10 @@ public Response patchProject( if (jsonProject.getParent() != null && jsonProject.getParent().getUuid() != null) { final Project parent = qm.getObjectByUuid(Project.class, jsonProject.getParent().getUuid()); if (parent == null) { - return Response.status(Response.Status.NOT_FOUND) - .entity("The UUID of the parent project could not be found.").build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the parent project could not be found.").build(); } if (!qm.hasAccess(getPrincipal(), parent)) { - return Response.status(Response.Status.FORBIDDEN) - .entity("Access to the specified parent project is forbidden").build(); + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified parent project is forbidden").build(); } modified |= project.getParent() == null || !parent.getUuid().equals(project.getParent().getUuid()); project.setParent(parent); @@ -483,13 +517,13 @@ public Response patchProject( project.setTags(jsonProject.getTags()); } if (isCollectionModified(jsonProject.getExternalReferences(), project.getExternalReferences())) { - modified = true; - project.setExternalReferences(jsonProject.getExternalReferences()); + modified = true; + project.setExternalReferences(jsonProject.getExternalReferences()); } if (modified) { try { project = qm.updateProject(project, true); - } catch (IllegalArgumentException e) { + } catch (IllegalArgumentException e){ LOGGER.debug(e.getMessage()); return Response.status(Response.Status.CONFLICT).entity(e.getMessage()).build(); } @@ -499,18 +533,16 @@ public Response patchProject( return Response.notModified().build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") - .build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); } } } /** - * returns `true` if the given [updated] collection should be considered an - * update of the [original] collection. + * returns `true` if the given [updated] collection should be considered an update of the [original] collection. */ private static boolean isCollectionModified(Collection updated, Collection original) { - return updated != null && (!Collections.isEmpty(updated) || !Collections.isEmpty(original)); + return updated != null && (!Collections.isEmpty(updated) || !Collections.isEmpty(original)); } /** @@ -519,17 +551,16 @@ private static boolean isCollectionModified(Collection updated, Collectio * only if the new value is not {@code null} and it is not * {@link Object#equals(java.lang.Object) equal to} the old value. * - * @param the type of the old and new value + * @param the type of the old and new value * @param source the source object that contains the new value * @param target the target object that should be updated * @param getter the method to retrieve the new value from {@code source} - * and the old value from {@code target} + * and the old value from {@code target} * @param setter the method to set the new value on {@code target} * @return {@code true} if {@code target} has been changed, else - * {@code false} + * {@code false} */ - private boolean setIfDifferent(final Project source, final Project target, final Function getter, - final BiConsumer setter) { + private boolean setIfDifferent(final Project source, final Project target, final Function getter, final BiConsumer setter) { final T newValue = getter.apply(source); if (newValue != null && !newValue.equals(getter.apply(target))) { setter.accept(target, newValue); @@ -543,7 +574,10 @@ private boolean setIfDifferent(final Project source, final Project target, f @Path("/{uuid}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Deletes a project", description = "

        Requires permission PORTFOLIO_MANAGEMENT

        ") + @Operation( + summary = "Deletes a project", + description = "

        Requires permission PORTFOLIO_MANAGEMENT

        " + ) @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "Project removed successfully"), @ApiResponse(responseCode = "401", description = "Unauthorized"), @@ -552,7 +586,8 @@ private boolean setIfDifferent(final Project source, final Project target, f }) @PermissionRequired(Permissions.Constants.PORTFOLIO_MANAGEMENT) public Response deleteProject( - @Parameter(description = "The UUID of the project to delete", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { + @Parameter(description = "The UUID of the project to delete", schema = @Schema(type = "string", format = "uuid"), required = true) + @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { final Project project = qm.getObjectByUuid(Project.class, uuid, Project.FetchGroup.ALL.name()); if (project != null) { @@ -561,13 +596,10 @@ public Response deleteProject( qm.recursivelyDelete(project, true); return Response.status(Response.Status.NO_CONTENT).build(); } else { - return Response.status(Response.Status.FORBIDDEN) - .entity("Access to the specified project is forbidden") - .build(); + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") - .build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); } } } @@ -576,9 +608,16 @@ public Response deleteProject( @Path("/clone") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Clones a project", description = "

        Requires permission PORTFOLIO_MANAGEMENT

        ") + @Operation( + summary = "Clones a project", + description = "

        Requires permission PORTFOLIO_MANAGEMENT

        " + ) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Token to be used for checking cloning progress", content = @Content(schema = @Schema(implementation = BomUploadResponse.class))), + @ApiResponse( + responseCode = "200", + description = "Token to be used for checking cloning progress", + content = @Content(schema = @Schema(implementation = BomUploadResponse.class)) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found") }) @@ -587,19 +626,16 @@ public Response cloneProject(CloneProjectRequest jsonRequest) { final Validator validator = super.getValidator(); failOnValidationError( validator.validateProperty(jsonRequest, "project"), - validator.validateProperty(jsonRequest, "version")); + validator.validateProperty(jsonRequest, "version") + ); try (QueryManager qm = new QueryManager()) { - final Project sourceProject = qm.getObjectByUuid(Project.class, jsonRequest.getProject(), - Project.FetchGroup.ALL.name()); + final Project sourceProject = qm.getObjectByUuid(Project.class, jsonRequest.getProject(), Project.FetchGroup.ALL.name()); if (sourceProject != null) { if (!qm.hasAccess(super.getPrincipal(), sourceProject)) { - return Response.status(Response.Status.FORBIDDEN) - .entity("Access to the specified project is forbidden") - .build(); + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } if (qm.doesProjectExist(sourceProject.getName(), StringUtils.trimToNull(jsonRequest.getVersion()))) { - return Response.status(Response.Status.CONFLICT) - .entity("A project with the specified name and version already exists.").build(); + return Response.status(Response.Status.CONFLICT).entity("A project with the specified name and version already exists.").build(); } LOGGER.info("Project " + sourceProject + " is being cloned by " + super.getPrincipal().getName()); @@ -607,27 +643,36 @@ public Response cloneProject(CloneProjectRequest jsonRequest) { Event.dispatch(event); return Response.ok(java.util.Collections.singletonMap("token", event.getChainIdentifier())).build(); } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") - .build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); } } } + @GET @Path("/{uuid}/children") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns a list of all children for a project", description = "

        Requires permission VIEW_PORTFOLIO

        ") + @Operation( + summary = "Returns a list of all children for a project", + description = "

        Requires permission VIEW_PORTFOLIO

        " + ) @PaginatedApi @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "A list of all children for a project", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), + @ApiResponse( + responseCode = "200", + description = "A list of all children for a project", + headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified project is forbidden"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) - public Response getChildrenProjects( - @Parameter(description = "The UUID of the project to get the children from", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive) { + public Response getChildrenProjects(@Parameter(description = "The UUID of the project to get the children from", schema = @Schema(type = "string", format = "uuid"), required = true) + @PathParam("uuid") @ValidUuid String uuid, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) + @QueryParam("excludeInactive") boolean excludeInactive) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { @@ -635,13 +680,10 @@ public Response getChildrenProjects( if (qm.hasAccess(super.getPrincipal(), project)) { return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); } else { - return Response.status(Response.Status.FORBIDDEN) - .entity("Access to the specified project is forbidden") - .build(); + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") - .build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); } } } @@ -649,35 +691,42 @@ public Response getChildrenProjects( @GET @Path("/{uuid}/children/classifier/{classifier}") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns a list of all children for a project by classifier", description = "

        Requires permission VIEW_PORTFOLIO

        ") + @Operation( + summary = "Returns a list of all children for a project by classifier", + description = "

        Requires permission VIEW_PORTFOLIO

        " + ) @PaginatedApi @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "A list of all children for a project by classifier", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), + @ApiResponse( + responseCode = "200", + description = "A list of all children for a project by classifier", + headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified project is forbidden"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getChildrenProjectsByClassifier( - @Parameter(description = "The classifier to query on", required = true) @PathParam("classifier") String classifierString, - @Parameter(description = "The UUID of the project to get the children from", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive) { + @Parameter(description = "The classifier to query on", required = true) + @PathParam("classifier") String classifierString, + @Parameter(description = "The UUID of the project to get the children from", schema = @Schema(type = "string", format = "uuid"), required = true) + @PathParam("uuid") @ValidUuid String uuid, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) + @QueryParam("excludeInactive") boolean excludeInactive) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { final Classifier classifier = Classifier.valueOf(classifierString); - final PaginatedResult result = qm.getChildrenProjects(classifier, project.getUuid(), true, - excludeInactive); + final PaginatedResult result = qm.getChildrenProjects(classifier, project.getUuid(), true, excludeInactive); if (qm.hasAccess(super.getPrincipal(), project)) { return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); } else { - return Response.status(Response.Status.FORBIDDEN) - .entity("Access to the specified project is forbidden") - .build(); + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") - .build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); } } } @@ -685,19 +734,30 @@ public Response getChildrenProjectsByClassifier( @GET @Path("/{uuid}/children/tag/{tag}") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns a list of all children for a project by tag", description = "

        Requires permission VIEW_PORTFOLIO

        ") + @Operation( + summary = "Returns a list of all children for a project by tag", + description = "

        Requires permission VIEW_PORTFOLIO

        " + ) @PaginatedApi @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "A list of all children for a project by tag", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), + @ApiResponse( + responseCode = "200", + description = "A list of all children for a project by tag", + headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified project is forbidden"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getChildrenProjectsByTag( - @Parameter(description = "The tag to query on", required = true) @PathParam("tag") String tagString, - @Parameter(description = "The UUID of the project to get the children from", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive) { + @Parameter(description = "The tag to query on", required = true) + @PathParam("tag") String tagString, + @Parameter(description = "The UUID of the project to get the children from", schema = @Schema(type = "string", format = "uuid"), required = true) + @PathParam("uuid") @ValidUuid String uuid, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) + @QueryParam("excludeInactive") boolean excludeInactive) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { @@ -706,13 +766,10 @@ public Response getChildrenProjectsByTag( if (qm.hasAccess(super.getPrincipal(), project)) { return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); } else { - return Response.status(Response.Status.FORBIDDEN) - .entity("Access to the specified project is forbidden") - .build(); + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") - .build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); } } } @@ -720,35 +777,41 @@ public Response getChildrenProjectsByTag( @GET @Path("/withoutDescendantsOf/{uuid}") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns a list of all projects without the descendants of the selected project", description = "

        Requires permission VIEW_PORTFOLIO

        ") + @Operation( + summary = "Returns a list of all projects without the descendants of the selected project", + description = "

        Requires permission VIEW_PORTFOLIO

        " + ) @PaginatedApi @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "A list of all projects without the descendants of the selected project", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class)))), + @ApiResponse( + responseCode = "200", + description = "A list of all projects without the descendants of the selected project", + headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of projects", schema = @Schema(format = "integer")), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = Project.class))) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "403", description = "Access to the specified project is forbidden"), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) public Response getProjectsWithoutDescendantsOf( - @Parameter(description = "The UUID of the project which descendants will be excluded", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid, - @Parameter(description = "The optional name of the project to query on", required = false) @QueryParam("name") String name, - @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) @QueryParam("excludeInactive") boolean excludeInactive) { + @Parameter(description = "The UUID of the project which descendants will be excluded", schema = @Schema(type = "string", format = "uuid"), required = true) + @PathParam("uuid") @ValidUuid String uuid, + @Parameter(description = "The optional name of the project to query on", required = false) + @QueryParam("name") String name, + @Parameter(description = "Optionally excludes inactive projects from being returned", required = false) + @QueryParam("excludeInactive") boolean excludeInactive) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { if (qm.hasAccess(super.getPrincipal(), project)) { - final PaginatedResult result = (name != null) - ? qm.getProjectsWithoutDescendantsOf(name, excludeInactive, project) - : qm.getProjectsWithoutDescendantsOf(excludeInactive, project); + final PaginatedResult result = (name != null) ? qm.getProjectsWithoutDescendantsOf(name, excludeInactive, project) : qm.getProjectsWithoutDescendantsOf(excludeInactive, project); return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); - } else { - return Response.status(Response.Status.FORBIDDEN) - .entity("Access to the specified project is forbidden") - .build(); + } else{ + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.") - .build(); + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); } } } diff --git a/src/main/java/org/dependencytrack/resources/v1/TeamResource.java b/src/main/java/org/dependencytrack/resources/v1/TeamResource.java index a065858ea4..23817c545c 100644 --- a/src/main/java/org/dependencytrack/resources/v1/TeamResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/TeamResource.java @@ -20,9 +20,7 @@ import alpine.Config; import alpine.common.logging.Logger; -import alpine.model.ConfigProperty; import alpine.model.ApiKey; -import alpine.model.Permission; import alpine.model.Team; import alpine.model.UserPrincipal; import alpine.server.auth.PermissionRequired; @@ -39,11 +37,11 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirements; import io.swagger.v3.oas.annotations.tags.Tag; import org.dependencytrack.auth.Permissions; +import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.validation.ValidUuid; -import org.dependencytrack.model.AvailableTeams; -import org.dependencytrack.model.LittleTeam; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.resources.v1.vo.TeamSelfResponse; +import org.dependencytrack.resources.v1.vo.VisibleTeams; import org.owasp.security.logging.SecurityMarkers; import jakarta.validation.Validator; @@ -57,7 +55,7 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; - +import java.security.Principal; import java.util.ArrayList; import java.util.List; @@ -81,9 +79,17 @@ public class TeamResource extends AlpineResource { @GET @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns a list of all teams", description = "

        Requires permission ACCESS_MANAGEMENT

        ") + @Operation( + summary = "Returns a list of all teams", + description = "

        Requires permission ACCESS_MANAGEMENT

        " + ) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "A list of all teams", headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of teams", schema = @Schema(format = "integer")), content = @Content(array = @ArraySchema(schema = @Schema(implementation = Team.class)))), + @ApiResponse( + responseCode = "200", + description = "A list of all teams", + headers = @Header(name = TOTAL_COUNT_HEADER, description = "The total number of teams", schema = @Schema(format = "integer")), + content = @Content(array = @ArraySchema(schema = @Schema(implementation = Team.class))) + ), @ApiResponse(responseCode = "401", description = "Unauthorized") }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) @@ -98,15 +104,23 @@ public Response getTeams() { @GET @Path("/{uuid}") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns a specific team", description = "

        Requires permission ACCESS_MANAGEMENT

        ") + @Operation( + summary = "Returns a specific team", + description = "

        Requires permission ACCESS_MANAGEMENT

        " + ) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "A specific team", content = @Content(schema = @Schema(implementation = Team.class))), + @ApiResponse( + responseCode = "200", + description = "A specific team", + content = @Content(schema = @Schema(implementation = Team.class)) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The team could not be found") }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) public Response getTeam( - @Parameter(description = "The UUID of the team to retrieve", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { + @Parameter(description = "The UUID of the team to retrieve", schema = @Schema(type = "string", format = "uuid"), required = true) + @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { final Team team = qm.getObjectByUuid(Team.class, uuid); if (team != null) { @@ -120,18 +134,26 @@ public Response getTeam( @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Creates a new team", description = "

        Requires permission ACCESS_MANAGEMENT

        ") + @Operation( + summary = "Creates a new team", + description = "

        Requires permission ACCESS_MANAGEMENT

        " + ) @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "The created team", content = @Content(schema = @Schema(implementation = Team.class))), + @ApiResponse( + responseCode = "201", + description = "The created team", + content = @Content(schema = @Schema(implementation = Team.class)) + ), @ApiResponse(responseCode = "401", description = "Unauthorized") }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) - // public Response createTeam(String jsonRequest) { + //public Response createTeam(String jsonRequest) { public Response createTeam(Team jsonTeam) { - // Team team = MapperUtil.readAsObjectOf(Team.class, jsonRequest); + //Team team = MapperUtil.readAsObjectOf(Team.class, jsonRequest); final Validator validator = super.getValidator(); failOnValidationError( - validator.validateProperty(jsonTeam, "name")); + validator.validateProperty(jsonTeam, "name") + ); try (QueryManager qm = new QueryManager()) { final Team team = qm.createTeam(jsonTeam.getName(), false); @@ -143,9 +165,16 @@ public Response createTeam(Team jsonTeam) { @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Updates a team's fields", description = "

        Requires permission ACCESS_MANAGEMENT

        ") + @Operation( + summary = "Updates a team's fields", + description = "

        Requires permission ACCESS_MANAGEMENT

        " + ) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "The updated team", content = @Content(schema = @Schema(implementation = Team.class))), + @ApiResponse( + responseCode = "200", + description = "The updated team", + content = @Content(schema = @Schema(implementation = Team.class)) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The team could not be found") }) @@ -153,12 +182,13 @@ public Response createTeam(Team jsonTeam) { public Response updateTeam(Team jsonTeam) { final Validator validator = super.getValidator(); failOnValidationError( - validator.validateProperty(jsonTeam, "name")); + validator.validateProperty(jsonTeam, "name") + ); try (QueryManager qm = new QueryManager()) { Team team = qm.getObjectByUuid(Team.class, jsonTeam.getUuid()); if (team != null) { team.setName(jsonTeam.getName()); - // todo: set permissions + //todo: set permissions team = qm.updateTeam(jsonTeam); super.logSecurityEvent(LOGGER, SecurityMarkers.SECURITY_AUDIT, "Team updated: " + team.getName()); return Response.ok(team).build(); @@ -171,7 +201,10 @@ public Response updateTeam(Team jsonTeam) { @DELETE @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Deletes a team", description = "

        Requires permission ACCESS_MANAGEMENT

        ") + @Operation( + summary = "Deletes a team", + description = "

        Requires permission ACCESS_MANAGEMENT

        " + ) @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "Team removed successfully"), @ApiResponse(responseCode = "401", description = "Unauthorized"), @@ -193,60 +226,28 @@ public Response deleteTeam(Team jsonTeam) { } @GET - @Path("/available-teams") + @Path("/visible") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns a list of Teams what are available as selection", description = "Requires permission PORTFOLIO_MANAGEMENT

        ") + @Operation(summary = "Returns a list of Teams what are visible", description = "Requires permission PORTFOLIO_MANAGEMENT

        ") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "The Available Teams", content = @Content(schema = @Schema(implementation = AvailableTeams.class))), + @ApiResponse(responseCode = "200", description = "The Visible Teams", content = @Content(schema = @Schema(implementation = VisibleTeams.class))), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "Teams could not be found") }) - @PermissionRequired(Permissions.Constants.PORTFOLIO_MANAGEMENT) public Response availableTeams() { - UserPrincipal user; - boolean isAllTeams = false; - boolean required = false; try (QueryManager qm = new QueryManager()) { - if (super.isLdapUser()) { - user = qm.getLdapUser(getPrincipal().getName()); - } else if (super.isManagedUser()) { - user = qm.getManagedUser(getPrincipal().getName()); - } else if (super.isOidcUser()) { - user = qm.getOidcUser(getPrincipal().getName()); - } else { - return Response.status(401).build(); - } - final List allTeams = qm.getTeams(); - final List configProperties = qm.getConfigProperties(); - for (final ConfigProperty configProperty : configProperties) { - // Replace the value of encrypted strings with the pre-defined placeholder - if (configProperty.getGroupName().equals("access-management") - && configProperty.getPropertyName().equals("acl.enabled")) { - required = configProperty.getPropertyValue().equals("true"); - break; - } + Principal user = getPrincipal(); + List userTeams = new ArrayList(); + if (user instanceof final UserPrincipal userPrincipal) { + userTeams = userPrincipal.getTeams(); + } else if (user instanceof final ApiKey apiKey) { + userTeams = apiKey.getTeams(); } - qm.getPersistenceManager().detachCopyAll(configProperties); - qm.close(); - List permissions = user.getPermissions(); - for (Permission permission : permissions) { - if (permission.getName().equals("ACCESS_MANAGEMENT")) { - isAllTeams = true; - break; - } - } - AvailableTeams response = new AvailableTeams(); - response.setRequired(required); - List availableTeams = new ArrayList(); - List teams = isAllTeams ? allTeams : user.getTeams(); - for (Team team : teams) { - LittleTeam newTeam = new LittleTeam(); - newTeam.setValue(team.getUuid()); - newTeam.setText(team.getName()); - availableTeams.add(newTeam); + boolean required = qm.isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED); + boolean isAllTeams = qm.hasAccessManagementPermission(user); + List teams = isAllTeams ? qm.getTeams() : userTeams; + VisibleTeams response = new VisibleTeams(required, teams); - } - response.setTeams(availableTeams); return Response.ok(response).build(); } } @@ -254,15 +255,23 @@ public Response availableTeams() { @PUT @Path("/{uuid}/key") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Generates an API key and returns its value", description = "

        Requires permission ACCESS_MANAGEMENT

        ") + @Operation( + summary = "Generates an API key and returns its value", + description = "

        Requires permission ACCESS_MANAGEMENT

        " + ) @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "The created API key", content = @Content(schema = @Schema(implementation = ApiKey.class))), + @ApiResponse( + responseCode = "201", + description = "The created API key", + content = @Content(schema = @Schema(implementation = ApiKey.class)) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The team could not be found") }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) public Response generateApiKey( - @Parameter(description = "The UUID of the team to generate a key for", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { + @Parameter(description = "The UUID of the team to generate a key for", schema = @Schema(type = "string", format = "uuid"), required = true) + @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { final Team team = qm.getObjectByUuid(Team.class, uuid); if (team != null) { @@ -277,15 +286,23 @@ public Response generateApiKey( @POST @Path("/key/{apikey}") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Regenerates an API key by removing the specified key, generating a new one and returning its value", description = "

        Requires permission ACCESS_MANAGEMENT

        ") + @Operation( + summary = "Regenerates an API key by removing the specified key, generating a new one and returning its value", + description = "

        Requires permission ACCESS_MANAGEMENT

        " + ) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "The re-generated API key", content = @Content(schema = @Schema(implementation = ApiKey.class))), + @ApiResponse( + responseCode = "200", + description = "The re-generated API key", + content = @Content(schema = @Schema(implementation = ApiKey.class)) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The API key could not be found") }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) public Response regenerateApiKey( - @Parameter(description = "The API key to regenerate", required = true) @PathParam("apikey") String apikey) { + @Parameter(description = "The API key to regenerate", required = true) + @PathParam("apikey") String apikey) { try (QueryManager qm = new QueryManager()) { ApiKey apiKey = qm.getApiKey(apikey); if (apiKey != null) { @@ -301,15 +318,22 @@ public Response regenerateApiKey( @Path("/key/{key}/comment") @Consumes(MediaType.TEXT_PLAIN) @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Updates an API key's comment", description = "

        Requires permission ACCESS_MANAGEMENT

        ") + @Operation( + summary = "Updates an API key's comment", + description = "

        Requires permission ACCESS_MANAGEMENT

        " + ) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "The updated API key", content = @Content(schema = @Schema(implementation = ApiKey.class))), + @ApiResponse( + responseCode = "200", + description = "The updated API key", + content = @Content(schema = @Schema(implementation = ApiKey.class)) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The API key could not be found") }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) public Response updateApiKeyComment(@PathParam("key") final String key, - final String comment) { + final String comment) { try (final var qm = new QueryManager()) { qm.getPersistenceManager().setProperty(PROPERTY_RETAIN_VALUES, "true"); @@ -330,7 +354,10 @@ public Response updateApiKeyComment(@PathParam("key") final String key, @DELETE @Path("/key/{apikey}") - @Operation(summary = "Deletes the specified API key", description = "

        Requires permission ACCESS_MANAGEMENT

        ") + @Operation( + summary = "Deletes the specified API key", + description = "

        Requires permission ACCESS_MANAGEMENT

        " + ) @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "API key removed successfully"), @ApiResponse(responseCode = "401", description = "Unauthorized"), @@ -338,7 +365,8 @@ public Response updateApiKeyComment(@PathParam("key") final String key, }) @PermissionRequired(Permissions.Constants.ACCESS_MANAGEMENT) public Response deleteApiKey( - @Parameter(description = "The API key to delete", required = true) @PathParam("apikey") String apikey) { + @Parameter(description = "The API key to delete", required = true) + @PathParam("apikey") String apikey) { try (QueryManager qm = new QueryManager()) { final ApiKey apiKey = qm.getApiKey(apikey); if (apiKey != null) { @@ -353,9 +381,14 @@ public Response deleteApiKey( @GET @Path("self") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns information about the current team.") + @Operation( + summary = "Returns information about the current team.") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Information about the current team", content = @Content(schema = @Schema(implementation = TeamSelfResponse.class))), + @ApiResponse( + responseCode = "200", + description = "Information about the current team", + content = @Content(schema = @Schema(implementation = TeamSelfResponse.class)) + ), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "400", description = "Invalid API key supplied"), @ApiResponse(responseCode = "404", description = "No Team for the given API key found") @@ -364,21 +397,19 @@ public Response getSelf() { if (Config.getInstance().getPropertyAsBoolean(Config.AlpineKey.ENFORCE_AUTHENTICATION)) { try (var qm = new QueryManager()) { if (isApiKey()) { - final var apiKey = qm.getApiKey(((ApiKey) getPrincipal()).getKey()); + final var apiKey = qm.getApiKey(((ApiKey)getPrincipal()).getKey()); final var team = apiKey.getTeams().stream().findFirst(); if (team.isPresent()) { return Response.ok(new TeamSelfResponse(team.get())).build(); } else { - return Response.status(Response.Status.NOT_FOUND).entity("No Team for the given API key found.") - .build(); + return Response.status(Response.Status.NOT_FOUND).entity("No Team for the given API key found.").build(); } } else { return Response.status(Response.Status.BAD_REQUEST).entity("Invalid API key supplied.").build(); } } } - // Authentication is not enabled, but we need to return a positive response - // without any principal data. + // Authentication is not enabled, but we need to return a positive response without any principal data. return Response.ok().build(); } } diff --git a/src/main/java/org/dependencytrack/resources/v1/vo/VisibleTeams.java b/src/main/java/org/dependencytrack/resources/v1/vo/VisibleTeams.java new file mode 100644 index 0000000000..ba9f186262 --- /dev/null +++ b/src/main/java/org/dependencytrack/resources/v1/vo/VisibleTeams.java @@ -0,0 +1,27 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.resources.v1.vo; + +import java.util.List; + +import alpine.model.Team; + +public record VisibleTeams(boolean required, + List teams) { +} From d5e095605d915637777123cfa64cf1b828a1628b Mon Sep 17 00:00:00 2001 From: Thomas Schauer-Koeckeis Date: Tue, 27 Aug 2024 13:00:00 +0200 Subject: [PATCH 123/429] Now able to add accessTeam to the API Query and handles right Signed-off-by: Thomas Schauer-Koeckeis --- .../org/dependencytrack/model/Project.java | 1 - .../resources/v1/ProjectResource.java | 21 ++++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/Project.java b/src/main/java/org/dependencytrack/model/Project.java index 6fc9dae62d..df8c5873b4 100644 --- a/src/main/java/org/dependencytrack/model/Project.java +++ b/src/main/java/org/dependencytrack/model/Project.java @@ -273,7 +273,6 @@ public enum FetchGroup { @Join(column = "PROJECT_ID") @Element(column = "TEAM_ID") @Order(extensions = @Extension(vendorName = "datanucleus", key = "list-ordering", value = "name ASC")) - //@JsonIgnore private List accessTeams; @Persistent(defaultFetchGroup = "true") diff --git a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java index 31c84c83e4..e51db7ffbb 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java @@ -326,22 +326,19 @@ public Response createProject(Project jsonProject) { boolean required = qm.isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED); boolean isAdmin = qm.hasAccessManagementPermission(principal); if (required && choosenTeams.size() == 0) { - return Response.status(422).build(); + return Response.status(422) + .entity("You need to specify at least one team to which the project should belong").build(); } List visibleTeams = isAdmin ? qm.getTeams() : userTeams; - boolean hasTeam; + jsonProject.setAccessTeams(new ArrayList()); for (Team choosenTeam : choosenTeams) { - hasTeam = false; - LOGGER.info(Boolean.toString(visibleTeams.contains(choosenTeam)) + choosenTeam.getName()); - for (Team team : visibleTeams) { - if (team.getUuid().equals(choosenTeam.getUuid())) { - hasTeam = true; - break; - } - } - if (!hasTeam) { - return Response.status(403).build(); + Team ormTeam = qm.getObjectByUuid(Team.class, choosenTeam.getUuid()); + if (!visibleTeams.contains(ormTeam)) { + return isAdmin ? Response.status(404).entity("This team does not exist!").build() + : Response.status(403) + .entity("You don't have the permission to assign this team to a project.").build(); } + jsonProject.addAccessTeam(ormTeam); } if (!qm.doesProjectExist(StringUtils.trimToNull(jsonProject.getName()), StringUtils.trimToNull(jsonProject.getVersion()))) { final Project project; From 13ee8f4341a4b09d1f00840345fe6277e9f3922e Mon Sep 17 00:00:00 2001 From: Thomas Schauer-Koeckeis Date: Tue, 27 Aug 2024 13:12:30 +0200 Subject: [PATCH 124/429] Deleted unused files Signed-off-by: Thomas Schauer-Koeckeis --- .../dependencytrack/model/AvailableTeams.java | 56 ------------------- .../org/dependencytrack/model/LittleTeam.java | 53 ------------------ 2 files changed, 109 deletions(-) delete mode 100644 src/main/java/org/dependencytrack/model/AvailableTeams.java delete mode 100644 src/main/java/org/dependencytrack/model/LittleTeam.java diff --git a/src/main/java/org/dependencytrack/model/AvailableTeams.java b/src/main/java/org/dependencytrack/model/AvailableTeams.java deleted file mode 100644 index 1a4522166f..0000000000 --- a/src/main/java/org/dependencytrack/model/AvailableTeams.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of Dependency-Track. - * - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright (c) OWASP Foundation. All Rights Reserved. - */ -package org.dependencytrack.model; - -import java.io.Serializable; -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonInclude; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class AvailableTeams implements Serializable { - private boolean required; - private List teams; - - public boolean isRequired() { - return required; - } - - public void setRequired(final boolean required) { - this.required = required; - } - - public List getTeams() { - return teams; - } - - public void setTeams(final List teams) { - this.teams = teams; - } - - @Override - public String toString() { - List strlistTeams = teams.stream() - .map(Object::toString) - .toList(); - String strTeams = String.join(",", strlistTeams); - return String.format("required: %s, teams: [ %s ]", required, strTeams); - } - -} diff --git a/src/main/java/org/dependencytrack/model/LittleTeam.java b/src/main/java/org/dependencytrack/model/LittleTeam.java deleted file mode 100644 index 5355b5a0e0..0000000000 --- a/src/main/java/org/dependencytrack/model/LittleTeam.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of Dependency-Track. - * - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright (c) OWASP Foundation. All Rights Reserved. - */ -package org.dependencytrack.model; - -import java.io.Serializable; - -import java.util.UUID; - -import com.fasterxml.jackson.annotation.JsonInclude; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class LittleTeam implements Serializable { - - private UUID value; - private String text; - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } - - public UUID getValue() { - return value; - } - - public void setValue(UUID value) { - this.value = value; - } - - @Override - public String toString() { - return String.format("{value: %s, text: %s}", value.toString(), text); - } -} From 639a4a58c394b40114afa9e32da574d7ac626151 Mon Sep 17 00:00:00 2001 From: Thomas Schauer-Koeckeis Date: Tue, 27 Aug 2024 13:14:35 +0200 Subject: [PATCH 125/429] Removed Debug info Signed-off-by: Thomas Schauer-Koeckeis --- .../java/org/dependencytrack/resources/v1/ProjectResource.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java index e51db7ffbb..5edaf6bea5 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java @@ -315,7 +315,6 @@ public Response createProject(Project jsonProject) { jsonProject.setParent(parent); } final List choosenTeams = jsonProject.getAccessTeams(); - LOGGER.info(choosenTeams.toString()); Principal principal = getPrincipal(); List userTeams = new ArrayList(); if (principal instanceof final UserPrincipal userPrincipal) { From 78aa74b987372351eebdb3d8f8e692adc2323b6c Mon Sep 17 00:00:00 2001 From: Thomas Schauer-Koeckeis Date: Tue, 27 Aug 2024 16:56:02 +0200 Subject: [PATCH 126/429] Fixed most tests Signed-off-by: Thomas Schauer-Koeckeis --- .../resources/v1/ProjectResourceTest.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java index acce124c10..adeabf8579 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java @@ -21,6 +21,7 @@ import alpine.common.util.UuidUtil; import alpine.event.framework.EventService; import alpine.model.IConfigProperty.PropertyType; +import alpine.model.Team; import alpine.server.filters.ApiFilter; import alpine.server.filters.AuthenticationFilter; import jakarta.json.Json; @@ -284,6 +285,7 @@ public void getProjectByUuidTest() { .withMatcher("childUuid", equalTo(childProject.getUuid().toString())) .isEqualTo(""" { + "accessTeams": [], "name": "acme-app", "version": "1.0.0", "uuid": "${json-unit.matches:projectUuid}", @@ -413,6 +415,7 @@ public void getProjectByUnknownTagTest() { @Test public void createProjectTest(){ Project project = new Project(); + project.setAccessTeams(new ArrayList()); project.setName("Acme Example"); project.setVersion("1.0"); project.setDescription("Test project"); @@ -433,6 +436,7 @@ public void createProjectTest(){ @Test public void createProjectDuplicateTest() { Project project = new Project(); + project.setAccessTeams(new ArrayList()); project.setName("Acme Example"); project.setVersion("1.0"); Response response = jersey.target(V1_PROJECT) @@ -452,6 +456,7 @@ public void createProjectDuplicateTest() { @Test public void createProjectWithoutVersionDuplicateTest() { Project project = new Project(); + project.setAccessTeams(new ArrayList()); project.setName("Acme Example"); Response response = jersey.target(V1_PROJECT) .request() @@ -708,6 +713,7 @@ public void patchProjectSuccessfullyPatchedTest() { .withMatcher("projectUuid", equalTo(p1.getUuid().toString())) .isEqualTo(""" { + "accessTeams": [], "publisher": "new publisher", "manufacturer": { "name": "manufacturerName", @@ -804,6 +810,7 @@ public void patchProjectParentTest() { .withMatcher("parentProjectUuid", CoreMatchers.equalTo(newParent.getUuid().toString())) .isEqualTo(""" { + "accessTeams": [], "name": "DEF", "version": "2.0", "uuid": "${json-unit.matches:projectUuid}", @@ -1162,6 +1169,7 @@ public void issue3883RegressionTest() { .header(X_API_KEY, apiKey) .put(Entity.json(""" { + "accessTeams": [], "name": "acme-app-parent", "version": "1.0.0" } @@ -1174,6 +1182,7 @@ public void issue3883RegressionTest() { .header(X_API_KEY, apiKey) .put(Entity.json(""" { + "accessTeams": [], "name": "acme-app", "version": "1.0.0", "parent": { @@ -1191,6 +1200,7 @@ public void issue3883RegressionTest() { assertThat(response.getStatus()).isEqualTo(200); assertThatJson(getPlainTextBody(response)).isEqualTo(""" { + "accessTeams": [], "name": "acme-app-parent", "version": "1.0.0", "classifier": "APPLICATION", @@ -1224,6 +1234,7 @@ public void issue3883RegressionTest() { assertThat(response.getStatus()).isEqualTo(200); assertThatJson(getPlainTextBody(response)).isEqualTo(""" { + "accessTeams": [], "name": "acme-app", "version": "1.0.0", "classifier": "APPLICATION", @@ -1264,7 +1275,8 @@ public void issue4048RegressionTest() { final JsonObjectBuilder requestBodyBuilder = Json.createObjectBuilder() .add("name", "project-%d-%d".formatted(i, j)) - .add("version", "%d.%d".formatted(i, j)); + .add("version", "%d.%d".formatted(i, j)) + .add("accessTeams", "[]"); if (parentUuid != null) { requestBodyBuilder.add("parent", Json.createObjectBuilder() .add("uuid", parentUuid.toString())); From b24d6f90820cba80edcca40d98f7bc0e00998db5 Mon Sep 17 00:00:00 2001 From: Thomas Schauer-Koeckeis Date: Wed, 28 Aug 2024 11:20:51 +0200 Subject: [PATCH 127/429] Fixed last failing test Signed-off-by: Thomas Schauer-Koeckeis --- .../org/dependencytrack/resources/v1/ProjectResourceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java index adeabf8579..e3f5c7ea8d 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java @@ -1276,7 +1276,7 @@ public void issue4048RegressionTest() { final JsonObjectBuilder requestBodyBuilder = Json.createObjectBuilder() .add("name", "project-%d-%d".formatted(i, j)) .add("version", "%d.%d".formatted(i, j)) - .add("accessTeams", "[]"); + .add("accessTeams",Json.createArrayBuilder().build()); if (parentUuid != null) { requestBodyBuilder.add("parent", Json.createObjectBuilder() .add("uuid", parentUuid.toString())); From e663e7e70969f206a288ccd441a663d5c5a9e0f2 Mon Sep 17 00:00:00 2001 From: Thomas Schauer-Koeckeis Date: Thu, 29 Aug 2024 11:10:26 +0200 Subject: [PATCH 128/429] Fixed Tests and bug for no Admin Users Signed-off-by: Thomas Schauer-Koeckeis --- .../resources/v1/ProjectResource.java | 8 +- .../resources/v1/ProjectResourceTest.java | 120 ++++++++++++++++++ 2 files changed, 125 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java index 5edaf6bea5..b85446aee7 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java @@ -69,6 +69,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.UUID; import java.util.function.BiConsumer; import java.util.function.Function; @@ -324,19 +325,20 @@ public Response createProject(Project jsonProject) { } boolean required = qm.isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED); boolean isAdmin = qm.hasAccessManagementPermission(principal); - if (required && choosenTeams.size() == 0) { + if (required && choosenTeams.isEmpty()) { return Response.status(422) .entity("You need to specify at least one team to which the project should belong").build(); } List visibleTeams = isAdmin ? qm.getTeams() : userTeams; + List visibleUuids = visibleTeams.isEmpty() ? new ArrayList(): visibleTeams.stream().map(Team::getUuid).toList(); jsonProject.setAccessTeams(new ArrayList()); for (Team choosenTeam : choosenTeams) { - Team ormTeam = qm.getObjectByUuid(Team.class, choosenTeam.getUuid()); - if (!visibleTeams.contains(ormTeam)) { + if (!visibleUuids.contains(choosenTeam.getUuid())) { return isAdmin ? Response.status(404).entity("This team does not exist!").build() : Response.status(403) .entity("You don't have the permission to assign this team to a project.").build(); } + Team ormTeam = qm.getObjectByUuid(Team.class, choosenTeam.getUuid()); jsonProject.addAccessTeam(ormTeam); } if (!qm.doesProjectExist(StringUtils.trimToNull(jsonProject.getName()), StringUtils.trimToNull(jsonProject.getVersion()))) { diff --git a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java index e3f5c7ea8d..1418ab36e1 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java @@ -20,8 +20,12 @@ import alpine.common.util.UuidUtil; import alpine.event.framework.EventService; +import alpine.model.IConfigProperty; +import alpine.model.ManagedUser; import alpine.model.IConfigProperty.PropertyType; import alpine.model.Team; +import alpine.model.Permission; +import alpine.server.auth.JsonWebToken; import alpine.server.filters.ApiFilter; import alpine.server.filters.AuthenticationFilter; import jakarta.json.Json; @@ -51,6 +55,7 @@ import org.dependencytrack.model.ServiceComponent; import org.dependencytrack.model.Tag; import org.dependencytrack.model.Vulnerability; +import org.dependencytrack.persistence.DefaultObjectGenerator; import org.dependencytrack.tasks.CloneProjectTask; import org.dependencytrack.tasks.scanners.AnalyzerIdentity; import org.glassfish.jersey.client.HttpUrlConnectorProvider; @@ -76,6 +81,8 @@ import static org.hamcrest.Matchers.equalTo; public class ProjectResourceTest extends ResourceTest { + private ManagedUser testUser; + private String jwt; @ClassRule public static JerseyTestRule jersey = new JerseyTestRule( @@ -90,6 +97,23 @@ public void after() throws Exception { super.after(); } + public void getUserToken(boolean isAdmin) { + testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); + jwt = new JsonWebToken().createToken(testUser); + qm.addUserToTeam(testUser, team); + final var generator = new DefaultObjectGenerator(); + generator.loadDefaultPermissions(); + List permissionsList = new ArrayList(); + final Permission permission = qm.getPermission("PORTFOLIO_MANAGEMENT"); + permissionsList.add(permission); + testUser.setPermissions(permissionsList); + if (isAdmin) { + final Permission adminPermission = qm.getPermission("ACCESS_MANAGEMENT"); + permissionsList.add(adminPermission); + testUser.setPermissions(permissionsList); + } + } + @Test public void getProjectsDefaultRequestTest() { for (int i=0; i<1000; i++) { @@ -483,6 +507,102 @@ public void createProjectEmptyTest() { Assert.assertEquals(400, response.getStatus(), 0); } + @Test + public void createProjectWithExistingTeamRequiredTest() { + getUserToken(false); + Team AllowedTeam = qm.createTeam("AllowedTeam", false); + Project project = new Project(); + project.setName("ProjectWithExistingTeamRequired"); + qm.addUserToTeam(testUser, AllowedTeam); + qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); + final JsonObject jsonTeam = Json.createObjectBuilder().add("uuid", AllowedTeam.getUuid().toString()).build(); + final JsonObjectBuilder requestBodyBuilder = Json.createObjectBuilder() + .add("name", project.getName()).add("classifier", "CONTAINER").addNull("parent").add("active", true) + .add("accessTeams", Json.createArrayBuilder().add(jsonTeam).build()); + Response response = jersey.target(V1_PROJECT) + .request() + .header("Authorization", "Bearer " + jwt) + .put(Entity.json(requestBodyBuilder.build().toString())); + Assert.assertEquals(201, response.getStatus(), 0); + } + + @Test + public void createProjectWithoutExistingTeamRequiredTest() { + getUserToken(false); + Project project = new Project(); + project.setName("ProjectWithoutExistingTeamRequired"); + project.setAccessTeams(new ArrayList()); + qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); + Response response = jersey.target(V1_PROJECT) + .request() + .header("Authorization", "Bearer " + jwt) + .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + Assert.assertEquals(422, response.getStatus(), 0); + } + + @Test + public void createProjectWithNotAllowedExistingTeamTest() { + getUserToken(false); + Team notAllowedTeam = qm.createTeam("NotAllowedTeam", false); + Project project = new Project(); + project.setName("ProjectWithNotAllowedExistingTeam"); + project.addAccessTeam(notAllowedTeam); + qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); + Response response = jersey.target(V1_PROJECT) + .request() + .header("Authorization", "Bearer " + jwt) + .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + Assert.assertEquals(403, response.getStatus(), 0); + } + + @Test + public void createProjectWithNotAllowedExistingTeamAdminTest() { + getUserToken(true); + Team notAllowedTeam = qm.createTeam("NotAllowedTeam", false); + Project project = new Project(); + project.setName("ProjectWithNotAllowedExistingTeam"); + project.addAccessTeam(notAllowedTeam); + qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); + Response response = jersey.target(V1_PROJECT) + .request() + .header("Authorization", "Bearer " + jwt) + .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + Assert.assertEquals(201, response.getStatus(), 0); + } + + @Test + public void createProjectWithNotExistingTeamNoAdminTest() { + getUserToken(false); + Team notAllowedTeam = new Team(); + notAllowedTeam.setUuid(new UUID(1, 1)); + notAllowedTeam.setName("NotAllowedTeam"); + Project project = new Project(); + project.addAccessTeam(notAllowedTeam); + project.setName("ProjectWithNotAllowedExistingTeam"); + qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); + Response response = jersey.target(V1_PROJECT) + .request() + .header("Authorization", "Bearer " + jwt) + .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + Assert.assertEquals(403, response.getStatus(), 0); + } + + @Test + public void createProjectWithNotExistingTeamTest() { + getUserToken(true); + Team notAllowedTeam = new Team(); + notAllowedTeam.setUuid(new UUID(1, 1)); + notAllowedTeam.setName("NotAllowedTeam"); + Project project = new Project(); + project.addAccessTeam(notAllowedTeam); + project.setName("ProjectWithNotExistingTeam"); + Response response = jersey.target(V1_PROJECT) + .request() + .header("Authorization", "Bearer " + jwt) + .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + Assert.assertEquals(404, response.getStatus(), 0); + } + @Test public void updateProjectTest() { Project project = qm.createProject("ABC", null, "1.0", null, null, null, true, false); From d8f24999fe2f565ba675b8cba660427c771c1e09 Mon Sep 17 00:00:00 2001 From: Thomas Schauer-Koeckeis Date: Fri, 30 Aug 2024 09:39:31 +0200 Subject: [PATCH 129/429] Added Tests for the visible API endpoint Signed-off-by: Thomas Schauer-Koeckeis --- .../resources/v1/TeamResourceTest.java | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java index b1662d8d36..be052f347e 100644 --- a/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java @@ -21,7 +21,9 @@ import alpine.common.util.UuidUtil; import alpine.model.ApiKey; import alpine.model.ConfigProperty; +import alpine.model.IConfigProperty; import alpine.model.ManagedUser; +import alpine.model.Permission; import alpine.model.Team; import alpine.server.auth.JsonWebToken; import alpine.server.filters.ApiFilter; @@ -31,6 +33,7 @@ import org.dependencytrack.auth.Permissions; import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.Project; +import org.dependencytrack.persistence.DefaultObjectGenerator; import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.server.ResourceConfig; import org.junit.Assert; @@ -42,6 +45,9 @@ import jakarta.ws.rs.client.Entity; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; + +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; @@ -49,6 +55,9 @@ import static org.hamcrest.CoreMatchers.equalTo; public class TeamResourceTest extends ResourceTest { + private ManagedUser testUser; + private String jwt; + private Team userNotPartof; @ClassRule public static JerseyTestRule jersey = new JerseyTestRule( @@ -56,6 +65,21 @@ public class TeamResourceTest extends ResourceTest { .register(ApiFilter.class) .register(AuthenticationFilter.class)); + public void getUserToken(boolean isAdmin) { + testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); + jwt = new JsonWebToken().createToken(testUser); + qm.addUserToTeam(testUser, team); + userNotPartof = qm.createTeam("UserNotPartof", false); + if (isAdmin) { + final var generator = new DefaultObjectGenerator(); + generator.loadDefaultPermissions(); + List permissionsList = new ArrayList(); + final Permission adminPermission = qm.getPermission("ACCESS_MANAGEMENT"); + permissionsList.add(adminPermission); + testUser.setPermissions(permissionsList); + } + } + @Test public void getTeamsTest() { for (int i=0; i<1000; i++) { @@ -206,6 +230,70 @@ public void deleteTeamWithAclTest() { Assert.assertEquals(204, response.getStatus(), 0); } + @Test + public void getVisibleAdminRequiredTeams() { + getUserToken(true); + qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); + Response response = jersey.target(V1_TEAM + "/visible") + .request() + .header("Authorization", "Bearer " + jwt) + .get(); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject body = parseJsonObject(response); + Assert.assertTrue(body.getBoolean("required")); + JsonArray teams = body.getJsonArray("teams"); + Assert.assertEquals(teams.size(), 2); + Assert.assertEquals(teams.getFirst().asJsonObject().getString("uuid"), this.team.getUuid().toString()); + Assert.assertEquals(teams.get(1).asJsonObject().getString("uuid"), userNotPartof.getUuid().toString()); + } + + @Test + public void getVisibleAdminNotRequiredTeams() { + getUserToken(true); + Response response = jersey.target(V1_TEAM + "/visible") + .request() + .header("Authorization", "Bearer " + jwt) + .get(); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject body = parseJsonObject(response); + Assert.assertFalse(body.getBoolean("required")); + JsonArray teams = body.getJsonArray("teams"); + Assert.assertEquals(teams.size(), 2); + Assert.assertEquals(teams.getFirst().asJsonObject().getString("uuid"), this.team.getUuid().toString()); + Assert.assertEquals(teams.get(1).asJsonObject().getString("uuid"), userNotPartof.getUuid().toString()); + } + + @Test + public void getVisibleNotAdminRequiredTeams() { + getUserToken(false); + qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); + Response response = jersey.target(V1_TEAM + "/visible") + .request() + .header("Authorization", "Bearer " + jwt) + .get(); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject body = parseJsonObject(response); + Assert.assertTrue(body.getBoolean("required")); + JsonArray teams = body.getJsonArray("teams"); + Assert.assertEquals(teams.size(), 1); + Assert.assertEquals(teams.getFirst().asJsonObject().getString("uuid"), this.team.getUuid().toString()); + } + + @Test + public void getVisibleNotAdminNotRequiredTeams() { + getUserToken(false); + Response response = jersey.target(V1_TEAM + "/visible") + .request() + .header("Authorization", "Bearer " + jwt) + .get(); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject body = parseJsonObject(response); + Assert.assertFalse(body.getBoolean("required")); + JsonArray teams = body.getJsonArray("teams"); + Assert.assertEquals(teams.size(), 1); + Assert.assertEquals(teams.getFirst().asJsonObject().getString("uuid"), this.team.getUuid().toString()); + } + @Test public void generateApiKeyTest() { Team team = qm.createTeam("My Team", false); From f45225ed9d03b564a330c7fc99f22e53c6c1346f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schauer-K=C3=B6ckeis=20Thomas?= Date: Tue, 10 Sep 2024 07:53:40 +0200 Subject: [PATCH 130/429] Fixed some things MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../java/org/dependencytrack/resources/v1/TeamResource.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/TeamResource.java b/src/main/java/org/dependencytrack/resources/v1/TeamResource.java index 23817c545c..fb447ac635 100644 --- a/src/main/java/org/dependencytrack/resources/v1/TeamResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/TeamResource.java @@ -228,11 +228,10 @@ public Response deleteTeam(Team jsonTeam) { @GET @Path("/visible") @Produces(MediaType.APPLICATION_JSON) - @Operation(summary = "Returns a list of Teams what are visible", description = "Requires permission PORTFOLIO_MANAGEMENT

        ") + @Operation(summary = "Returns a list of Teams that are visible", description = "

        ") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "The Visible Teams", content = @Content(schema = @Schema(implementation = VisibleTeams.class))), - @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "404", description = "Teams could not be found") + @ApiResponse(responseCode = "401", description = "Unauthorized") }) public Response availableTeams() { try (QueryManager qm = new QueryManager()) { From 3822462c82a2bc876a634ff4d80bc13a70d98636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Schauer-K=C3=B6ckeis=20Thomas?= Date: Tue, 10 Sep 2024 07:52:20 +0200 Subject: [PATCH 131/429] Switched params for tests, so expected is the real expected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../resources/v1/TeamResourceTest.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java index be052f347e..0e463ba7df 100644 --- a/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java @@ -242,9 +242,9 @@ public void getVisibleAdminRequiredTeams() { JsonObject body = parseJsonObject(response); Assert.assertTrue(body.getBoolean("required")); JsonArray teams = body.getJsonArray("teams"); - Assert.assertEquals(teams.size(), 2); - Assert.assertEquals(teams.getFirst().asJsonObject().getString("uuid"), this.team.getUuid().toString()); - Assert.assertEquals(teams.get(1).asJsonObject().getString("uuid"), userNotPartof.getUuid().toString()); + Assert.assertEquals(2, teams.size()); + Assert.assertEquals(this.team.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); + Assert.assertEquals(userNotPartof.getUuid().toString(), teams.get(1).asJsonObject().getString("uuid")); } @Test @@ -258,9 +258,9 @@ public void getVisibleAdminNotRequiredTeams() { JsonObject body = parseJsonObject(response); Assert.assertFalse(body.getBoolean("required")); JsonArray teams = body.getJsonArray("teams"); - Assert.assertEquals(teams.size(), 2); - Assert.assertEquals(teams.getFirst().asJsonObject().getString("uuid"), this.team.getUuid().toString()); - Assert.assertEquals(teams.get(1).asJsonObject().getString("uuid"), userNotPartof.getUuid().toString()); + Assert.assertEquals(2, teams.size()); + Assert.assertEquals(this.team.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); + Assert.assertEquals(userNotPartof.getUuid().toString(), teams.get(1).asJsonObject().getString("uuid")); } @Test @@ -275,8 +275,8 @@ public void getVisibleNotAdminRequiredTeams() { JsonObject body = parseJsonObject(response); Assert.assertTrue(body.getBoolean("required")); JsonArray teams = body.getJsonArray("teams"); - Assert.assertEquals(teams.size(), 1); - Assert.assertEquals(teams.getFirst().asJsonObject().getString("uuid"), this.team.getUuid().toString()); + Assert.assertEquals(1, teams.size()); + Assert.assertEquals(this.team.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); } @Test @@ -290,8 +290,8 @@ public void getVisibleNotAdminNotRequiredTeams() { JsonObject body = parseJsonObject(response); Assert.assertFalse(body.getBoolean("required")); JsonArray teams = body.getJsonArray("teams"); - Assert.assertEquals(teams.size(), 1); - Assert.assertEquals(teams.getFirst().asJsonObject().getString("uuid"), this.team.getUuid().toString()); + Assert.assertEquals(1, teams.size()); + Assert.assertEquals(this.team.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); } @Test From 4ece98c77c60da2a0197801f638e6b3f21bbe3b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Wed, 11 Sep 2024 16:46:11 +0200 Subject: [PATCH 132/429] Feat: Added Systemwide Default Language MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../org/dependencytrack/model/ConfigPropertyConstants.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java index a7864276b3..bfac30ac73 100644 --- a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java +++ b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java @@ -116,7 +116,8 @@ public enum ConfigPropertyConstants { BOM_VALIDATION_TAGS_INCLUSIVE("artifact", "bom.validation.tags.inclusive", "[]", PropertyType.STRING, "JSON array of tags for which BOM validation shall be performed"), BOM_VALIDATION_TAGS_EXCLUSIVE("artifact", "bom.validation.tags.exclusive", "[]", PropertyType.STRING, "JSON array of tags for which BOM validation shall NOT be performed"), WELCOME_MESSAGE("general", "welcome.message.html", "%20%3Chtml%3E%3Ch1%3EYour%20Welcome%20Message%3C%2Fh1%3E%3C%2Fhtml%3E", PropertyType.STRING, "Custom HTML Code that is displayed before login", true), - IS_WELCOME_MESSAGE("general", "welcome.message.enabled", "false", PropertyType.BOOLEAN, "Bool that says wheter to show the welcome message or not", true); + IS_WELCOME_MESSAGE("general", "welcome.message.enabled", "false", PropertyType.BOOLEAN, "Bool that says wheter to show the welcome message or not", true), + DEFAULT_LANGUAGE("general", "default.language", "", PropertyType.STRING, "Determine the default Language to use", true); private final String groupName; private final String propertyName; From d60c40f12cd13e7f4ef20113a2e49edf7263ed07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Wed, 11 Sep 2024 17:01:13 +0200 Subject: [PATCH 133/429] Fixed initial value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../java/org/dependencytrack/model/ConfigPropertyConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java index bfac30ac73..77d72b3490 100644 --- a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java +++ b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java @@ -117,7 +117,7 @@ public enum ConfigPropertyConstants { BOM_VALIDATION_TAGS_EXCLUSIVE("artifact", "bom.validation.tags.exclusive", "[]", PropertyType.STRING, "JSON array of tags for which BOM validation shall NOT be performed"), WELCOME_MESSAGE("general", "welcome.message.html", "%20%3Chtml%3E%3Ch1%3EYour%20Welcome%20Message%3C%2Fh1%3E%3C%2Fhtml%3E", PropertyType.STRING, "Custom HTML Code that is displayed before login", true), IS_WELCOME_MESSAGE("general", "welcome.message.enabled", "false", PropertyType.BOOLEAN, "Bool that says wheter to show the welcome message or not", true), - DEFAULT_LANGUAGE("general", "default.language", "", PropertyType.STRING, "Determine the default Language to use", true); + DEFAULT_LANGUAGE("general", "default.language", "%20", PropertyType.STRING, "Determine the default Language to use", true); private final String groupName; private final String propertyName; From 002c6f4e1b624b564f1b73fb087c47ed5e0f3282 Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Sun, 11 Aug 2024 18:25:30 +0200 Subject: [PATCH 134/429] Change badges access from checkbox to permission Replace enabling of unauthenticaed access to badges via admin config checkbox with an api authentication with a new dedicated permission "VIEW_BADGES". Signed-off-by: Kirill.Sybin --- .../org/dependencytrack/auth/Permissions.java | 4 +- .../resources/v1/BadgeResource.java | 111 ++++++++---------- 2 files changed, 51 insertions(+), 64 deletions(-) diff --git a/src/main/java/org/dependencytrack/auth/Permissions.java b/src/main/java/org/dependencytrack/auth/Permissions.java index 2c1d08cdbb..51416ef5e7 100644 --- a/src/main/java/org/dependencytrack/auth/Permissions.java +++ b/src/main/java/org/dependencytrack/auth/Permissions.java @@ -38,7 +38,8 @@ public enum Permissions { SYSTEM_CONFIGURATION("Allows the configuration of the system including notifications, repositories, and email settings"), PROJECT_CREATION_UPLOAD("Provides the ability to optionally create project (if non-existent) on BOM or scan upload"), POLICY_MANAGEMENT("Allows the creation, modification, and deletion of policy"), - TAG_MANAGEMENT("Allows the modification and deletion of tags"); + TAG_MANAGEMENT("Allows the modification and deletion of tags"), + VIEW_BADGES("Provides the ability to view badges"); private final String description; @@ -64,6 +65,7 @@ public static class Constants { public static final String PROJECT_CREATION_UPLOAD = "PROJECT_CREATION_UPLOAD"; public static final String POLICY_MANAGEMENT = "POLICY_MANAGEMENT"; public static final String TAG_MANAGEMENT = "TAG_MANAGEMENT"; + public static final String VIEW_BADGES = "VIEW_BADGES"; } } diff --git a/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java b/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java index a0a6e0cedd..c320704fcc 100644 --- a/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java @@ -18,9 +18,7 @@ */ package org.dependencytrack.resources.v1; -import alpine.common.util.BooleanUtil; -import alpine.model.ConfigProperty; -import alpine.server.auth.AuthenticationNotRequired; +import alpine.server.auth.PermissionRequired; import alpine.server.resources.AlpineResource; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -28,7 +26,10 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.security.SecurityRequirements; import io.swagger.v3.oas.annotations.tags.Tag; +import org.dependencytrack.auth.Permissions; import org.dependencytrack.model.Project; import org.dependencytrack.model.ProjectMetrics; import org.dependencytrack.model.validation.ValidUuid; @@ -41,8 +42,6 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.Response; -import static org.dependencytrack.model.ConfigPropertyConstants.GENERAL_BADGE_ENABLED; - /** * JAX-RS resources for processing metrics. * @@ -51,47 +50,42 @@ */ @Path("/v1/badge") @Tag(name = "badge") +@SecurityRequirements({ + @SecurityRequirement(name = "ApiKeyAuth"), + @SecurityRequirement(name = "BearerAuth") +}) public class BadgeResource extends AlpineResource { private static final String SVG_MEDIA_TYPE = "image/svg+xml"; - private boolean isBadgeSupportEnabled(final QueryManager qm) { - ConfigProperty property = qm.getConfigProperty( - GENERAL_BADGE_ENABLED.getGroupName(), GENERAL_BADGE_ENABLED.getPropertyName()); - return BooleanUtil.valueOf(property.getPropertyValue()); - } - @GET @Path("/vulns/project/{uuid}") @Produces(SVG_MEDIA_TYPE) @Operation( - summary = "Returns current metrics for a specific project") + summary = "Returns current metrics for a specific project", + description = "

        Requires permission VIEW_BADGES

        " + ) @ApiResponses(value = { @ApiResponse( responseCode = "200", description = "A badge displaying current vulnerability metrics for a project in SVG format", content = @Content(schema = @Schema(type = "string")) ), - @ApiResponse(responseCode = "204", description = "Badge support is disabled. No content will be returned."), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The project could not be found") }) - @AuthenticationNotRequired + @PermissionRequired(Permissions.Constants.VIEW_BADGES) public Response getProjectVulnerabilitiesBadge( @Parameter(description = "The UUID of the project to retrieve metrics for", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { - if (isBadgeSupportEnabled(qm)) { - final Project project = qm.getObjectByUuid(Project.class, uuid); - if (project != null) { - final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); - final Badger badger = new Badger(); - return Response.ok(badger.generateVulnerabilities(metrics)).build(); - } else { - return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); - } + final Project project = qm.getObjectByUuid(Project.class, uuid); + if (project != null) { + final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); + final Badger badger = new Badger(); + return Response.ok(badger.generateVulnerabilities(metrics)).build(); } else { - return Response.status(Response.Status.NO_CONTENT).build(); + return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); } } } @@ -100,35 +94,32 @@ public Response getProjectVulnerabilitiesBadge( @Path("/vulns/project/{name}/{version}") @Produces(SVG_MEDIA_TYPE) @Operation( - summary = "Returns current metrics for a specific project") + summary = "Returns current metrics for a specific project", + description = "

        Requires permission VIEW_BADGES

        " + ) @ApiResponses(value = { @ApiResponse( responseCode = "200", description = "A badge displaying current vulnerability metrics for a project in SVG format", content = @Content(schema = @Schema(type = "string")) ), - @ApiResponse(responseCode = "204", description = "Badge support is disabled. No content will be returned."), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The project could not be found") }) - @AuthenticationNotRequired + @PermissionRequired(Permissions.Constants.VIEW_BADGES) public Response getProjectVulnerabilitiesBadge( @Parameter(description = "The name of the project to query on", required = true) @PathParam("name") String name, @Parameter(description = "The version of the project to query on", required = true) @PathParam("version") String version) { try (QueryManager qm = new QueryManager()) { - if (isBadgeSupportEnabled(qm)) { - final Project project = qm.getProject(name, version); - if (project != null) { - final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); - final Badger badger = new Badger(); - return Response.ok(badger.generateVulnerabilities(metrics)).build(); - } else { - return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); - } + final Project project = qm.getProject(name, version); + if (project != null) { + final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); + final Badger badger = new Badger(); + return Response.ok(badger.generateVulnerabilities(metrics)).build(); } else { - return Response.status(Response.Status.NO_CONTENT).build(); + return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); } } } @@ -137,33 +128,30 @@ public Response getProjectVulnerabilitiesBadge( @Path("/violations/project/{uuid}") @Produces(SVG_MEDIA_TYPE) @Operation( - summary = "Returns a policy violations badge for a specific project") + summary = "Returns a policy violations badge for a specific project", + description = "

        Requires permission VIEW_BADGES

        " + ) @ApiResponses(value = { @ApiResponse( responseCode = "200", description = "A badge displaying current policy violation metrics of a project in SVG format", content = @Content(schema = @Schema(type = "string")) ), - @ApiResponse(responseCode = "204", description = "Badge support is disabled. No content will be returned."), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The project could not be found") }) - @AuthenticationNotRequired + @PermissionRequired(Permissions.Constants.VIEW_BADGES) public Response getProjectPolicyViolationsBadge( @Parameter(description = "The UUID of the project to retrieve a badge for", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { - if (isBadgeSupportEnabled(qm)) { - final Project project = qm.getObjectByUuid(Project.class, uuid); - if (project != null) { - final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); - final Badger badger = new Badger(); - return Response.ok(badger.generateViolations(metrics)).build(); - } else { - return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); - } + final Project project = qm.getObjectByUuid(Project.class, uuid); + if (project != null) { + final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); + final Badger badger = new Badger(); + return Response.ok(badger.generateViolations(metrics)).build(); } else { - return Response.status(Response.Status.NO_CONTENT).build(); + return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); } } } @@ -172,35 +160,32 @@ public Response getProjectPolicyViolationsBadge( @Path("/violations/project/{name}/{version}") @Produces(SVG_MEDIA_TYPE) @Operation( - summary = "Returns a policy violations badge for a specific project") + summary = "Returns a policy violations badge for a specific project", + description = "

        Requires permission VIEW_BADGES

        " + ) @ApiResponses(value = { @ApiResponse( responseCode = "200", description = "A badge displaying current policy violation metrics of a project in SVG format", content = @Content(schema = @Schema(type = "string")) ), - @ApiResponse(responseCode = "204", description = "Badge support is disabled. No content will be returned."), @ApiResponse(responseCode = "401", description = "Unauthorized"), @ApiResponse(responseCode = "404", description = "The project could not be found") }) - @AuthenticationNotRequired + @PermissionRequired(Permissions.Constants.VIEW_BADGES) public Response getProjectPolicyViolationsBadge( @Parameter(description = "The name of the project to query on", required = true) @PathParam("name") String name, @Parameter(description = "The version of the project to query on", required = true) @PathParam("version") String version) { try (QueryManager qm = new QueryManager()) { - if (isBadgeSupportEnabled(qm)) { - final Project project = qm.getProject(name, version); - if (project != null) { - final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); - final Badger badger = new Badger(); - return Response.ok(badger.generateViolations(metrics)).build(); - } else { - return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); - } + final Project project = qm.getProject(name, version); + if (project != null) { + final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); + final Badger badger = new Badger(); + return Response.ok(badger.generateViolations(metrics)).build(); } else { - return Response.status(Response.Status.NO_CONTENT).build(); + return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); } } } From f7cdc28008b7e87fd3e09f5b7a2f8de320f34813 Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Sun, 18 Aug 2024 12:00:33 +0200 Subject: [PATCH 135/429] Add ACL awareness to badges Signed-off-by: Kirill.Sybin --- .../dependencytrack/resources/v1/BadgeResource.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java b/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java index c320704fcc..29427084c8 100644 --- a/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java @@ -81,6 +81,9 @@ public Response getProjectVulnerabilitiesBadge( try (QueryManager qm = new QueryManager()) { final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { + if (!qm.hasAccess(super.getPrincipal(), project)) { + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + } final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); final Badger badger = new Badger(); return Response.ok(badger.generateVulnerabilities(metrics)).build(); @@ -115,6 +118,9 @@ public Response getProjectVulnerabilitiesBadge( try (QueryManager qm = new QueryManager()) { final Project project = qm.getProject(name, version); if (project != null) { + if (!qm.hasAccess(super.getPrincipal(), project)) { + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + } final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); final Badger badger = new Badger(); return Response.ok(badger.generateVulnerabilities(metrics)).build(); @@ -147,6 +153,9 @@ public Response getProjectPolicyViolationsBadge( try (QueryManager qm = new QueryManager()) { final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { + if (!qm.hasAccess(super.getPrincipal(), project)) { + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + } final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); final Badger badger = new Badger(); return Response.ok(badger.generateViolations(metrics)).build(); @@ -181,6 +190,9 @@ public Response getProjectPolicyViolationsBadge( try (QueryManager qm = new QueryManager()) { final Project project = qm.getProject(name, version); if (project != null) { + if (!qm.hasAccess(super.getPrincipal(), project)) { + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + } final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); final Badger badger = new Badger(); return Response.ok(badger.generateViolations(metrics)).build(); From 309dc3955113b2c9e05c491459d9e7fd0433b050 Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Sun, 18 Aug 2024 14:37:12 +0200 Subject: [PATCH 136/429] Remove config property constant for badge enabling Signed-off-by: Kirill.Sybin --- .../java/org/dependencytrack/model/ConfigPropertyConstants.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java index a7864276b3..e6c623decf 100644 --- a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java +++ b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java @@ -27,7 +27,6 @@ public enum ConfigPropertyConstants { GENERAL_BASE_URL("general", "base.url", null, PropertyType.URL, "URL used to construct links back to Dependency-Track from external systems"), - GENERAL_BADGE_ENABLED("general", "badge.enabled", "false", PropertyType.BOOLEAN, "Flag to enable/disable SVG badge support from metrics"), EMAIL_SMTP_ENABLED("email", "smtp.enabled", "false", PropertyType.BOOLEAN, "Flag to enable/disable SMTP"), EMAIL_SMTP_FROM_ADDR("email", "smtp.from.address", null, PropertyType.STRING, "The from email address to use to send output SMTP mail"), EMAIL_PREFIX("email", "subject.prefix", "[Dependency-Track]", PropertyType.STRING, "The Prefix Subject email to use"), From d1067c264435e65fb98b65d9d6e93347bac58c4c Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Sun, 18 Aug 2024 14:37:59 +0200 Subject: [PATCH 137/429] Modify tests to accomodate badge changes Add new badge permission to tests. Remove tests for badge disabling. Add tests testing authentication, permission and ACL access. Signed-off-by: Kirill.Sybin --- .../dependencytrack/auth/PermissionsTest.java | 5 +- .../resources/v1/BadgeResourceTest.java | 339 ++++++++++++++++-- .../resources/v1/PermissionResourceTest.java | 4 + 3 files changed, 308 insertions(+), 40 deletions(-) diff --git a/src/test/java/org/dependencytrack/auth/PermissionsTest.java b/src/test/java/org/dependencytrack/auth/PermissionsTest.java index f02da899a0..3e3f5a1548 100644 --- a/src/test/java/org/dependencytrack/auth/PermissionsTest.java +++ b/src/test/java/org/dependencytrack/auth/PermissionsTest.java @@ -34,11 +34,12 @@ import static org.dependencytrack.auth.Permissions.Constants.VIEW_VULNERABILITY; import static org.dependencytrack.auth.Permissions.Constants.VULNERABILITY_ANALYSIS; import static org.dependencytrack.auth.Permissions.Constants.VULNERABILITY_MANAGEMENT; +import static org.dependencytrack.auth.Permissions.Constants.VIEW_BADGES; public class PermissionsTest { @Test public void testPermissionEnums() { - Assert.assertEquals(13, Permissions.values().length); + Assert.assertEquals(14, Permissions.values().length); Assert.assertEquals("BOM_UPLOAD", Permissions.BOM_UPLOAD.name()); Assert.assertEquals("VIEW_PORTFOLIO", Permissions.VIEW_PORTFOLIO.name()); Assert.assertEquals("PORTFOLIO_MANAGEMENT", Permissions.PORTFOLIO_MANAGEMENT.name()); @@ -52,6 +53,7 @@ public void testPermissionEnums() { Assert.assertEquals("PROJECT_CREATION_UPLOAD", Permissions.PROJECT_CREATION_UPLOAD.name()); Assert.assertEquals("POLICY_MANAGEMENT", Permissions.POLICY_MANAGEMENT.name()); Assert.assertEquals("TAG_MANAGEMENT", Permissions.TAG_MANAGEMENT.name()); + Assert.assertEquals("VIEW_BADGES", Permissions.VIEW_BADGES.name()); } @Test @@ -69,5 +71,6 @@ public void testPermissionConstants() { Assert.assertEquals("PROJECT_CREATION_UPLOAD", PROJECT_CREATION_UPLOAD); Assert.assertEquals("POLICY_MANAGEMENT", POLICY_MANAGEMENT); Assert.assertEquals("TAG_MANAGEMENT", TAG_MANAGEMENT); + Assert.assertEquals("VIEW_BADGES", VIEW_BADGES); } } diff --git a/src/test/java/org/dependencytrack/resources/v1/BadgeResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BadgeResourceTest.java index f427cc1f81..01c03a5e06 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BadgeResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BadgeResourceTest.java @@ -18,10 +18,13 @@ */ package org.dependencytrack.resources.v1; -import alpine.model.IConfigProperty; import alpine.server.filters.ApiFilter; +import alpine.server.filters.AuthenticationFilter; +import alpine.server.filters.AuthorizationFilter; import org.dependencytrack.JerseyTestRule; import org.dependencytrack.ResourceTest; +import org.dependencytrack.auth.Permissions; +import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.Project; import org.glassfish.jersey.server.ResourceConfig; import org.junit.Assert; @@ -34,27 +37,25 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.UUID; -import static org.dependencytrack.model.ConfigPropertyConstants.GENERAL_BADGE_ENABLED; - public class BadgeResourceTest extends ResourceTest { @ClassRule public static JerseyTestRule jersey = new JerseyTestRule( new ResourceConfig(BadgeResource.class) - .register(ApiFilter.class)); - - @Override - public void before() throws Exception { - super.before(); - qm.createConfigProperty(GENERAL_BADGE_ENABLED.getGroupName(), GENERAL_BADGE_ENABLED.getPropertyName(), "true", IConfigProperty.PropertyType.BOOLEAN, "Badge enabled"); - } + .register(ApiFilter.class) + .register(AuthenticationFilter.class) + .register(AuthorizationFilter.class)); @Test public void projectVulnerabilitiesByUuidTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()).request() + .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(200, response.getStatus(), 0); Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); @@ -62,24 +63,54 @@ public void projectVulnerabilitiesByUuidTest() { } @Test - public void projectVulnerabilitiesByUuidProjectDisabledTest() { - disableBadge(); + public void projectVulnerabilitiesByUuidProjectNotFoundTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + Response response = jersey.target(V1_BADGE + "/vulns/project/" + UUID.randomUUID()).request() + .header(X_API_KEY, apiKey) .get(Response.class); - Assert.assertEquals(204, response.getStatus(), 0); + Assert.assertEquals(404, response.getStatus(), 0); } @Test - public void projectVulnerabilitiesByUuidProjectNotFoundTest() { - Response response = jersey.target(V1_BADGE + "/vulns/project/" + UUID.randomUUID()).request() + public void projectVulnerabilitiesByUuidMissingAuthenticationTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()).request() .get(Response.class); - Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertEquals(401, response.getStatus(), 0); } @Test - public void projectVulnerabilitiesByNameAndVersionTest() { - qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); - Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0").request() + public void projectVulnerabilitiesByUuidMissingPermissionTest() { + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()).request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(403, response.getStatus(), 0); + } + + @Test + public void projectVulnerabilitiesByUuidWithAclAccessTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null + ); + + Project project = new Project(); + project.setName("Acme Example"); + project.setVersion("1.0.0"); + project.setAccessTeams(List.of(team)); + qm.persist(project); + + Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()).request() + .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(200, response.getStatus(), 0); Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); @@ -87,32 +118,137 @@ public void projectVulnerabilitiesByNameAndVersionTest() { } @Test - public void projectVulnerabilitiesByNameAndVersionDisabledTest() { - disableBadge(); - Response response = jersey.target(V1_BADGE + "/vulns/project/ProjectNameDoesNotExist/1.0.0").request() + public void projectVulnerabilitiesByUuidWithAclNoAccessTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null + ); + + Project project = new Project(); + project.setName("Acme Example"); + project.setVersion("1.0.0"); + qm.persist(project); + + Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()).request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(403, response.getStatus(), 0); + } + + @Test + public void projectVulnerabilitiesByNameAndVersionTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0").request() + .header(X_API_KEY, apiKey) .get(Response.class); - Assert.assertEquals(204, response.getStatus(), 0); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); } @Test public void projectVulnerabilitiesByNameAndVersionProjectNotFoundTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + Response response = jersey.target(V1_BADGE + "/vulns/project/ProjectNameDoesNotExist/1.0.0").request() + .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(404, response.getStatus(), 0); } @Test public void projectVulnerabilitiesByNameAndVersionVersionNotFoundTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.2.0").request() + .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(404, response.getStatus(), 0); } + @Test + public void projectVulnerabilitiesByNameAndVersionMissingAuthenticationTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0").request() + .get(Response.class); + Assert.assertEquals(401, response.getStatus(), 0); + } + + @Test + public void projectVulnerabilitiesByNameAndVersionMissingPermissionTest() { + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0").request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(403, response.getStatus(), 0); + } + + @Test + public void projectVulnerabilitiesByNameAndVersionWithAclAccessTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null + ); + + Project project = new Project(); + project.setName("Acme Example"); + project.setVersion("1.0.0"); + project.setAccessTeams(List.of(team)); + qm.persist(project); + + Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0").request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + + @Test + public void projectVulnerabilitiesByNameAndVersionWithAclNoAccessTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null + ); + + Project project = new Project(); + project.setName("Acme Example"); + project.setVersion("1.0.0"); + qm.persist(project); + + Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0").request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(403, response.getStatus(), 0); + } + @Test public void projectPolicyViolationsByUuidTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()).request() + .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(200, response.getStatus(), 0); Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); @@ -120,24 +256,54 @@ public void projectPolicyViolationsByUuidTest() { } @Test - public void projectPolicyViolationsByUuidProjectDisabledTest() { - disableBadge(); + public void projectPolicyViolationsByUuidProjectNotFoundTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + Response response = jersey.target(V1_BADGE + "/violations/project/" + UUID.randomUUID()).request() + .header(X_API_KEY, apiKey) .get(Response.class); - Assert.assertEquals(204, response.getStatus(), 0); + Assert.assertEquals(404, response.getStatus(), 0); } @Test - public void projectPolicyViolationsByUuidProjectNotFoundTest() { - Response response = jersey.target(V1_BADGE + "/violations/project/" + UUID.randomUUID()).request() + public void projectPolicyViolationsByUuidMissingAuthenticationTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()).request() .get(Response.class); - Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertEquals(401, response.getStatus(), 0); } @Test - public void projectPolicyViolationsByNameAndVersionTest() { - qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); - Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0").request() + public void projectPolicyViolationsByUuidMissingPermissionTest() { + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()).request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(403, response.getStatus(), 0); + } + + @Test + public void projectPolicyViolationsByUuidWithAclAccessTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null + ); + + Project project = new Project(); + project.setName("Acme Example"); + project.setVersion("1.0.0"); + project.setAccessTeams(List.of(team)); + qm.persist(project); + + Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()).request() + .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(200, response.getStatus(), 0); Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); @@ -145,31 +311,126 @@ public void projectPolicyViolationsByNameAndVersionTest() { } @Test - public void projectPolicyViolationsByNameAndVersionDisabledTest() { - disableBadge(); - Response response = jersey.target(V1_BADGE + "/violations/project/ProjectNameDoesNotExist/1.0.0").request() + public void projectPolicyViolationsByUuidWithAclNoAccessTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null + ); + + Project project = new Project(); + project.setName("Acme Example"); + project.setVersion("1.0.0"); + qm.persist(project); + + Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()).request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(403, response.getStatus(), 0); + } + + @Test + public void projectPolicyViolationsByNameAndVersionTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0").request() + .header(X_API_KEY, apiKey) .get(Response.class); - Assert.assertEquals(204, response.getStatus(), 0); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); } @Test public void projectPolicyViolationsByNameAndVersionProjectNotFoundTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + Response response = jersey.target(V1_BADGE + "/violations/project/ProjectNameDoesNotExist/1.0.0").request() + .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(404, response.getStatus(), 0); } @Test public void projectPolicyViolationsByNameAndVersionVersionNotFoundTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.2.0").request() + .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(404, response.getStatus(), 0); } - private void disableBadge() { - qm.getConfigProperty(GENERAL_BADGE_ENABLED.getGroupName(), GENERAL_BADGE_ENABLED.getPropertyName()) - .setPropertyValue("false"); + @Test + public void projectPolicyViolationsByNameAndVersionMissingAuthenticationTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0").request() + .get(Response.class); + Assert.assertEquals(401, response.getStatus(), 0); + } + + @Test + public void projectPolicyViolationsByNameAndVersionMissingPermissionTest() { + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0").request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(403, response.getStatus(), 0); + } + + @Test + public void projectPolicyViolationsByNameAndVersionWithAclAccessTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null + ); + + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + project.setAccessTeams(List.of(team)); + qm.persist(project); + + Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0").request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + + @Test + public void projectPolicyViolationsByNameAndVersionWithAclNoAccessTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null + ); + + Project project = new Project(); + project.setName("Acme Example"); + project.setVersion("1.0.0"); + qm.persist(project); + + Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0").request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(403, response.getStatus(), 0); } private boolean isLikelySvg(String body) { diff --git a/src/test/java/org/dependencytrack/resources/v1/PermissionResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/PermissionResourceTest.java index 48a663d2bd..a2931d9b9c 100644 --- a/src/test/java/org/dependencytrack/resources/v1/PermissionResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/PermissionResourceTest.java @@ -100,6 +100,10 @@ public void getAllPermissionsTest() { "description": "Allows the modification and deletion of tags", "name": "TAG_MANAGEMENT" }, + { + "description": "Provides the ability to view badges", + "name": "VIEW_BADGES" + }, { "description": "Provides the ability to view policy violations", "name": "VIEW_POLICY_VIOLATION" From 1931654ff8f0a27b27f3bed2a6c56e33437594e5 Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Sun, 1 Sep 2024 19:01:00 +0200 Subject: [PATCH 138/429] Update documentation Signed-off-by: Kirill.Sybin --- docs/_docs/integrations/badges.md | 43 ++++++++++++++++++------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/docs/_docs/integrations/badges.md b/docs/_docs/integrations/badges.md index 5f648cd7b3..c6422859dc 100644 --- a/docs/_docs/integrations/badges.md +++ b/docs/_docs/integrations/badges.md @@ -5,14 +5,21 @@ chapter: 6 order: 10 --- -Dependency-Track supports badges in Scalable Vector Graphics (SVG) format. Support for badges is a globally configurable -option and is disabled by default. +Dependency-Track supports badges in Scalable Vector Graphics (SVG) format. Support for badges is configurable on a team +basis via permission. -> Enabling badge support will provide vulnerability and policy violation metric information to unauthenticated users. -> Any anonymous user with network access to Dependency-Track and knowledge of a projects information will be able -> to view the SVG badge. +To enable badges for a team, activate the permission `VIEW_BADGES`. To deactivate badges, remove the permission. To +retrieve a badge, use a team's API key either in the badge API header `X-API-Key` or in the URI parameter `apiKey`. -In all following examples, replace `{name}`, `{version}`, and `{uuid}` with their respective values. +> As badges are typically embedded in places that more people have access to than to Dependency-Track, the API key used +> for the badge request should have minimal scope to prevent unintended access beyond that badge. Ideally, the API +> key belongs to a single-purpose team, having just the `VIEW_BADGES` permission, with only one API key and access to +> only the projects/project versions whose badges are displayed at one site--the latter requiring _Portfolio Access +> Control_. + +In all following examples, replace `{name}`, `{version}`, `{uuid}`, and `{apiKey}` with their respective values. For +brevity, the examples use the URI query parameter as the method of authentication, however, they also work with +authentication by header. ### Vulnerable components Create a badge for vulnerable components of the project. It either shows: @@ -33,8 +40,8 @@ name and version. #### Examples ``` -https://dtrack.example.com/api/v1/badge/vulns/project/{name}/{version} -https://dtrack.example.com/api/v1/badge/vulns/project/{uuid} +https://dtrack.example.com/api/v1/badge/vulns/project/{name}/{version}?apiKey={apiKey} +https://dtrack.example.com/api/v1/badge/vulns/project/{uuid}?apiKey={apiKey} ``` ### Policy violations @@ -57,8 +64,8 @@ projects name and version. #### Examples ``` -https://dtrack.example.com/api/v1/badge/violations/project/{name}/{version} -https://dtrack.example.com/api/v1/badge/violations/project/{uuid} +https://dtrack.example.com/api/v1/badge/violations/project/{name}/{version}?apiKey={apiKey} +https://dtrack.example.com/api/v1/badge/violations/project/{uuid}?apiKey={apiKey} ``` @@ -67,17 +74,17 @@ You can embed the badges in other documents. It allows you to display a badge in #### HTML Examples ```html - - - - + + + + ``` #### Markdown Examples ```markdown -![alt text](https://dtrack.example.com/api/v1/badge/vulns/project/{name}/{version}) -![alt text](https://dtrack.example.com/api/v1/badge/vulns/project/{uuid}) -![alt text](https://dtrack.example.com/api/v1/badge/violations/project/{name}/{version}) -![alt text](https://dtrack.example.com/api/v1/badge/violations/project/{uuid}) +![alt text](https://dtrack.example.com/api/v1/badge/vulns/project/{name}/{version}?apiKey={apiKey}) +![alt text](https://dtrack.example.com/api/v1/badge/vulns/project/{uuid}?apiKey={apiKey}) +![alt text](https://dtrack.example.com/api/v1/badge/violations/project/{name}/{version}?apiKey={apiKey}) +![alt text](https://dtrack.example.com/api/v1/badge/violations/project/{uuid}?apiKey={apiKey}) ``` From 60ffaaf90af22eeebe5efd1a7394164f70a0508c Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Sun, 1 Sep 2024 21:31:40 +0200 Subject: [PATCH 139/429] Enable auth via URI query param for badge API Allows API authentication via URI query param for badge requests as an alternative to header authentication because typical use cases for badges do not easily allow header injection. Requires https://github.com/stevespringett/Alpine/issues/641 Signed-off-by: Kirill.Sybin --- .../org/dependencytrack/resources/v1/BadgeResource.java | 8 +++++++- src/main/resources/openapi-configuration.yaml | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java b/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java index 29427084c8..0abad88e19 100644 --- a/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java @@ -18,6 +18,7 @@ */ package org.dependencytrack.resources.v1; +import alpine.server.auth.AllowApiKeyInQueryParameter; import alpine.server.auth.PermissionRequired; import alpine.server.resources.AlpineResource; import io.swagger.v3.oas.annotations.Operation; @@ -52,7 +53,8 @@ @Tag(name = "badge") @SecurityRequirements({ @SecurityRequirement(name = "ApiKeyAuth"), - @SecurityRequirement(name = "BearerAuth") + @SecurityRequirement(name = "BearerAuth"), + @SecurityRequirement(name = "ApiKeyQueryAuth") }) public class BadgeResource extends AlpineResource { @@ -75,6 +77,7 @@ public class BadgeResource extends AlpineResource { @ApiResponse(responseCode = "404", description = "The project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_BADGES) + @AllowApiKeyInQueryParameter public Response getProjectVulnerabilitiesBadge( @Parameter(description = "The UUID of the project to retrieve metrics for", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { @@ -110,6 +113,7 @@ public Response getProjectVulnerabilitiesBadge( @ApiResponse(responseCode = "404", description = "The project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_BADGES) + @AllowApiKeyInQueryParameter public Response getProjectVulnerabilitiesBadge( @Parameter(description = "The name of the project to query on", required = true) @PathParam("name") String name, @@ -147,6 +151,7 @@ public Response getProjectVulnerabilitiesBadge( @ApiResponse(responseCode = "404", description = "The project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_BADGES) + @AllowApiKeyInQueryParameter public Response getProjectPolicyViolationsBadge( @Parameter(description = "The UUID of the project to retrieve a badge for", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { @@ -182,6 +187,7 @@ public Response getProjectPolicyViolationsBadge( @ApiResponse(responseCode = "404", description = "The project could not be found") }) @PermissionRequired(Permissions.Constants.VIEW_BADGES) + @AllowApiKeyInQueryParameter public Response getProjectPolicyViolationsBadge( @Parameter(description = "The name of the project to query on", required = true) @PathParam("name") String name, diff --git a/src/main/resources/openapi-configuration.yaml b/src/main/resources/openapi-configuration.yaml index 1d17c261a0..1737efc9c4 100644 --- a/src/main/resources/openapi-configuration.yaml +++ b/src/main/resources/openapi-configuration.yaml @@ -23,6 +23,10 @@ openAPI: BearerAuth: type: http scheme: Bearer + ApiKeyQueryAuth: + name: apiKey + type: apiKey + in: query prettyPrint: true resourcePackages: - alpine.server.resources From 8c40c9a413a7622c3100b117eebb732dc4610a6e Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Sun, 1 Sep 2024 21:33:58 +0200 Subject: [PATCH 140/429] Update badge resource tests to auth via URI query Update tests to focus on API authentication via URI query parameter, but keep some tests that test header authentication as that remains an option. Requires https://github.com/stevespringett/Alpine/issues/641 Signed-off-by: Kirill.Sybin --- .../org/dependencytrack/ResourceTest.java | 1 + .../resources/v1/BadgeResourceTest.java | 232 +++++++++++++++--- 2 files changed, 205 insertions(+), 28 deletions(-) diff --git a/src/test/java/org/dependencytrack/ResourceTest.java b/src/test/java/org/dependencytrack/ResourceTest.java index ef37c77967..61d6bed35f 100644 --- a/src/test/java/org/dependencytrack/ResourceTest.java +++ b/src/test/java/org/dependencytrack/ResourceTest.java @@ -78,6 +78,7 @@ public abstract class ResourceTest { protected final String SIZE = "size"; protected final String TOTAL_COUNT_HEADER = "X-Total-Count"; protected final String X_API_KEY = "X-Api-Key"; + protected final String API_KEY = "apiKey"; protected final String V1_TAG = "/v1/tag"; // Hashing is expensive. Do it once and re-use across tests as much as possible. diff --git a/src/test/java/org/dependencytrack/resources/v1/BadgeResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BadgeResourceTest.java index 01c03a5e06..3599d3111d 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BadgeResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BadgeResourceTest.java @@ -53,6 +53,20 @@ public class BadgeResourceTest extends ResourceTest { public void projectVulnerabilitiesByUuidTest() { initializeWithPermissions(Permissions.VIEW_BADGES); + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()) + .queryParam(API_KEY, apiKey) + .request() + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + + @Test + public void projectVulnerabilitiesByUuidWithHeaderAuthenticationTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()).request() .header(X_API_KEY, apiKey) @@ -66,8 +80,9 @@ public void projectVulnerabilitiesByUuidTest() { public void projectVulnerabilitiesByUuidProjectNotFoundTest() { initializeWithPermissions(Permissions.VIEW_BADGES); - Response response = jersey.target(V1_BADGE + "/vulns/project/" + UUID.randomUUID()).request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/vulns/project/" + UUID.randomUUID()) + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(404, response.getStatus(), 0); } @@ -85,8 +100,9 @@ public void projectVulnerabilitiesByUuidMissingAuthenticationTest() { @Test public void projectVulnerabilitiesByUuidMissingPermissionTest() { Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); - Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()).request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()) + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(403, response.getStatus(), 0); } @@ -109,6 +125,33 @@ public void projectVulnerabilitiesByUuidWithAclAccessTest() { project.setAccessTeams(List.of(team)); qm.persist(project); + Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()) + .queryParam(API_KEY, apiKey) + .request() + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + + @Test + public void projectVulnerabilitiesByUuidWithAclAccessWithHeaderAuthenticationTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null + ); + + Project project = new Project(); + project.setName("Acme Example"); + project.setVersion("1.0.0"); + project.setAccessTeams(List.of(team)); + qm.persist(project); + Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()).request() .header(X_API_KEY, apiKey) .get(Response.class); @@ -134,8 +177,9 @@ public void projectVulnerabilitiesByUuidWithAclNoAccessTest() { project.setVersion("1.0.0"); qm.persist(project); - Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()).request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()) + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(403, response.getStatus(), 0); } @@ -144,6 +188,20 @@ public void projectVulnerabilitiesByUuidWithAclNoAccessTest() { public void projectVulnerabilitiesByNameAndVersionTest() { initializeWithPermissions(Permissions.VIEW_BADGES); + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0") + .queryParam(API_KEY, apiKey) + .request() + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + + @Test + public void projectVulnerabilitiesByNameAndVersionWithHeaderAuthenticationTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0").request() .header(X_API_KEY, apiKey) @@ -157,8 +215,9 @@ public void projectVulnerabilitiesByNameAndVersionTest() { public void projectVulnerabilitiesByNameAndVersionProjectNotFoundTest() { initializeWithPermissions(Permissions.VIEW_BADGES); - Response response = jersey.target(V1_BADGE + "/vulns/project/ProjectNameDoesNotExist/1.0.0").request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/vulns/project/ProjectNameDoesNotExist/1.0.0") + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(404, response.getStatus(), 0); } @@ -168,8 +227,9 @@ public void projectVulnerabilitiesByNameAndVersionVersionNotFoundTest() { initializeWithPermissions(Permissions.VIEW_BADGES); qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); - Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.2.0").request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.2.0") + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(404, response.getStatus(), 0); } @@ -187,8 +247,9 @@ public void projectVulnerabilitiesByNameAndVersionMissingAuthenticationTest() { @Test public void projectVulnerabilitiesByNameAndVersionMissingPermissionTest() { qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); - Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0").request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0") + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(403, response.getStatus(), 0); } @@ -211,6 +272,33 @@ public void projectVulnerabilitiesByNameAndVersionWithAclAccessTest() { project.setAccessTeams(List.of(team)); qm.persist(project); + Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0") + .queryParam(API_KEY, apiKey) + .request() + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + + @Test + public void projectVulnerabilitiesByNameAndVersionWithAclAccessWithHeaderAuthenticationTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null + ); + + Project project = new Project(); + project.setName("Acme Example"); + project.setVersion("1.0.0"); + project.setAccessTeams(List.of(team)); + qm.persist(project); + Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0").request() .header(X_API_KEY, apiKey) .get(Response.class); @@ -236,8 +324,9 @@ public void projectVulnerabilitiesByNameAndVersionWithAclNoAccessTest() { project.setVersion("1.0.0"); qm.persist(project); - Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0").request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0") + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(403, response.getStatus(), 0); } @@ -246,6 +335,20 @@ public void projectVulnerabilitiesByNameAndVersionWithAclNoAccessTest() { public void projectPolicyViolationsByUuidTest() { initializeWithPermissions(Permissions.VIEW_BADGES); + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()) + .queryParam(API_KEY, apiKey) + .request() + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + + @Test + public void projectPolicyViolationsByUuidWithHeaderAuthenticationTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()).request() .header(X_API_KEY, apiKey) @@ -259,8 +362,9 @@ public void projectPolicyViolationsByUuidTest() { public void projectPolicyViolationsByUuidProjectNotFoundTest() { initializeWithPermissions(Permissions.VIEW_BADGES); - Response response = jersey.target(V1_BADGE + "/violations/project/" + UUID.randomUUID()).request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/violations/project/" + UUID.randomUUID()) + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(404, response.getStatus(), 0); } @@ -278,8 +382,9 @@ public void projectPolicyViolationsByUuidMissingAuthenticationTest() { @Test public void projectPolicyViolationsByUuidMissingPermissionTest() { Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); - Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()).request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()) + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(403, response.getStatus(), 0); } @@ -302,6 +407,33 @@ public void projectPolicyViolationsByUuidWithAclAccessTest() { project.setAccessTeams(List.of(team)); qm.persist(project); + Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()) + .queryParam(API_KEY, apiKey) + .request() + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + + @Test + public void projectPolicyViolationsByUuidWithAclAccessWithHeaderAuthenticationTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null + ); + + Project project = new Project(); + project.setName("Acme Example"); + project.setVersion("1.0.0"); + project.setAccessTeams(List.of(team)); + qm.persist(project); + Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()).request() .header(X_API_KEY, apiKey) .get(Response.class); @@ -327,8 +459,9 @@ public void projectPolicyViolationsByUuidWithAclNoAccessTest() { project.setVersion("1.0.0"); qm.persist(project); - Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()).request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()) + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(403, response.getStatus(), 0); } @@ -337,6 +470,20 @@ public void projectPolicyViolationsByUuidWithAclNoAccessTest() { public void projectPolicyViolationsByNameAndVersionTest() { initializeWithPermissions(Permissions.VIEW_BADGES); + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0") + .queryParam(API_KEY, apiKey) + .request() + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + + @Test + public void projectPolicyViolationsByNameAndVersionWithHeaderAuthenticationTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0").request() .header(X_API_KEY, apiKey) @@ -350,8 +497,9 @@ public void projectPolicyViolationsByNameAndVersionTest() { public void projectPolicyViolationsByNameAndVersionProjectNotFoundTest() { initializeWithPermissions(Permissions.VIEW_BADGES); - Response response = jersey.target(V1_BADGE + "/violations/project/ProjectNameDoesNotExist/1.0.0").request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/violations/project/ProjectNameDoesNotExist/1.0.0") + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(404, response.getStatus(), 0); } @@ -361,8 +509,9 @@ public void projectPolicyViolationsByNameAndVersionVersionNotFoundTest() { initializeWithPermissions(Permissions.VIEW_BADGES); qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); - Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.2.0").request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.2.0") + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(404, response.getStatus(), 0); } @@ -380,8 +529,9 @@ public void projectPolicyViolationsByNameAndVersionMissingAuthenticationTest() { @Test public void projectPolicyViolationsByNameAndVersionMissingPermissionTest() { qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); - Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0").request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0") + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(403, response.getStatus(), 0); } @@ -402,6 +552,31 @@ public void projectPolicyViolationsByNameAndVersionWithAclAccessTest() { project.setAccessTeams(List.of(team)); qm.persist(project); + Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0") + .queryParam(API_KEY, apiKey) + .request() + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + + @Test + public void projectPolicyViolationsByNameAndVersionWithAclAccessWithHeaderAuthenticationTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null + ); + + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + project.setAccessTeams(List.of(team)); + qm.persist(project); + Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0").request() .header(X_API_KEY, apiKey) .get(Response.class); @@ -427,8 +602,9 @@ public void projectPolicyViolationsByNameAndVersionWithAclNoAccessTest() { project.setVersion("1.0.0"); qm.persist(project); - Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0").request() - .header(X_API_KEY, apiKey) + Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0") + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(403, response.getStatus(), 0); } From a40a1474923169ee5b069a7ca88593b3a0d6ebf7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 08:49:17 +0000 Subject: [PATCH 141/429] Bump lib.protobuf-java.version from 4.28.0 to 4.28.1 Bumps `lib.protobuf-java.version` from 4.28.0 to 4.28.1. Updates `com.google.protobuf:protobuf-java` from 4.28.0 to 4.28.1 - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/commits) Updates `com.google.protobuf:protobuf-java-util` from 4.28.0 to 4.28.1 --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-java dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.google.protobuf:protobuf-java-util dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 72df84de9a..d7fd29dbdd 100644 --- a/pom.xml +++ b/pom.xml @@ -118,7 +118,7 @@ 6.2.0 1.5.0 3.2.2 - 4.28.0 + 4.28.1 2.2.0 2.1.22 1.19.0 From 4db85be4fe3fd92c73731a298724880edeaa85fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Thu, 12 Sep 2024 14:55:28 +0200 Subject: [PATCH 142/429] Feat: Fix that Emails render all symbols right MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../notification/publisher/DefaultNotificationPublishers.java | 2 +- .../notification/publisher/SendMailPublisher.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java b/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java index 85f49c7a38..3c5ee7c262 100644 --- a/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java +++ b/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java @@ -25,7 +25,7 @@ public enum DefaultNotificationPublishers { SLACK("Slack", "Publishes notifications to a Slack channel", SlackPublisher.class, "/templates/notification/publisher/slack.peb", MediaType.APPLICATION_JSON, true), MS_TEAMS("Microsoft Teams", "Publishes notifications to a Microsoft Teams channel", MsTeamsPublisher.class, "/templates/notification/publisher/msteams.peb", MediaType.APPLICATION_JSON, true), MATTERMOST("Mattermost", "Publishes notifications to a Mattermost channel", MattermostPublisher.class, "/templates/notification/publisher/mattermost.peb", MediaType.APPLICATION_JSON, true), - EMAIL("Email", "Sends notifications to an email address", SendMailPublisher.class, "/templates/notification/publisher/email.peb", MediaType.TEXT_PLAIN, true), + EMAIL("Email", "Sends notifications to an email address", SendMailPublisher.class, "/templates/notification/publisher/email.peb", "text/plain; charset=utf-8", true), CONSOLE("Console", "Displays notifications on the system console", ConsolePublisher.class, "/templates/notification/publisher/console.peb", MediaType.TEXT_PLAIN, true), WEBHOOK("Outbound Webhook", "Publishes notifications to a configurable endpoint", WebhookPublisher.class, "/templates/notification/publisher/webhook.peb", MediaType.APPLICATION_JSON, true), CS_WEBEX("Cisco Webex", "Publishes notifications to a Cisco Webex Teams channel", CsWebexPublisher.class, "/templates/notification/publisher/cswebex.peb", MediaType.APPLICATION_JSON, true), diff --git a/src/main/java/org/dependencytrack/notification/publisher/SendMailPublisher.java b/src/main/java/org/dependencytrack/notification/publisher/SendMailPublisher.java index 715313afe5..a515ca9b52 100644 --- a/src/main/java/org/dependencytrack/notification/publisher/SendMailPublisher.java +++ b/src/main/java/org/dependencytrack/notification/publisher/SendMailPublisher.java @@ -28,6 +28,8 @@ import alpine.server.mail.SendMailException; import io.pebbletemplates.pebble.PebbleEngine; import io.pebbletemplates.pebble.template.PebbleTemplate; + +import org.apache.commons.text.StringEscapeUtils; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.util.DebugDataEncryption; @@ -141,7 +143,7 @@ private void sendNotification(final PublishContext ctx, Notification notificatio .from(smtpFrom) .to(destinations) .subject(emailSubjectPrefix + " " + notification.getTitle()) - .body(content) + .body(StringEscapeUtils.unescapeHtml4(content)) .bodyMimeType(mimeType) .host(smtpHostname) .port(smtpPort) From 8a05fbe915c6380a5e2c7735effd361d3363b9ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Thu, 12 Sep 2024 15:19:12 +0200 Subject: [PATCH 143/429] Feat: replaced SNAPSHOT with empty String for when no version is given MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../dependencytrack/parser/cyclonedx/util/ModelConverter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java index fce714a633..2ea2bb172b 100644 --- a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java +++ b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java @@ -708,7 +708,7 @@ public static org.cyclonedx.model.Metadata createMetadata(final Project project) cycloneComponent.setGroup(StringUtils.trimToNull(project.getGroup())); cycloneComponent.setName(StringUtils.trimToNull(project.getName())); if (StringUtils.trimToNull(project.getVersion()) == null) { - cycloneComponent.setVersion("SNAPSHOT"); // Version is required per CycloneDX spec + cycloneComponent.setVersion(""); // Version is required per CycloneDX spec } else { cycloneComponent.setVersion(StringUtils.trimToNull(project.getVersion())); } From 7f8cd640f65c6401cbf3dd3932ef6325a810e6dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Thu, 12 Sep 2024 15:44:26 +0200 Subject: [PATCH 144/429] Fixed test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../publisher/DefaultNotificationPublishersTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishersTest.java b/src/test/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishersTest.java index 3718ba3bec..b125d50766 100644 --- a/src/test/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishersTest.java +++ b/src/test/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishersTest.java @@ -72,7 +72,7 @@ public void testEmail() { Assert.assertEquals("Sends notifications to an email address", DefaultNotificationPublishers.EMAIL.getPublisherDescription()); Assert.assertEquals(SendMailPublisher.class, DefaultNotificationPublishers.EMAIL.getPublisherClass()); Assert.assertEquals("/templates/notification/publisher/email.peb", DefaultNotificationPublishers.EMAIL.getPublisherTemplateFile()); - Assert.assertEquals(MediaType.TEXT_PLAIN, DefaultNotificationPublishers.EMAIL.getTemplateMimeType()); + Assert.assertEquals("text/plain; charset=utf-8", DefaultNotificationPublishers.EMAIL.getTemplateMimeType()); Assert.assertTrue(DefaultNotificationPublishers.EMAIL.isDefaultPublisher()); } From e7eda8c6f7b5be538c7f64cd6790e9b80cf67af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Thu, 12 Sep 2024 15:51:11 +0200 Subject: [PATCH 145/429] Fixed tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../org/dependencytrack/resources/v1/BomResourceTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java index 48b5015910..e99a365dae 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java @@ -252,7 +252,7 @@ public void exportProjectAsCycloneDxInventoryTest() { "name": "projectSupplier" }, "name": "acme-app", - "version": "SNAPSHOT" + "version": "" }, "manufacture": { "name": "projectManufacturer" @@ -492,7 +492,7 @@ public void exportProjectAsCycloneDxInventoryWithVulnerabilitiesTest() { "type": "application", "bom-ref": "${json-unit.matches:projectUuid}", "name": "acme-app", - "version": "SNAPSHOT" + "version": "" }, "tools": [ { @@ -686,7 +686,7 @@ public void exportProjectAsCycloneDxVdrTest() { "type": "application", "bom-ref": "${json-unit.matches:projectUuid}", "name": "acme-app", - "version": "SNAPSHOT" + "version": "" }, "tools": [ { From cdaf77762f7addbdc6eca202eaa21b8565dfd4fb Mon Sep 17 00:00:00 2001 From: nscuro Date: Tue, 10 Sep 2024 21:02:22 +0200 Subject: [PATCH 146/429] Add `AUTHOR` -> `AUTHORS` migration Required by https://github.com/DependencyTrack/dependency-track/pull/3969 Signed-off-by: nscuro --- .../upgrade/v4120/v4120Updater.java | 128 +++++++++++++++++- 1 file changed, 124 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java b/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java index 0b6615c481..a8dc36f363 100644 --- a/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java +++ b/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java @@ -24,6 +24,7 @@ import alpine.server.util.DbUtil; import org.dependencytrack.model.BomValidationMode; +import jakarta.json.Json; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -46,6 +47,8 @@ public void executeUpgrade(final AlpineQueryManager qm, final Connection connect removeExperimentalBomUploadProcessingV2ConfigProperty(connection); migrateBomValidationConfigProperty(connection); extendTeamNameColumnMaxLength(connection); + migrateAuthorToAuthors(connection); + dropAuthorColumns(connection); } private static void removeExperimentalBomUploadProcessingV2ConfigProperty(final Connection connection) throws SQLException { @@ -156,13 +159,130 @@ private void extendTeamNameColumnMaxLength(final Connection connection) throws S """); } else if (DbUtil.isMysql()) { stmt.executeUpdate(""" - ALTER TABLE "TEAM" MODIFY "NAME" VARCHAR(255) NOT NULL - """); + ALTER TABLE "TEAM" MODIFY "NAME" VARCHAR(255) NOT NULL + """); } else { stmt.executeUpdate(""" - ALTER TABLE "TEAM" ALTER COLUMN "NAME" TYPE VARCHAR(255) - """); + ALTER TABLE "TEAM" ALTER COLUMN "NAME" TYPE VARCHAR(255) + """); + } + } + } + + private void migrateAuthorToAuthors(final Connection connection) throws SQLException { + LOGGER.info("Migrating PROJECT.AUTHOR and COMPONENT.AUTHOR to PROJECT.AUTHORS and COMPONENT.AUTHORS"); + + // MSSQL did not have native JSON functions until version 2022. + // Since we have to support versions earlier than that, the migration + // requires a more procedural approach. + if (DbUtil.isMssql()) { + migrateAuthorToAuthorsMssql(connection); + return; + } + + try (final Statement stmt = connection.createStatement()) { + if (DbUtil.isH2()) { + stmt.executeUpdate(""" + UPDATE "PROJECT" + SET "AUTHORS" = JSON_ARRAY(JSON_OBJECT('name': "AUTHOR")) + WHERE "AUTHOR" IS NOT NULL + """); + stmt.executeUpdate(""" + UPDATE "COMPONENT" + SET "AUTHORS" = JSON_ARRAY(JSON_OBJECT('name': "AUTHOR")) + WHERE "AUTHOR" IS NOT NULL + """); + } else if (DbUtil.isMysql()) { + stmt.executeUpdate(""" + UPDATE "PROJECT" + SET "AUTHORS" = JSON_ARRAY(JSON_OBJECT('name', "AUTHOR")) + WHERE "AUTHOR" IS NOT NULL + """); + stmt.executeUpdate(""" + UPDATE "COMPONENT" + SET "AUTHORS" = JSON_ARRAY(JSON_OBJECT('name', "AUTHOR")) + WHERE "AUTHOR" IS NOT NULL + """); + } else if (DbUtil.isPostgreSQL()) { + stmt.executeUpdate(""" + UPDATE "PROJECT" + SET "AUTHORS" = JSON_BUILD_ARRAY(JSON_BUILD_OBJECT('name', "AUTHOR"))::TEXT + WHERE "AUTHOR" IS NOT NULL + """); + stmt.executeUpdate(""" + UPDATE "COMPONENT" + SET "AUTHORS" = JSON_BUILD_ARRAY(JSON_BUILD_OBJECT('name', "AUTHOR"))::TEXT + WHERE "AUTHOR" IS NOT NULL + """); + } else { + throw new IllegalStateException("Unrecognized database type"); + } + } + } + + private void migrateAuthorToAuthorsMssql(final Connection connection) throws SQLException { + migrateAuthorToAuthorsMssqlForTable(connection, "PROJECT"); + migrateAuthorToAuthorsMssqlForTable(connection, "COMPONENT"); + } + + private void migrateAuthorToAuthorsMssqlForTable( + final Connection connection, + final String tableName) throws SQLException { + try (final PreparedStatement selectStatement = connection.prepareStatement(""" + SELECT "ID" + , "AUTHOR" + FROM "%s" + WHERE "AUTHOR" IS NOT NULL + AND "AUTHORS" IS NULL + """.formatted(tableName)); + final PreparedStatement updateStatement = connection.prepareStatement(""" + UPDATE "%s" + SET "AUTHORS" = ? + WHERE "ID" = ? + """.formatted(tableName))) { + int batchSize = 0, numBatches = 0, numUpdates = 0; + final ResultSet rs = selectStatement.executeQuery(); + while (rs.next()) { + final long id = rs.getLong(1); + final String author = rs.getString(2); + final String authors = Json.createArrayBuilder() + .add(Json.createObjectBuilder() + .add("name", author)) + .build() + .toString(); + + updateStatement.setString(1, authors); + updateStatement.setLong(2, id); + updateStatement.addBatch(); + if (++batchSize == 500) { + updateStatement.executeBatch(); + numUpdates += batchSize; + numBatches++; + batchSize = 0; + } + } + + if (batchSize > 0) { + updateStatement.executeBatch(); + numUpdates += batchSize; + numBatches++; } + + LOGGER.info("Updated %d %s records in %d batches" + .formatted(numUpdates, tableName, numBatches)); + } + } + + private void dropAuthorColumns(final Connection connection) throws SQLException { + LOGGER.info("Dropping PROJECT.AUTHOR and COMPONENT.AUTHOR columns"); + + try (final Statement stmt = connection.createStatement()) { + stmt.executeUpdate(""" + ALTER TABLE "PROJECT" DROP COLUMN "AUTHOR" + """); + stmt.executeUpdate(""" + ALTER TABLE "COMPONENT" DROP COLUMN "AUTHOR" + """); } } From dd85630cd478a35316ecff32c238637833181a4b Mon Sep 17 00:00:00 2001 From: nscuro Date: Thu, 12 Sep 2024 19:30:07 +0200 Subject: [PATCH 147/429] Fix `docker-compose.mssql.yml` Signed-off-by: nscuro --- dev/docker-compose.mssql.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dev/docker-compose.mssql.yml b/dev/docker-compose.mssql.yml index 1257b99c13..be6ff379bd 100644 --- a/dev/docker-compose.mssql.yml +++ b/dev/docker-compose.mssql.yml @@ -33,7 +33,7 @@ services: ACCEPT_EULA: "Y" MSSQL_SA_PASSWORD: "DTrack1234#" healthcheck: - test: /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$$MSSQL_SA_PASSWORD" -Q "SELECT 1" -b -o /dev/null + test: /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P "$$MSSQL_SA_PASSWORD" -Q "SELECT 1" -b -o /dev/null interval: 15s timeout: 3s retries: 10 @@ -51,7 +51,8 @@ services: mssql: condition: service_healthy command: - - /opt/mssql-tools/bin/sqlcmd + - /opt/mssql-tools18/bin/sqlcmd + - -C - -S - mssql - -U From e61c561c264782c81184fa5d5d081aec3571abbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Fri, 13 Sep 2024 10:16:14 +0200 Subject: [PATCH 148/429] Changed so, html rendering should also work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../notification/publisher/SendMailPublisher.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/notification/publisher/SendMailPublisher.java b/src/main/java/org/dependencytrack/notification/publisher/SendMailPublisher.java index a515ca9b52..c83c38fb65 100644 --- a/src/main/java/org/dependencytrack/notification/publisher/SendMailPublisher.java +++ b/src/main/java/org/dependencytrack/notification/publisher/SendMailPublisher.java @@ -35,6 +35,8 @@ import jakarta.json.JsonObject; import jakarta.json.JsonString; + +import jakarta.ws.rs.core.MediaType; import java.io.IOException; import java.util.Arrays; import java.util.Collections; @@ -137,13 +139,14 @@ private void sendNotification(final PublishContext ctx, Notification notificatio LOGGER.error("Failed to decrypt SMTP password (%s)".formatted(ctx), e); return; } + String unescapedContent = StringEscapeUtils.unescapeHtml4(content); try { final SendMail sendMail = new SendMail() .from(smtpFrom) .to(destinations) .subject(emailSubjectPrefix + " " + notification.getTitle()) - .body(StringEscapeUtils.unescapeHtml4(content)) + .body(mimeType == MediaType.TEXT_HTML ? StringEscapeUtils.escapeHtml4(unescapedContent): unescapedContent) .bodyMimeType(mimeType) .host(smtpHostname) .port(smtpPort) From c46a7c266d526872363957976aaaae832a71d889 Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Fri, 13 Sep 2024 16:32:56 +0100 Subject: [PATCH 149/429] add new licence list Signed-off-by: Ross Murphy --- .../license-list-data/json/details/0BSD.json | 4 +- .../json/details/3D-Slicer-1.0.json | 16 +- .../license-list-data/json/details/AAL.json | 2 +- .../license-list-data/json/details/ADSL.json | 2 +- .../json/details/AFL-1.1.json | 20 +- .../json/details/AFL-1.2.json | 20 +- .../json/details/AFL-2.0.json | 2 +- .../json/details/AFL-2.1.json | 6 +- .../json/details/AFL-3.0.json | 4 +- .../json/details/AGPL-1.0-only.json | 2 +- .../json/details/AGPL-1.0-or-later.json | 2 +- .../json/details/AGPL-1.0.json | 2 +- .../json/details/AGPL-3.0-only.json | 4 +- .../json/details/AGPL-3.0-or-later.json | 20 +- .../json/details/AGPL-3.0.json | 4 +- .../json/details/AMD-newlib.json | 2 +- .../json/details/AMDPLPA.json | 2 +- .../json/details/AML-glslang.json | 4 +- .../license-list-data/json/details/AML.json | 2 +- .../license-list-data/json/details/AMPAS.json | 2 +- .../json/details/ANTLR-PD-fallback.json | 2 +- .../json/details/ANTLR-PD.json | 2 +- .../json/details/APAFML.json | 2 +- .../json/details/APL-1.0.json | 2 +- .../json/details/APSL-1.0.json | 2 +- .../json/details/APSL-1.1.json | 2 +- .../json/details/APSL-1.2.json | 2 +- .../json/details/APSL-2.0.json | 2 +- .../json/details/ASWF-Digital-Assets-1.0.json | 2 +- .../json/details/ASWF-Digital-Assets-1.1.json | 2 +- .../json/details/Abstyles.json | 2 +- .../json/details/AdaCore-doc.json | 18 +- .../json/details/Adobe-2006.json | 2 +- .../details/Adobe-Display-PostScript.json | 2 +- .../json/details/Adobe-Glyph.json | 2 +- .../json/details/Adobe-Utopia.json | 2 +- .../json/details/Afmparse.json | 2 +- .../json/details/Aladdin.json | 2 +- .../json/details/Apache-1.0.json | 2 +- .../json/details/Apache-1.1.json | 4 +- .../json/details/Apache-2.0.json | 4 +- .../json/details/App-s2p.json | 2 +- .../json/details/Arphic-1999.json | 2 +- .../json/details/Artistic-1.0-Perl.json | 2 +- .../json/details/Artistic-1.0-cl8.json | 2 +- .../json/details/Artistic-1.0.json | 2 +- .../json/details/Artistic-2.0.json | 22 +- .../json/details/BSD-1-Clause.json | 2 +- .../json/details/BSD-2-Clause-Darwin.json | 2 +- .../json/details/BSD-2-Clause-FreeBSD.json | 2 +- .../json/details/BSD-2-Clause-NetBSD.json | 2 +- .../json/details/BSD-2-Clause-Patent.json | 2 +- .../json/details/BSD-2-Clause-Views.json | 18 +- .../details/BSD-2-Clause-first-lines.json | 4 +- .../json/details/BSD-2-Clause.json | 2 +- .../details/BSD-3-Clause-Attribution.json | 2 +- .../json/details/BSD-3-Clause-Clear.json | 2 +- .../json/details/BSD-3-Clause-HP.json | 2 +- .../json/details/BSD-3-Clause-LBNL.json | 2 +- .../details/BSD-3-Clause-Modification.json | 2 +- .../BSD-3-Clause-No-Military-License.json | 4 +- .../BSD-3-Clause-No-Nuclear-License-2014.json | 2 +- .../BSD-3-Clause-No-Nuclear-License.json | 2 +- .../BSD-3-Clause-No-Nuclear-Warranty.json | 2 +- .../json/details/BSD-3-Clause-Open-MPI.json | 12 +- .../json/details/BSD-3-Clause-Sun.json | 2 +- .../json/details/BSD-3-Clause-acpica.json | 2 +- .../json/details/BSD-3-Clause-flex.json | 2 +- .../json/details/BSD-3-Clause.json | 18 +- .../json/details/BSD-4-Clause-Shortened.json | 2 +- .../json/details/BSD-4-Clause-UC.json | 2 +- .../json/details/BSD-4-Clause.json | 2 +- .../json/details/BSD-4.3RENO.json | 4 +- .../json/details/BSD-4.3TAHOE.json | 12 +- .../BSD-Advertising-Acknowledgement.json | 2 +- .../BSD-Attribution-HPND-disclaimer.json | 2 +- .../json/details/BSD-Inferno-Nettverk.json | 2 +- .../json/details/BSD-Protection.json | 2 +- .../json/details/BSD-Source-Code.json | 2 +- .../details/BSD-Source-beginning-file.json | 2 +- .../json/details/BSD-Systemics-W3Works.json | 2 +- .../json/details/BSD-Systemics.json | 2 +- .../json/details/BSL-1.0.json | 4 +- .../json/details/BUSL-1.1.json | 2 +- .../json/details/Baekmuk.json | 2 +- .../json/details/Bahyph.json | 2 +- .../license-list-data/json/details/Barr.json | 2 +- .../json/details/Beerware.json | 4 +- .../json/details/BitTorrent-1.0.json | 2 +- .../json/details/BitTorrent-1.1.json | 2 +- .../json/details/Bitstream-Charter.json | 4 +- .../json/details/Bitstream-Vera.json | 20 +- .../json/details/BlueOak-1.0.0.json | 2 +- .../json/details/Boehm-GC.json | 18 +- .../json/details/Borceux.json | 2 +- .../json/details/Brian-Gladman-2-Clause.json | 4 +- .../json/details/Brian-Gladman-3-Clause.json | 2 +- .../json/details/C-UDA-1.0.json | 4 +- .../CAL-1.0-Combined-Work-Exception.json | 4 +- .../json/details/CAL-1.0.json | 20 +- .../json/details/CATOSL-1.1.json | 2 +- .../json/details/CC-BY-1.0.json | 2 +- .../json/details/CC-BY-2.0.json | 2 +- .../json/details/CC-BY-2.5-AU.json | 2 +- .../json/details/CC-BY-2.5.json | 2 +- .../json/details/CC-BY-3.0-AT.json | 2 +- .../json/details/CC-BY-3.0-AU.json | 2 +- .../json/details/CC-BY-3.0-DE.json | 2 +- .../json/details/CC-BY-3.0-IGO.json | 2 +- .../json/details/CC-BY-3.0-NL.json | 2 +- .../json/details/CC-BY-3.0-US.json | 2 +- .../json/details/CC-BY-3.0.json | 2 +- .../json/details/CC-BY-4.0.json | 2 +- .../json/details/CC-BY-NC-1.0.json | 2 +- .../json/details/CC-BY-NC-2.0.json | 2 +- .../json/details/CC-BY-NC-2.5.json | 2 +- .../json/details/CC-BY-NC-3.0-DE.json | 2 +- .../json/details/CC-BY-NC-3.0.json | 2 +- .../json/details/CC-BY-NC-4.0.json | 2 +- .../json/details/CC-BY-NC-ND-1.0.json | 2 +- .../json/details/CC-BY-NC-ND-2.0.json | 2 +- .../json/details/CC-BY-NC-ND-2.5.json | 2 +- .../json/details/CC-BY-NC-ND-3.0-DE.json | 2 +- .../json/details/CC-BY-NC-ND-3.0-IGO.json | 2 +- .../json/details/CC-BY-NC-ND-3.0.json | 2 +- .../json/details/CC-BY-NC-ND-4.0.json | 2 +- .../json/details/CC-BY-NC-SA-1.0.json | 2 +- .../json/details/CC-BY-NC-SA-2.0-DE.json | 2 +- .../json/details/CC-BY-NC-SA-2.0-FR.json | 2 +- .../json/details/CC-BY-NC-SA-2.0-UK.json | 2 +- .../json/details/CC-BY-NC-SA-2.0.json | 2 +- .../json/details/CC-BY-NC-SA-2.5.json | 2 +- .../json/details/CC-BY-NC-SA-3.0-DE.json | 2 +- .../json/details/CC-BY-NC-SA-3.0-IGO.json | 2 +- .../json/details/CC-BY-NC-SA-3.0.json | 2 +- .../json/details/CC-BY-NC-SA-4.0.json | 2 +- .../json/details/CC-BY-ND-1.0.json | 2 +- .../json/details/CC-BY-ND-2.0.json | 2 +- .../json/details/CC-BY-ND-2.5.json | 2 +- .../json/details/CC-BY-ND-3.0-DE.json | 2 +- .../json/details/CC-BY-ND-3.0.json | 2 +- .../json/details/CC-BY-ND-4.0.json | 2 +- .../json/details/CC-BY-SA-1.0.json | 2 +- .../json/details/CC-BY-SA-2.0-UK.json | 2 +- .../json/details/CC-BY-SA-2.0.json | 2 +- .../json/details/CC-BY-SA-2.1-JP.json | 2 +- .../json/details/CC-BY-SA-2.5.json | 2 +- .../json/details/CC-BY-SA-3.0-AT.json | 2 +- .../json/details/CC-BY-SA-3.0-DE.json | 2 +- .../json/details/CC-BY-SA-3.0-IGO.json | 2 +- .../json/details/CC-BY-SA-3.0.json | 2 +- .../json/details/CC-BY-SA-4.0.json | 2 +- .../json/details/CC-PDDC.json | 2 +- .../json/details/CC0-1.0.json | 2 +- .../json/details/CDDL-1.0.json | 2 +- .../json/details/CDDL-1.1.json | 4 +- .../json/details/CDL-1.0.json | 18 +- .../json/details/CDLA-Permissive-1.0.json | 2 +- .../json/details/CDLA-Permissive-2.0.json | 2 +- .../json/details/CDLA-Sharing-1.0.json | 2 +- .../json/details/CECILL-1.0.json | 2 +- .../json/details/CECILL-1.1.json | 2 +- .../json/details/CECILL-2.0.json | 2 +- .../json/details/CECILL-2.1.json | 2 +- .../json/details/CECILL-B.json | 2 +- .../json/details/CECILL-C.json | 2 +- .../json/details/CERN-OHL-1.1.json | 2 +- .../json/details/CERN-OHL-1.2.json | 2 +- .../json/details/CERN-OHL-P-2.0.json | 2 +- .../json/details/CERN-OHL-S-2.0.json | 2 +- .../json/details/CERN-OHL-W-2.0.json | 2 +- .../json/details/CFITSIO.json | 12 +- .../json/details/CMU-Mach-nodoc.json | 4 +- .../json/details/CMU-Mach.json | 2 +- .../json/details/CNRI-Jython.json | 2 +- .../details/CNRI-Python-GPL-Compatible.json | 2 +- .../json/details/CNRI-Python.json | 2 +- .../json/details/COIL-1.0.json | 2 +- .../json/details/CPAL-1.0.json | 2 +- .../json/details/CPL-1.0.json | 2 +- .../json/details/CPOL-1.02.json | 2 +- .../json/details/CUA-OPL-1.0.json | 2 +- .../json/details/Caldera-no-preamble.json | 2 +- .../json/details/Caldera.json | 2 +- .../json/details/Catharon.json | 2 +- .../json/details/ClArtistic.json | 12 +- .../license-list-data/json/details/Clips.json | 2 +- .../json/details/Community-Spec-1.0.json | 2 +- .../json/details/Condor-1.1.json | 20 +- .../json/details/Cornell-Lossless-JPEG.json | 14 +- .../json/details/Cronyx.json | 20 +- .../json/details/Crossword.json | 2 +- .../json/details/CrystalStacker.json | 2 +- .../license-list-data/json/details/Cube.json | 2 +- .../json/details/D-FSL-1.0.json | 48 +- .../json/details/DEC-3-Clause.json | 2 +- .../json/details/DL-DE-BY-2.0.json | 2 +- .../json/details/DL-DE-ZERO-2.0.json | 2 +- .../license-list-data/json/details/DOC.json | 12 +- .../json/details/DRL-1.0.json | 2 +- .../json/details/DRL-1.1.json | 2 +- .../license-list-data/json/details/DSDP.json | 2 +- .../json/details/DocBook-Schema.json | 23 + .../json/details/DocBook-XML.json | 23 + .../json/details/Dotseqn.json | 2 +- .../json/details/ECL-1.0.json | 2 +- .../json/details/ECL-2.0.json | 2 +- .../json/details/EFL-1.0.json | 4 +- .../json/details/EFL-2.0.json | 16 +- .../license-list-data/json/details/EPICS.json | 2 +- .../json/details/EPL-1.0.json | 16 +- .../json/details/EPL-2.0.json | 14 +- .../json/details/EUDatagrid.json | 4 +- .../json/details/EUPL-1.0.json | 12 +- .../json/details/EUPL-1.1.json | 6 +- .../json/details/EUPL-1.2.json | 36 +- .../json/details/Elastic-2.0.json | 4 +- .../json/details/Entessa.json | 2 +- .../json/details/ErlPL-1.1.json | 2 +- .../json/details/Eurosym.json | 2 +- .../license-list-data/json/details/FBM.json | 2 +- .../json/details/FDK-AAC.json | 4 +- .../details/FSFAP-no-warranty-disclaimer.json | 2 +- .../license-list-data/json/details/FSFAP.json | 2 +- .../license-list-data/json/details/FSFUL.json | 2 +- .../json/details/FSFULLR.json | 2 +- .../json/details/FSFULLRWD.json | 2 +- .../license-list-data/json/details/FTL.json | 22 +- .../license-list-data/json/details/Fair.json | 4 +- .../json/details/Ferguson-Twofish.json | 2 +- .../json/details/Frameworx-1.0.json | 2 +- .../json/details/FreeBSD-DOC.json | 2 +- .../json/details/FreeImage.json | 2 +- .../json/details/Furuseth.json | 2 +- .../json/details/GCR-docs.json | 2 +- .../license-list-data/json/details/GD.json | 2 +- .../details/GFDL-1.1-invariants-only.json | 2 +- .../details/GFDL-1.1-invariants-or-later.json | 2 +- .../details/GFDL-1.1-no-invariants-only.json | 2 +- .../GFDL-1.1-no-invariants-or-later.json | 2 +- .../json/details/GFDL-1.1-only.json | 2 +- .../json/details/GFDL-1.1-or-later.json | 2 +- .../json/details/GFDL-1.1.json | 2 +- .../details/GFDL-1.2-invariants-only.json | 2 +- .../details/GFDL-1.2-invariants-or-later.json | 2 +- .../details/GFDL-1.2-no-invariants-only.json | 2 +- .../GFDL-1.2-no-invariants-or-later.json | 2 +- .../json/details/GFDL-1.2-only.json | 2 +- .../json/details/GFDL-1.2-or-later.json | 2 +- .../json/details/GFDL-1.2.json | 2 +- .../details/GFDL-1.3-invariants-only.json | 2 +- .../details/GFDL-1.3-invariants-or-later.json | 2 +- .../details/GFDL-1.3-no-invariants-only.json | 2 +- .../GFDL-1.3-no-invariants-or-later.json | 2 +- .../json/details/GFDL-1.3-only.json | 2 +- .../json/details/GFDL-1.3-or-later.json | 2 +- .../json/details/GFDL-1.3.json | 2 +- .../license-list-data/json/details/GL2PS.json | 2 +- .../json/details/GLWTPL.json | 2 +- .../json/details/GPL-1.0+.json | 2 +- .../json/details/GPL-1.0-only.json | 2 +- .../json/details/GPL-1.0-or-later.json | 2 +- .../json/details/GPL-1.0.json | 2 +- .../json/details/GPL-2.0+.json | 20 +- .../json/details/GPL-2.0-only.json | 6 +- .../json/details/GPL-2.0-or-later.json | 20 +- .../details/GPL-2.0-with-GCC-exception.json | 2 +- .../GPL-2.0-with-autoconf-exception.json | 2 +- .../details/GPL-2.0-with-bison-exception.json | 2 +- .../GPL-2.0-with-classpath-exception.json | 2 +- .../details/GPL-2.0-with-font-exception.json | 2 +- .../json/details/GPL-2.0.json | 4 +- .../json/details/GPL-3.0+.json | 4 +- .../json/details/GPL-3.0-only.json | 4 +- .../json/details/GPL-3.0-or-later.json | 4 +- .../details/GPL-3.0-with-GCC-exception.json | 2 +- .../GPL-3.0-with-autoconf-exception.json | 2 +- .../json/details/GPL-3.0.json | 16 +- .../json/details/Giftware.json | 2 +- .../license-list-data/json/details/Glide.json | 2 +- .../json/details/Glulxe.json | 2 +- .../json/details/Graphics-Gems.json | 2 +- .../json/details/Gutmann.json | 2 +- .../json/details/HIDAPI.json | 23 + .../json/details/HP-1986.json | 2 +- .../json/details/HP-1989.json | 2 +- .../json/details/HPND-DEC.json | 2 +- .../details/HPND-Fenneberg-Livingston.json | 4 +- .../json/details/HPND-INRIA-IMAG.json | 2 +- .../json/details/HPND-Intel.json | 2 +- .../json/details/HPND-Kevlin-Henney.json | 2 +- .../json/details/HPND-MIT-disclaimer.json | 2 +- .../json/details/HPND-Markus-Kuhn.json | 12 +- .../json/details/HPND-Netrek.json | 11 + .../json/details/HPND-Pbmplus.json | 2 +- .../json/details/HPND-UC-export-US.json | 2 +- .../json/details/HPND-UC.json | 2 +- .../json/details/HPND-doc-sell.json | 4 +- .../json/details/HPND-doc.json | 4 +- .../HPND-export-US-acknowledgement.json | 4 +- .../json/details/HPND-export-US-modify.json | 4 +- .../json/details/HPND-export-US.json | 2 +- .../json/details/HPND-export2-US.json | 8 +- .../details/HPND-merchantability-variant.json | 2 +- .../HPND-sell-MIT-disclaimer-xserver.json | 2 +- .../json/details/HPND-sell-regexpr.json | 2 +- .../HPND-sell-variant-MIT-disclaimer-rev.json | 2 +- .../HPND-sell-variant-MIT-disclaimer.json | 2 +- .../json/details/HPND-sell-variant.json | 2 +- .../license-list-data/json/details/HPND.json | 4 +- .../json/details/HTMLTIDY.json | 2 +- .../json/details/HaskellReport.json | 2 +- .../json/details/Hippocratic-2.1.json | 4 +- .../json/details/IBM-pibs.json | 2 +- .../license-list-data/json/details/ICU.json | 2 +- .../details/IEC-Code-Components-EULA.json | 6 +- .../json/details/IJG-short.json | 2 +- .../license-list-data/json/details/IJG.json | 2 +- .../license-list-data/json/details/IPA.json | 2 +- .../json/details/IPL-1.0.json | 2 +- .../json/details/ISC-Veillard.json | 6 +- .../license-list-data/json/details/ISC.json | 22 +- .../json/details/ImageMagick.json | 2 +- .../json/details/Imlib2.json | 4 +- .../json/details/Info-ZIP.json | 2 +- .../json/details/Inner-Net-2.0.json | 16 +- .../json/details/Intel-ACPI.json | 2 +- .../license-list-data/json/details/Intel.json | 2 +- .../json/details/Interbase-1.0.json | 2 +- .../json/details/JPL-image.json | 2 +- .../license-list-data/json/details/JPNIC.json | 2 +- .../license-list-data/json/details/JSON.json | 2 +- .../license-list-data/json/details/Jam.json | 4 +- .../json/details/JasPer-2.0.json | 2 +- .../json/details/Kastrup.json | 2 +- .../json/details/Kazlib.json | 2 +- .../json/details/Knuth-CTAN.json | 2 +- .../json/details/LAL-1.2.json | 2 +- .../json/details/LAL-1.3.json | 2 +- .../json/details/LGPL-2.0+.json | 2 +- .../json/details/LGPL-2.0-only.json | 2 +- .../json/details/LGPL-2.0-or-later.json | 2 +- .../json/details/LGPL-2.0.json | 2 +- .../json/details/LGPL-2.1+.json | 4 +- .../json/details/LGPL-2.1-only.json | 4 +- .../json/details/LGPL-2.1-or-later.json | 4 +- .../json/details/LGPL-2.1.json | 16 +- .../json/details/LGPL-3.0+.json | 6 +- .../json/details/LGPL-3.0-only.json | 6 +- .../json/details/LGPL-3.0-or-later.json | 6 +- .../json/details/LGPL-3.0.json | 22 +- .../json/details/LGPLLR.json | 2 +- .../license-list-data/json/details/LOOP.json | 36 +- .../json/details/LPD-document.json | 4 +- .../json/details/LPL-1.0.json | 2 +- .../json/details/LPL-1.02.json | 16 +- .../json/details/LPPL-1.0.json | 2 +- .../json/details/LPPL-1.1.json | 2 +- .../json/details/LPPL-1.2.json | 2 +- .../json/details/LPPL-1.3a.json | 2 +- .../json/details/LPPL-1.3c.json | 16 +- .../json/details/LZMA-SDK-9.11-to-9.20.json | 4 +- .../json/details/LZMA-SDK-9.22.json | 4 +- .../details/Latex2e-translated-notice.json | 2 +- .../json/details/Latex2e.json | 2 +- .../json/details/Leptonica.json | 2 +- .../json/details/LiLiQ-P-1.1.json | 20 +- .../json/details/LiLiQ-R-1.1.json | 4 +- .../json/details/LiLiQ-Rplus-1.1.json | 20 +- .../json/details/Libpng.json | 2 +- .../json/details/Linux-OpenIB.json | 2 +- .../json/details/Linux-man-pages-1-para.json | 2 +- .../Linux-man-pages-copyleft-2-para.json | 12 +- .../details/Linux-man-pages-copyleft-var.json | 2 +- .../details/Linux-man-pages-copyleft.json | 2 +- .../json/details/Lucida-Bitmap-Fonts.json | 2 +- .../license-list-data/json/details/MIT-0.json | 18 +- .../json/details/MIT-CMU.json | 4 +- .../json/details/MIT-Festival.json | 4 +- .../json/details/MIT-Khronos-old.json | 2 +- .../json/details/MIT-Modern-Variant.json | 18 +- .../json/details/MIT-Wu.json | 2 +- .../json/details/MIT-advertising.json | 2 +- .../json/details/MIT-enna.json | 2 +- .../json/details/MIT-feh.json | 2 +- .../json/details/MIT-open-group.json | 16 +- .../json/details/MIT-testregex.json | 2 +- .../license-list-data/json/details/MIT.json | 2 +- .../json/details/MITNFA.json | 2 +- .../json/details/MMIXware.json | 2 +- .../json/details/MPEG-SSG.json | 2 +- .../json/details/MPL-1.0.json | 4 +- .../json/details/MPL-1.1.json | 26 +- .../MPL-2.0-no-copyleft-exception.json | 14 +- .../json/details/MPL-2.0.json | 26 +- .../json/details/MS-LPL.json | 18 +- .../license-list-data/json/details/MS-PL.json | 4 +- .../license-list-data/json/details/MS-RL.json | 4 +- .../license-list-data/json/details/MTLL.json | 2 +- .../Mackerras-3-Clause-acknowledgment.json | 2 +- .../json/details/Mackerras-3-Clause.json | 2 +- .../json/details/MakeIndex.json | 2 +- .../json/details/Martin-Birgmeier.json | 2 +- .../json/details/McPhee-slideshow.json | 2 +- .../json/details/Minpack.json | 12 +- .../license-list-data/json/details/MirOS.json | 2 +- .../json/details/Motosoto.json | 2 +- .../json/details/MulanPSL-1.0.json | 4 +- .../json/details/MulanPSL-2.0.json | 6 +- .../json/details/Multics.json | 2 +- .../license-list-data/json/details/Mup.json | 2 +- .../json/details/NAIST-2003.json | 12 +- .../json/details/NASA-1.3.json | 4 +- .../json/details/NBPL-1.0.json | 2 +- .../json/details/NCBI-PD.json | 22 +- .../json/details/NCGL-UK-2.0.json | 2 +- .../license-list-data/json/details/NCL.json | 2 +- .../license-list-data/json/details/NCSA.json | 16 +- .../license-list-data/json/details/NGPL.json | 2 +- .../json/details/NICTA-1.0.json | 2 +- .../json/details/NIST-PD-fallback.json | 4 +- .../json/details/NIST-PD.json | 12 +- .../json/details/NIST-Software.json | 4 +- .../json/details/NLOD-1.0.json | 2 +- .../json/details/NLOD-2.0.json | 2 +- .../license-list-data/json/details/NLPL.json | 2 +- .../license-list-data/json/details/NOSL.json | 2 +- .../json/details/NPL-1.0.json | 2 +- .../json/details/NPL-1.1.json | 2 +- .../json/details/NPOSL-3.0.json | 2 +- .../license-list-data/json/details/NRL.json | 2 +- .../license-list-data/json/details/NTP-0.json | 2 +- .../license-list-data/json/details/NTP.json | 2 +- .../json/details/Naumen.json | 2 +- .../json/details/Net-SNMP.json | 9 +- .../json/details/NetCDF.json | 2 +- .../json/details/Newsletr.json | 2 +- .../license-list-data/json/details/Nokia.json | 2 +- .../license-list-data/json/details/Noweb.json | 2 +- .../license-list-data/json/details/Nunit.json | 2 +- .../json/details/O-UDA-1.0.json | 4 +- .../license-list-data/json/details/OAR.json | 2 +- .../json/details/OCCT-PL.json | 2 +- .../json/details/OCLC-2.0.json | 4 +- .../json/details/ODC-By-1.0.json | 2 +- .../json/details/ODbL-1.0.json | 12 +- .../license-list-data/json/details/OFFIS.json | 2 +- .../json/details/OFL-1.0-RFN.json | 2 +- .../json/details/OFL-1.0-no-RFN.json | 2 +- .../json/details/OFL-1.0.json | 2 +- .../json/details/OFL-1.1-RFN.json | 4 +- .../json/details/OFL-1.1-no-RFN.json | 4 +- .../json/details/OFL-1.1.json | 16 +- .../json/details/OGC-1.0.json | 2 +- .../json/details/OGDL-Taiwan-1.0.json | 2 +- .../json/details/OGL-Canada-2.0.json | 2 +- .../json/details/OGL-UK-1.0.json | 2 +- .../json/details/OGL-UK-2.0.json | 2 +- .../json/details/OGL-UK-3.0.json | 2 +- .../license-list-data/json/details/OGTSL.json | 16 +- .../json/details/OLDAP-1.1.json | 2 +- .../json/details/OLDAP-1.2.json | 2 +- .../json/details/OLDAP-1.3.json | 2 +- .../json/details/OLDAP-1.4.json | 2 +- .../json/details/OLDAP-2.0.1.json | 2 +- .../json/details/OLDAP-2.0.json | 2 +- .../json/details/OLDAP-2.1.json | 2 +- .../json/details/OLDAP-2.2.1.json | 2 +- .../json/details/OLDAP-2.2.2.json | 2 +- .../json/details/OLDAP-2.2.json | 2 +- .../json/details/OLDAP-2.3.json | 2 +- .../json/details/OLDAP-2.4.json | 2 +- .../json/details/OLDAP-2.5.json | 2 +- .../json/details/OLDAP-2.6.json | 2 +- .../json/details/OLDAP-2.7.json | 2 +- .../json/details/OLDAP-2.8.json | 2 +- .../json/details/OLFL-1.3.json | 4 +- .../license-list-data/json/details/OML.json | 2 +- .../json/details/OPL-1.0.json | 20 +- .../json/details/OPL-UK-3.0.json | 2 +- .../json/details/OPUBL-1.0.json | 14 +- .../json/details/OSET-PL-2.1.json | 20 +- .../json/details/OSL-1.0.json | 2 +- .../json/details/OSL-1.1.json | 2 +- .../json/details/OSL-2.0.json | 2 +- .../json/details/OSL-2.1.json | 20 +- .../json/details/OSL-3.0.json | 20 +- .../json/details/OpenPBS-2.3.json | 12 +- .../json/details/OpenSSL-standalone.json | 12 +- .../json/details/OpenSSL.json | 2 +- .../json/details/OpenVision.json | 22 +- .../license-list-data/json/details/PADL.json | 2 +- .../json/details/PDDL-1.0.json | 4 +- .../json/details/PHP-3.0.json | 4 +- .../json/details/PHP-3.01.json | 2 +- .../license-list-data/json/details/PPL.json | 4 +- .../json/details/PSF-2.0.json | 2 +- .../json/details/Parity-6.0.0.json | 2 +- .../json/details/Parity-7.0.0.json | 2 +- .../license-list-data/json/details/Pixar.json | 6 +- .../json/details/Plexus.json | 2 +- .../details/PolyForm-Noncommercial-1.0.0.json | 2 +- .../PolyForm-Small-Business-1.0.0.json | 2 +- .../json/details/PostgreSQL.json | 16 +- .../json/details/Python-2.0.1.json | 18 +- .../json/details/Python-2.0.json | 2 +- .../json/details/QPL-1.0-INRIA-2004.json | 2 +- .../json/details/QPL-1.0.json | 22 +- .../license-list-data/json/details/Qhull.json | 2 +- .../json/details/RHeCos-1.1.json | 2 +- .../json/details/RPL-1.1.json | 2 +- .../json/details/RPL-1.5.json | 2 +- .../json/details/RPSL-1.0.json | 4 +- .../json/details/RSA-MD.json | 2 +- .../license-list-data/json/details/RSCPL.json | 20 +- .../license-list-data/json/details/Rdisc.json | 2 +- .../json/details/Ruby-pty.json | 44 + .../license-list-data/json/details/Ruby.json | 2 +- .../json/details/SAX-PD-2.0.json | 2 +- .../json/details/SAX-PD.json | 2 +- .../license-list-data/json/details/SCEA.json | 2 +- .../json/details/SGI-B-1.0.json | 2 +- .../json/details/SGI-B-1.1.json | 2 +- .../json/details/SGI-B-2.0.json | 2 +- .../json/details/SGI-OpenGL.json | 2 +- .../license-list-data/json/details/SGP4.json | 2 +- .../json/details/SHL-0.5.json | 2 +- .../json/details/SHL-0.51.json | 2 +- .../json/details/SISSL-1.2.json | 2 +- .../license-list-data/json/details/SISSL.json | 4 +- .../license-list-data/json/details/SL.json | 2 +- .../license-list-data/json/details/SMLNJ.json | 2 +- .../license-list-data/json/details/SMPPL.json | 2 +- .../license-list-data/json/details/SNIA.json | 2 +- .../json/details/SPL-1.0.json | 2 +- .../json/details/SSH-OpenSSH.json | 2 +- .../json/details/SSH-short.json | 22 +- .../json/details/SSLeay-standalone.json | 2 +- .../json/details/SSPL-1.0.json | 2 +- .../license-list-data/json/details/SWL.json | 2 +- .../json/details/Saxpath.json | 2 +- .../json/details/Sendmail-8.23.json | 20 +- .../json/details/Sendmail.json | 4 +- .../json/details/SimPL-2.0.json | 2 +- .../json/details/Sleepycat.json | 2 +- .../json/details/Soundex.json | 2 +- .../json/details/Spencer-86.json | 2 +- .../json/details/Spencer-94.json | 12 +- .../json/details/Spencer-99.json | 2 +- .../json/details/StandardML-NJ.json | 2 +- .../json/details/SugarCRM-1.1.3.json | 2 +- .../json/details/Sun-PPP-2000.json | 2 +- .../json/details/Sun-PPP.json | 2 +- .../json/details/SunPro.json | 4 +- .../json/details/Symlinks.json | 2 +- .../json/details/TAPR-OHL-1.0.json | 2 +- .../license-list-data/json/details/TCL.json | 4 +- .../json/details/TCP-wrappers.json | 2 +- .../json/details/TGPPL-1.0.json | 12 +- .../license-list-data/json/details/TMate.json | 2 +- .../json/details/TORQUE-1.1.json | 2 +- .../license-list-data/json/details/TOSL.json | 2 +- .../license-list-data/json/details/TPDL.json | 2 +- .../json/details/TPL-1.0.json | 2 +- .../license-list-data/json/details/TTWL.json | 4 +- .../license-list-data/json/details/TTYP0.json | 2 +- .../json/details/TU-Berlin-1.0.json | 2 +- .../json/details/TU-Berlin-2.0.json | 2 +- .../json/details/TermReadKey.json | 2 +- .../license-list-data/json/details/UCAR.json | 2 +- .../json/details/UCL-1.0.json | 2 +- .../json/details/UMich-Merit.json | 2 +- .../json/details/UPL-1.0.json | 2 +- .../json/details/URT-RLE.json | 12 +- .../json/details/Ubuntu-font-1.0.json | 34 + .../json/details/Unicode-3.0.json | 2 +- .../json/details/Unicode-DFS-2015.json | 2 +- .../json/details/Unicode-DFS-2016.json | 22 +- .../json/details/Unicode-TOU.json | 20 +- .../json/details/UnixCrypt.json | 22 +- .../json/details/Unlicense.json | 2 +- .../json/details/VOSTROM.json | 2 +- .../json/details/VSL-1.0.json | 2 +- .../license-list-data/json/details/Vim.json | 2 +- .../json/details/W3C-19980720.json | 2 +- .../json/details/W3C-20150513.json | 20 +- .../license-list-data/json/details/W3C.json | 4 +- .../license-list-data/json/details/WTFPL.json | 4 +- .../json/details/Watcom-1.0.json | 2 +- .../json/details/Widget-Workshop.json | 2 +- .../json/details/Wsuipa.json | 2 +- .../X11-distribute-modifications-variant.json | 2 +- .../json/details/X11-swapped.json | 24 + .../license-list-data/json/details/X11.json | 2 +- .../json/details/XFree86-1.1.json | 2 +- .../license-list-data/json/details/XSkat.json | 2 +- .../json/details/Xdebug-1.03.json | 2 +- .../license-list-data/json/details/Xerox.json | 2 +- .../license-list-data/json/details/Xfig.json | 18 +- .../license-list-data/json/details/Xnet.json | 2 +- .../json/details/YPL-1.0.json | 2 +- .../json/details/YPL-1.1.json | 2 +- .../json/details/ZPL-1.1.json | 2 +- .../json/details/ZPL-2.0.json | 16 +- .../json/details/ZPL-2.1.json | 2 +- .../license-list-data/json/details/Zed.json | 2 +- .../license-list-data/json/details/Zeeff.json | 2 +- .../json/details/Zend-2.0.json | 2 +- .../json/details/Zimbra-1.3.json | 2 +- .../json/details/Zimbra-1.4.json | 2 +- .../license-list-data/json/details/Zlib.json | 16 +- .../json/details/any-OSI.json | 2 +- .../json/details/bcrypt-Solar-Designer.json | 2 +- .../json/details/blessing.json | 4 +- .../json/details/bzip2-1.0.5.json | 12 +- .../json/details/bzip2-1.0.6.json | 18 +- .../json/details/check-cvs.json | 2 +- .../json/details/checkmk.json | 2 +- .../json/details/copyleft-next-0.3.0.json | 2 +- .../json/details/copyleft-next-0.3.1.json | 2 +- .../license-list-data/json/details/curl.json | 2 +- .../json/details/cve-tou.json | 2 +- .../json/details/diffmark.json | 2 +- .../license-list-data/json/details/dtoa.json | 12 +- .../json/details/dvipdfm.json | 2 +- .../json/details/eCos-2.0.json | 2 +- .../json/details/eGenix.json | 16 +- .../json/details/etalab-2.0.json | 12 +- .../license-list-data/json/details/fwlw.json | 2 +- .../json/details/gSOAP-1.3b.json | 2 +- .../json/details/gnuplot.json | 2 +- .../json/details/gtkbook.json | 12 +- .../json/details/hdparm.json | 2 +- .../json/details/iMatix.json | 2 +- .../json/details/libpng-2.0.json | 2 +- .../json/details/libselinux-1.0.json | 2 +- .../json/details/libtiff.json | 2 +- .../json/details/libutil-David-Nugent.json | 8 +- .../license-list-data/json/details/lsof.json | 2 +- .../license-list-data/json/details/magaz.json | 2 +- .../json/details/mailprio.json | 2 +- .../json/details/metamail.json | 2 +- .../json/details/mpi-permissive.json | 6 +- .../json/details/mpich2.json | 2 +- .../license-list-data/json/details/mplus.json | 2 +- .../json/details/pkgconf.json | 2 +- .../json/details/pnmstitch.json | 2 +- .../json/details/psfrag.json | 2 +- .../json/details/psutils.json | 2 +- .../json/details/python-ldap.json | 4 +- .../license-list-data/json/details/radvd.json | 2 +- .../json/details/snprintf.json | 2 +- .../json/details/softSurfer.json | 4 +- .../json/details/ssh-keyscan.json | 2 +- .../json/details/swrule.json | 2 +- .../json/details/threeparttable.json | 2 +- .../license-list-data/json/details/ulem.json | 6 +- .../license-list-data/json/details/w3m.json | 2 +- .../json/details/wxWindows.json | 2 +- .../json/details/xinetd.json | 2 +- .../details/xkeyboard-config-Zinoviev.json | 2 +- .../license-list-data/json/details/xlock.json | 2 +- .../license-list-data/json/details/xpp.json | 2 +- .../license-list-data/json/details/xzoom.json | 2 +- .../json/details/zlib-acknowledgement.json | 2 +- .../license-list-data/json/exceptions.json | 171 +- .../erlang-otp-linking-exception.json | 14 + .../json/exceptions/romic-exception.json | 16 + .../license-list-data/json/licenses.json | 1411 +++++++++-------- 669 files changed, 2504 insertions(+), 2177 deletions(-) create mode 100644 src/main/resources/license-list-data/json/details/DocBook-Schema.json create mode 100644 src/main/resources/license-list-data/json/details/DocBook-XML.json create mode 100644 src/main/resources/license-list-data/json/details/HIDAPI.json create mode 100644 src/main/resources/license-list-data/json/details/HPND-Netrek.json create mode 100644 src/main/resources/license-list-data/json/details/Ruby-pty.json create mode 100644 src/main/resources/license-list-data/json/details/Ubuntu-font-1.0.json create mode 100644 src/main/resources/license-list-data/json/details/X11-swapped.json create mode 100644 src/main/resources/license-list-data/json/exceptions/erlang-otp-linking-exception.json create mode 100644 src/main/resources/license-list-data/json/exceptions/romic-exception.json diff --git a/src/main/resources/license-list-data/json/details/0BSD.json b/src/main/resources/license-list-data/json/details/0BSD.json index 3216ee8c64..b8c70d2c32 100644 --- a/src/main/resources/license-list-data/json/details/0BSD.json +++ b/src/main/resources/license-list-data/json/details/0BSD.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/0BSD", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:29:17Z", + "timestamp": "2024-08-19T17:47:27Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "http://landley.net/toybox/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:18Z", + "timestamp": "2024-08-19T17:47:28Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/3D-Slicer-1.0.json b/src/main/resources/license-list-data/json/details/3D-Slicer-1.0.json index b2ff9973cc..74e2c5ca14 100644 --- a/src/main/resources/license-list-data/json/details/3D-Slicer-1.0.json +++ b/src/main/resources/license-list-data/json/details/3D-Slicer-1.0.json @@ -6,22 +6,22 @@ "licenseId": "3D-Slicer-1.0", "crossRef": [ { - "match": "false", - "url": "https://slicer.org/LICENSE", + "match": "true", + "url": "https://github.com/Slicer/Slicer/blob/main/License.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:08Z", + "timestamp": "2024-08-19T17:43:03Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { - "match": "true", - "url": "https://github.com/Slicer/Slicer/blob/main/License.txt", + "match": "false", + "url": "https://slicer.org/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:09Z", + "timestamp": "2024-08-19T17:43:04Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/AAL.json b/src/main/resources/license-list-data/json/details/AAL.json index d1253f2b20..36d693e0dd 100644 --- a/src/main/resources/license-list-data/json/details/AAL.json +++ b/src/main/resources/license-list-data/json/details/AAL.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/attribution", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:24:39Z", + "timestamp": "2024-08-19T17:37:36Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ADSL.json b/src/main/resources/license-list-data/json/details/ADSL.json index 7eb92e99a5..f41319f63a 100644 --- a/src/main/resources/license-list-data/json/details/ADSL.json +++ b/src/main/resources/license-list-data/json/details/ADSL.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/AmazonDigitalServicesLicense", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:16Z", + "timestamp": "2024-08-19T17:46:39Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AFL-1.1.json b/src/main/resources/license-list-data/json/details/AFL-1.1.json index 56d037b0b3..a253f48c3b 100644 --- a/src/main/resources/license-list-data/json/details/AFL-1.1.json +++ b/src/main/resources/license-list-data/json/details/AFL-1.1.json @@ -11,21 +11,21 @@ "crossRef": [ { "match": "N/A", - "url": "http://wayback.archive.org/web/20021004124254/http://www.opensource.org/licenses/academic.php", - "isValid": false, + "url": "http://opensource.linux-mirror.org/licenses/afl-1.1.txt", + "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:31:25Z", + "timestamp": "2024-08-19T17:35:11Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { - "match": "false", - "url": "http://opensource.linux-mirror.org/licenses/afl-1.1.txt", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:31:26Z", + "match": "N/A", + "url": "http://wayback.archive.org/web/20021004124254/http://www.opensource.org/licenses/academic.php", + "isValid": false, + "isLive": false, + "timestamp": "2024-08-19T17:35:11Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/AFL-1.2.json b/src/main/resources/license-list-data/json/details/AFL-1.2.json index 7dd0afeea6..425ec87d71 100644 --- a/src/main/resources/license-list-data/json/details/AFL-1.2.json +++ b/src/main/resources/license-list-data/json/details/AFL-1.2.json @@ -9,23 +9,23 @@ "licenseId": "AFL-1.2", "standardLicenseHeader": "Licensed under the Academic Free License version 1.2\n\n", "crossRef": [ - { - "match": "false", - "url": "http://opensource.linux-mirror.org/licenses/afl-1.2.txt", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:18:43Z", - "isWayBackLink": false, - "order": 0 - }, { "match": "N/A", "url": "http://wayback.archive.org/web/20021204204652/http://www.opensource.org/licenses/academic.php", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:18:45Z", + "timestamp": "2024-08-19T17:49:02Z", "isWayBackLink": false, "order": 1 + }, + { + "match": "N/A", + "url": "http://opensource.linux-mirror.org/licenses/afl-1.2.txt", + "isValid": true, + "isLive": false, + "timestamp": "2024-08-19T17:49:02Z", + "isWayBackLink": false, + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/AFL-2.0.json b/src/main/resources/license-list-data/json/details/AFL-2.0.json index a6050709af..84e2091c74 100644 --- a/src/main/resources/license-list-data/json/details/AFL-2.0.json +++ b/src/main/resources/license-list-data/json/details/AFL-2.0.json @@ -13,7 +13,7 @@ "url": "http://wayback.archive.org/web/20060924134533/http://www.opensource.org/licenses/afl-2.0.txt", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:19:57Z", + "timestamp": "2024-08-19T17:46:35Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AFL-2.1.json b/src/main/resources/license-list-data/json/details/AFL-2.1.json index 22feee3b99..6183a6d18d 100644 --- a/src/main/resources/license-list-data/json/details/AFL-2.1.json +++ b/src/main/resources/license-list-data/json/details/AFL-2.1.json @@ -9,11 +9,11 @@ "standardLicenseHeader": "Licensed under the Academic Free License version 2.1\n\n", "crossRef": [ { - "match": "false", + "match": "N/A", "url": "http://opensource.linux-mirror.org/licenses/afl-2.1.txt", "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:24:15Z", + "isLive": false, + "timestamp": "2024-08-19T17:47:01Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AFL-3.0.json b/src/main/resources/license-list-data/json/details/AFL-3.0.json index 3d1b8867f7..967d8cb249 100644 --- a/src/main/resources/license-list-data/json/details/AFL-3.0.json +++ b/src/main/resources/license-list-data/json/details/AFL-3.0.json @@ -13,7 +13,7 @@ "url": "https://opensource.org/licenses/afl-3.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:23:40Z", + "timestamp": "2024-08-19T17:39:20Z", "isWayBackLink": false, "order": 1 }, @@ -22,7 +22,7 @@ "url": "http://www.rosenlaw.com/AFL3.0.htm", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:40Z", + "timestamp": "2024-08-19T17:39:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AGPL-1.0-only.json b/src/main/resources/license-list-data/json/details/AGPL-1.0-only.json index 875f155abc..e39b1a194d 100644 --- a/src/main/resources/license-list-data/json/details/AGPL-1.0-only.json +++ b/src/main/resources/license-list-data/json/details/AGPL-1.0-only.json @@ -10,7 +10,7 @@ "url": "http://www.affero.org/oagpl.html", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:23:06Z", + "timestamp": "2024-08-19T17:35:27Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AGPL-1.0-or-later.json b/src/main/resources/license-list-data/json/details/AGPL-1.0-or-later.json index 0368800e35..c5ac4cc2bd 100644 --- a/src/main/resources/license-list-data/json/details/AGPL-1.0-or-later.json +++ b/src/main/resources/license-list-data/json/details/AGPL-1.0-or-later.json @@ -11,7 +11,7 @@ "url": "http://www.affero.org/oagpl.html", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:28:52Z", + "timestamp": "2024-08-19T17:36:09Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AGPL-1.0.json b/src/main/resources/license-list-data/json/details/AGPL-1.0.json index 89cc2a9c39..8774036374 100644 --- a/src/main/resources/license-list-data/json/details/AGPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/AGPL-1.0.json @@ -12,7 +12,7 @@ "url": "http://www.affero.org/oagpl.html", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:20:00Z", + "timestamp": "2024-08-19T17:50:17Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AGPL-3.0-only.json b/src/main/resources/license-list-data/json/details/AGPL-3.0-only.json index ee974612e1..16e5478a21 100644 --- a/src/main/resources/license-list-data/json/details/AGPL-3.0-only.json +++ b/src/main/resources/license-list-data/json/details/AGPL-3.0-only.json @@ -14,7 +14,7 @@ "url": "https://opensource.org/licenses/AGPL-3.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:19:08Z", + "timestamp": "2024-08-19T17:39:14Z", "isWayBackLink": false, "order": 1 }, @@ -23,7 +23,7 @@ "url": "https://www.gnu.org/licenses/agpl.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:08Z", + "timestamp": "2024-08-19T17:39:14Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AGPL-3.0-or-later.json b/src/main/resources/license-list-data/json/details/AGPL-3.0-or-later.json index fdd597d41d..2ebfc0f3bc 100644 --- a/src/main/resources/license-list-data/json/details/AGPL-3.0-or-later.json +++ b/src/main/resources/license-list-data/json/details/AGPL-3.0-or-later.json @@ -9,23 +9,23 @@ "licenseId": "AGPL-3.0-or-later", "standardLicenseHeader": "\u003cone line to give the program\u0027s name and a brief idea of what it does.\u003e\n\nCopyright (C) \u003cyear\u003e \u003cname of author\u003e\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program 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 Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n", "crossRef": [ - { - "match": "false", - "url": "https://www.gnu.org/licenses/agpl.txt", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:18:59Z", - "isWayBackLink": false, - "order": 0 - }, { "match": "N/A", "url": "https://opensource.org/licenses/AGPL-3.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:59Z", + "timestamp": "2024-08-19T17:40:05Z", "isWayBackLink": false, "order": 1 + }, + { + "match": "false", + "url": "https://www.gnu.org/licenses/agpl.txt", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:40:07Z", + "isWayBackLink": false, + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/AGPL-3.0.json b/src/main/resources/license-list-data/json/details/AGPL-3.0.json index e7a7ef9a2d..0312b65b72 100644 --- a/src/main/resources/license-list-data/json/details/AGPL-3.0.json +++ b/src/main/resources/license-list-data/json/details/AGPL-3.0.json @@ -13,7 +13,7 @@ "url": "https://opensource.org/licenses/AGPL-3.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:19Z", + "timestamp": "2024-08-19T17:35:14Z", "isWayBackLink": false, "order": 1 }, @@ -22,7 +22,7 @@ "url": "https://www.gnu.org/licenses/agpl.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:19Z", + "timestamp": "2024-08-19T17:35:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AMD-newlib.json b/src/main/resources/license-list-data/json/details/AMD-newlib.json index 7d67e1d2db..39d0d11779 100644 --- a/src/main/resources/license-list-data/json/details/AMD-newlib.json +++ b/src/main/resources/license-list-data/json/details/AMD-newlib.json @@ -10,7 +10,7 @@ "url": "https://sourceware.org/git/?p\u003dnewlib-cygwin.git;a\u003dblob;f\u003dnewlib/libc/sys/a29khif/_close.S;h\u003d04f52ae00de1dafbd9055ad8d73c5c697a3aae7f;hb\u003dHEAD", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:21Z", + "timestamp": "2024-08-19T17:39:07Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AMDPLPA.json b/src/main/resources/license-list-data/json/details/AMDPLPA.json index 4815a2a170..f401cad546 100644 --- a/src/main/resources/license-list-data/json/details/AMDPLPA.json +++ b/src/main/resources/license-list-data/json/details/AMDPLPA.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/AMD_plpa_map_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:26Z", + "timestamp": "2024-08-19T17:43:13Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AML-glslang.json b/src/main/resources/license-list-data/json/details/AML-glslang.json index 8e6b71d30c..24181e4db9 100644 --- a/src/main/resources/license-list-data/json/details/AML-glslang.json +++ b/src/main/resources/license-list-data/json/details/AML-glslang.json @@ -10,7 +10,7 @@ "url": "https://docs.omniverse.nvidia.com/install-guide/latest/common/licenses.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:26Z", + "timestamp": "2024-08-19T17:46:55Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://github.com/KhronosGroup/glslang/blob/main/LICENSE.txt#L949", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:27Z", + "timestamp": "2024-08-19T17:46:55Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AML.json b/src/main/resources/license-list-data/json/details/AML.json index e3cbd9b8cc..b28f0f2163 100644 --- a/src/main/resources/license-list-data/json/details/AML.json +++ b/src/main/resources/license-list-data/json/details/AML.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Apple_MIT_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:30Z", + "timestamp": "2024-08-19T17:38:13Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AMPAS.json b/src/main/resources/license-list-data/json/details/AMPAS.json index 2d7e63abbb..881778d7b8 100644 --- a/src/main/resources/license-list-data/json/details/AMPAS.json +++ b/src/main/resources/license-list-data/json/details/AMPAS.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/BSD#AMPASBSD", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:04Z", + "timestamp": "2024-08-19T17:41:43Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ANTLR-PD-fallback.json b/src/main/resources/license-list-data/json/details/ANTLR-PD-fallback.json index 0053c7a6b6..eefa2de9a0 100644 --- a/src/main/resources/license-list-data/json/details/ANTLR-PD-fallback.json +++ b/src/main/resources/license-list-data/json/details/ANTLR-PD-fallback.json @@ -11,7 +11,7 @@ "url": "http://www.antlr2.org/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:51Z", + "timestamp": "2024-08-19T17:35:26Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ANTLR-PD.json b/src/main/resources/license-list-data/json/details/ANTLR-PD.json index ece84294c8..d0854505ed 100644 --- a/src/main/resources/license-list-data/json/details/ANTLR-PD.json +++ b/src/main/resources/license-list-data/json/details/ANTLR-PD.json @@ -11,7 +11,7 @@ "url": "http://www.antlr2.org/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:29Z", + "timestamp": "2024-08-19T17:42:56Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/APAFML.json b/src/main/resources/license-list-data/json/details/APAFML.json index 2a81e54c34..74933cca46 100644 --- a/src/main/resources/license-list-data/json/details/APAFML.json +++ b/src/main/resources/license-list-data/json/details/APAFML.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/AdobePostscriptAFM", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:19Z", + "timestamp": "2024-08-19T17:49:40Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/APL-1.0.json b/src/main/resources/license-list-data/json/details/APL-1.0.json index 6d04ed4997..08194634a9 100644 --- a/src/main/resources/license-list-data/json/details/APL-1.0.json +++ b/src/main/resources/license-list-data/json/details/APL-1.0.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/APL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:22Z", + "timestamp": "2024-08-19T17:35:30Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/APSL-1.0.json b/src/main/resources/license-list-data/json/details/APSL-1.0.json index 5c2ebc9e47..7804dd3903 100644 --- a/src/main/resources/license-list-data/json/details/APSL-1.0.json +++ b/src/main/resources/license-list-data/json/details/APSL-1.0.json @@ -12,7 +12,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Apple_Public_Source_License_1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:35Z", + "timestamp": "2024-08-19T17:39:26Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/APSL-1.1.json b/src/main/resources/license-list-data/json/details/APSL-1.1.json index 54eec5a003..c592938416 100644 --- a/src/main/resources/license-list-data/json/details/APSL-1.1.json +++ b/src/main/resources/license-list-data/json/details/APSL-1.1.json @@ -11,7 +11,7 @@ "url": "http://www.opensource.apple.com/source/IOSerialFamily/IOSerialFamily-7/APPLE_LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:33Z", + "timestamp": "2024-08-19T17:38:46Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/APSL-1.2.json b/src/main/resources/license-list-data/json/details/APSL-1.2.json index 2e966c212f..7a2c29f5f6 100644 --- a/src/main/resources/license-list-data/json/details/APSL-1.2.json +++ b/src/main/resources/license-list-data/json/details/APSL-1.2.json @@ -11,7 +11,7 @@ "url": "http://www.samurajdata.se/opensource/mirror/licenses/apsl.php", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:28Z", + "timestamp": "2024-08-19T17:37:54Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/APSL-2.0.json b/src/main/resources/license-list-data/json/details/APSL-2.0.json index f0718eda73..dd526b7e2e 100644 --- a/src/main/resources/license-list-data/json/details/APSL-2.0.json +++ b/src/main/resources/license-list-data/json/details/APSL-2.0.json @@ -14,7 +14,7 @@ "url": "http://www.opensource.apple.com/license/apsl/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:23Z", + "timestamp": "2024-08-19T17:47:45Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ASWF-Digital-Assets-1.0.json b/src/main/resources/license-list-data/json/details/ASWF-Digital-Assets-1.0.json index be8a525501..bbbab16ef9 100644 --- a/src/main/resources/license-list-data/json/details/ASWF-Digital-Assets-1.0.json +++ b/src/main/resources/license-list-data/json/details/ASWF-Digital-Assets-1.0.json @@ -10,7 +10,7 @@ "url": "https://github.com/AcademySoftwareFoundation/foundation/blob/main/digital_assets/aswf_digital_assets_license_v1.0.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:01Z", + "timestamp": "2024-08-19T17:36:17Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ASWF-Digital-Assets-1.1.json b/src/main/resources/license-list-data/json/details/ASWF-Digital-Assets-1.1.json index 230905472e..ee52deda3f 100644 --- a/src/main/resources/license-list-data/json/details/ASWF-Digital-Assets-1.1.json +++ b/src/main/resources/license-list-data/json/details/ASWF-Digital-Assets-1.1.json @@ -10,7 +10,7 @@ "url": "https://github.com/AcademySoftwareFoundation/foundation/blob/main/digital_assets/aswf_digital_assets_license_v1.1.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:53Z", + "timestamp": "2024-08-19T17:35:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Abstyles.json b/src/main/resources/license-list-data/json/details/Abstyles.json index 5a16139d40..c59330f592 100644 --- a/src/main/resources/license-list-data/json/details/Abstyles.json +++ b/src/main/resources/license-list-data/json/details/Abstyles.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Abstyles", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:52Z", + "timestamp": "2024-08-19T17:36:23Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/AdaCore-doc.json b/src/main/resources/license-list-data/json/details/AdaCore-doc.json index 6776fe4dd4..2d2cd2bf31 100644 --- a/src/main/resources/license-list-data/json/details/AdaCore-doc.json +++ b/src/main/resources/license-list-data/json/details/AdaCore-doc.json @@ -7,30 +7,30 @@ "crossRef": [ { "match": "false", - "url": "https://github.com/AdaCore/xmlada/blob/master/docs/index.rst", + "url": "https://github.com/AdaCore/gnatcoll-db/blob/master/docs/index.rst", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:15Z", + "timestamp": "2024-08-19T17:39:54Z", "isWayBackLink": false, - "order": 0 + "order": 2 }, { "match": "false", - "url": "https://github.com/AdaCore/gnatcoll-db/blob/master/docs/index.rst", + "url": "https://github.com/AdaCore/gnatcoll-core/blob/master/docs/index.rst", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:16Z", + "timestamp": "2024-08-19T17:39:55Z", "isWayBackLink": false, - "order": 2 + "order": 1 }, { "match": "false", - "url": "https://github.com/AdaCore/gnatcoll-core/blob/master/docs/index.rst", + "url": "https://github.com/AdaCore/xmlada/blob/master/docs/index.rst", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:16Z", + "timestamp": "2024-08-19T17:39:55Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Adobe-2006.json b/src/main/resources/license-list-data/json/details/Adobe-2006.json index 7e8f7cfc41..22c3830b0d 100644 --- a/src/main/resources/license-list-data/json/details/Adobe-2006.json +++ b/src/main/resources/license-list-data/json/details/Adobe-2006.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/AdobeLicense", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:54Z", + "timestamp": "2024-08-19T17:35:52Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Adobe-Display-PostScript.json b/src/main/resources/license-list-data/json/details/Adobe-Display-PostScript.json index edef6830bc..931fa362b8 100644 --- a/src/main/resources/license-list-data/json/details/Adobe-Display-PostScript.json +++ b/src/main/resources/license-list-data/json/details/Adobe-Display-PostScript.json @@ -10,7 +10,7 @@ "url": "https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/COPYING?ref_type\u003dheads#L752", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:21Z", + "timestamp": "2024-08-19T17:41:48Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Adobe-Glyph.json b/src/main/resources/license-list-data/json/details/Adobe-Glyph.json index 1f93196546..a4e714a58e 100644 --- a/src/main/resources/license-list-data/json/details/Adobe-Glyph.json +++ b/src/main/resources/license-list-data/json/details/Adobe-Glyph.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/MIT#AdobeGlyph", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:08Z", + "timestamp": "2024-08-19T17:35:40Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Adobe-Utopia.json b/src/main/resources/license-list-data/json/details/Adobe-Utopia.json index 4fba66ff69..2bc2ef44e8 100644 --- a/src/main/resources/license-list-data/json/details/Adobe-Utopia.json +++ b/src/main/resources/license-list-data/json/details/Adobe-Utopia.json @@ -10,7 +10,7 @@ "url": "https://gitlab.freedesktop.org/xorg/font/adobe-utopia-100dpi/-/blob/master/COPYING?ref_type\u003dheads", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:06Z", + "timestamp": "2024-08-19T17:44:05Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Afmparse.json b/src/main/resources/license-list-data/json/details/Afmparse.json index 04040727bc..e2d4e52898 100644 --- a/src/main/resources/license-list-data/json/details/Afmparse.json +++ b/src/main/resources/license-list-data/json/details/Afmparse.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Afmparse", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:21Z", + "timestamp": "2024-08-19T17:36:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Aladdin.json b/src/main/resources/license-list-data/json/details/Aladdin.json index 012e2feb60..16ac7af7dd 100644 --- a/src/main/resources/license-list-data/json/details/Aladdin.json +++ b/src/main/resources/license-list-data/json/details/Aladdin.json @@ -12,7 +12,7 @@ "url": "http://pages.cs.wisc.edu/~ghost/doc/AFPL/6.01/Public.htm", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:40Z", + "timestamp": "2024-08-19T17:46:30Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Apache-1.0.json b/src/main/resources/license-list-data/json/details/Apache-1.0.json index 09499d04de..0acb00653f 100644 --- a/src/main/resources/license-list-data/json/details/Apache-1.0.json +++ b/src/main/resources/license-list-data/json/details/Apache-1.0.json @@ -11,7 +11,7 @@ "url": "http://www.apache.org/licenses/LICENSE-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:53Z", + "timestamp": "2024-08-19T17:37:34Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Apache-1.1.json b/src/main/resources/license-list-data/json/details/Apache-1.1.json index db26ddde3e..6dab8ff2e6 100644 --- a/src/main/resources/license-list-data/json/details/Apache-1.1.json +++ b/src/main/resources/license-list-data/json/details/Apache-1.1.json @@ -12,7 +12,7 @@ "url": "https://opensource.org/licenses/Apache-1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:31Z", + "timestamp": "2024-08-19T17:38:20Z", "isWayBackLink": false, "order": 1 }, @@ -21,7 +21,7 @@ "url": "http://apache.org/licenses/LICENSE-1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:31Z", + "timestamp": "2024-08-19T17:38:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Apache-2.0.json b/src/main/resources/license-list-data/json/details/Apache-2.0.json index a75a295b2b..58cd92ebd5 100644 --- a/src/main/resources/license-list-data/json/details/Apache-2.0.json +++ b/src/main/resources/license-list-data/json/details/Apache-2.0.json @@ -14,7 +14,7 @@ "url": "https://opensource.org/licenses/Apache-2.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:05Z", + "timestamp": "2024-08-19T17:36:01Z", "isWayBackLink": false, "order": 1 }, @@ -23,7 +23,7 @@ "url": "https://www.apache.org/licenses/LICENSE-2.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:05Z", + "timestamp": "2024-08-19T17:36:01Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/App-s2p.json b/src/main/resources/license-list-data/json/details/App-s2p.json index 5dc2a0497d..6f4b3a4847 100644 --- a/src/main/resources/license-list-data/json/details/App-s2p.json +++ b/src/main/resources/license-list-data/json/details/App-s2p.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/App-s2p", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:33Z", + "timestamp": "2024-08-19T17:37:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Arphic-1999.json b/src/main/resources/license-list-data/json/details/Arphic-1999.json index 94c2d24fef..b4f2170426 100644 --- a/src/main/resources/license-list-data/json/details/Arphic-1999.json +++ b/src/main/resources/license-list-data/json/details/Arphic-1999.json @@ -10,7 +10,7 @@ "url": "http://ftp.gnu.org/gnu/non-gnu/chinese-fonts-truetype/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:08Z", + "timestamp": "2024-08-19T17:35:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Artistic-1.0-Perl.json b/src/main/resources/license-list-data/json/details/Artistic-1.0-Perl.json index 1f02559beb..e0af400120 100644 --- a/src/main/resources/license-list-data/json/details/Artistic-1.0-Perl.json +++ b/src/main/resources/license-list-data/json/details/Artistic-1.0-Perl.json @@ -11,7 +11,7 @@ "url": "http://dev.perl.org/licenses/artistic.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:11Z", + "timestamp": "2024-08-19T17:44:04Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Artistic-1.0-cl8.json b/src/main/resources/license-list-data/json/details/Artistic-1.0-cl8.json index ab4885c1c2..cbeda38062 100644 --- a/src/main/resources/license-list-data/json/details/Artistic-1.0-cl8.json +++ b/src/main/resources/license-list-data/json/details/Artistic-1.0-cl8.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/Artistic-1.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:22:43Z", + "timestamp": "2024-08-19T17:39:51Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Artistic-1.0.json b/src/main/resources/license-list-data/json/details/Artistic-1.0.json index 4a1e4dafd1..8ba6273dfd 100644 --- a/src/main/resources/license-list-data/json/details/Artistic-1.0.json +++ b/src/main/resources/license-list-data/json/details/Artistic-1.0.json @@ -12,7 +12,7 @@ "url": "https://opensource.org/licenses/Artistic-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:23Z", + "timestamp": "2024-08-19T17:35:59Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Artistic-2.0.json b/src/main/resources/license-list-data/json/details/Artistic-2.0.json index 295cffd8a6..4159499be1 100644 --- a/src/main/resources/license-list-data/json/details/Artistic-2.0.json +++ b/src/main/resources/license-list-data/json/details/Artistic-2.0.json @@ -7,21 +7,12 @@ "licenseComments": "This license was released: 2006", "licenseId": "Artistic-2.0", "crossRef": [ - { - "match": "N/A", - "url": "https://opensource.org/licenses/artistic-license-2.0", - "isValid": true, - "isLive": false, - "timestamp": "2024-05-22T17:23:33Z", - "isWayBackLink": false, - "order": 2 - }, { "match": "false", "url": "https://www.perlfoundation.org/artistic-license-20.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:34Z", + "timestamp": "2024-08-19T17:38:04Z", "isWayBackLink": false, "order": 1 }, @@ -30,9 +21,18 @@ "url": "http://www.perlfoundation.org/artistic_license_2_0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:34Z", + "timestamp": "2024-08-19T17:38:05Z", "isWayBackLink": false, "order": 0 + }, + { + "match": "N/A", + "url": "https://opensource.org/licenses/artistic-license-2.0", + "isValid": true, + "isLive": false, + "timestamp": "2024-08-19T17:38:05Z", + "isWayBackLink": false, + "order": 2 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/BSD-1-Clause.json b/src/main/resources/license-list-data/json/details/BSD-1-Clause.json index b12ba36479..c378f4af6a 100644 --- a/src/main/resources/license-list-data/json/details/BSD-1-Clause.json +++ b/src/main/resources/license-list-data/json/details/BSD-1-Clause.json @@ -10,7 +10,7 @@ "url": "https://svnweb.freebsd.org/base/head/include/ifaddrs.h?revision\u003d326823", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:12Z", + "timestamp": "2024-08-19T17:41:00Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-2-Clause-Darwin.json b/src/main/resources/license-list-data/json/details/BSD-2-Clause-Darwin.json index 551967008e..daf7747266 100644 --- a/src/main/resources/license-list-data/json/details/BSD-2-Clause-Darwin.json +++ b/src/main/resources/license-list-data/json/details/BSD-2-Clause-Darwin.json @@ -11,7 +11,7 @@ "url": "https://github.com/file/file/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:41Z", + "timestamp": "2024-08-19T17:38:29Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-2-Clause-FreeBSD.json b/src/main/resources/license-list-data/json/details/BSD-2-Clause-FreeBSD.json index 52316aedb8..7368e408a1 100644 --- a/src/main/resources/license-list-data/json/details/BSD-2-Clause-FreeBSD.json +++ b/src/main/resources/license-list-data/json/details/BSD-2-Clause-FreeBSD.json @@ -12,7 +12,7 @@ "url": "http://www.freebsd.org/copyright/freebsd-license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:03Z", + "timestamp": "2024-08-19T17:46:50Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-2-Clause-NetBSD.json b/src/main/resources/license-list-data/json/details/BSD-2-Clause-NetBSD.json index 450fdb4f33..974ca91d4f 100644 --- a/src/main/resources/license-list-data/json/details/BSD-2-Clause-NetBSD.json +++ b/src/main/resources/license-list-data/json/details/BSD-2-Clause-NetBSD.json @@ -12,7 +12,7 @@ "url": "http://www.netbsd.org/about/redistribution.html#default", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:56Z", + "timestamp": "2024-08-19T17:40:30Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-2-Clause-Patent.json b/src/main/resources/license-list-data/json/details/BSD-2-Clause-Patent.json index 52bd49436c..36c2d64b5a 100644 --- a/src/main/resources/license-list-data/json/details/BSD-2-Clause-Patent.json +++ b/src/main/resources/license-list-data/json/details/BSD-2-Clause-Patent.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/BSDplusPatent", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:31:46Z", + "timestamp": "2024-08-19T17:35:11Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-2-Clause-Views.json b/src/main/resources/license-list-data/json/details/BSD-2-Clause-Views.json index 25c74ea7e3..7616a0772b 100644 --- a/src/main/resources/license-list-data/json/details/BSD-2-Clause-Views.json +++ b/src/main/resources/license-list-data/json/details/BSD-2-Clause-Views.json @@ -8,30 +8,30 @@ "crossRef": [ { "match": "false", - "url": "https://people.freebsd.org/~ivoras/wine/patch-wine-nvidia.sh", + "url": "http://www.freebsd.org/copyright/freebsd-license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:45Z", + "timestamp": "2024-08-19T17:38:27Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { "match": "false", - "url": "http://www.freebsd.org/copyright/freebsd-license.html", + "url": "https://github.com/protegeproject/protege/blob/master/license.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:45Z", + "timestamp": "2024-08-19T17:38:27Z", "isWayBackLink": false, - "order": 0 + "order": 2 }, { "match": "false", - "url": "https://github.com/protegeproject/protege/blob/master/license.txt", + "url": "https://people.freebsd.org/~ivoras/wine/patch-wine-nvidia.sh", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:45Z", + "timestamp": "2024-08-19T17:38:28Z", "isWayBackLink": false, - "order": 2 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/BSD-2-Clause-first-lines.json b/src/main/resources/license-list-data/json/details/BSD-2-Clause-first-lines.json index 39b99c171f..a2d5ccee8c 100644 --- a/src/main/resources/license-list-data/json/details/BSD-2-Clause-first-lines.json +++ b/src/main/resources/license-list-data/json/details/BSD-2-Clause-first-lines.json @@ -11,7 +11,7 @@ "url": "https://web.mit.edu/kerberos/krb5-1.21/doc/mitK5license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:58Z", + "timestamp": "2024-08-19T17:36:56Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L664-L690", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:59Z", + "timestamp": "2024-08-19T17:36:56Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-2-Clause.json b/src/main/resources/license-list-data/json/details/BSD-2-Clause.json index d473fc14e7..810800a416 100644 --- a/src/main/resources/license-list-data/json/details/BSD-2-Clause.json +++ b/src/main/resources/license-list-data/json/details/BSD-2-Clause.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/BSD-2-Clause", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:14Z", + "timestamp": "2024-08-19T17:37:11Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause-Attribution.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause-Attribution.json index 3d268dfe3d..ea7947ccae 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause-Attribution.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause-Attribution.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/BSD_with_Attribution", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:31Z", + "timestamp": "2024-08-19T17:35:27Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause-Clear.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause-Clear.json index cdd3e30e7e..266ab768f3 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause-Clear.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause-Clear.json @@ -12,7 +12,7 @@ "url": "http://labs.metacarta.com/license-explanation.html#license", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:23:23Z", + "timestamp": "2024-08-19T17:37:31Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause-HP.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause-HP.json index 8a6f1e5b35..efbb5ac0b8 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause-HP.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause-HP.json @@ -11,7 +11,7 @@ "url": "https://github.com/zdohnal/hplip/blob/master/COPYING#L939", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:21Z", + "timestamp": "2024-08-19T17:36:55Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause-LBNL.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause-LBNL.json index 8dcbdc7522..458557043e 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause-LBNL.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause-LBNL.json @@ -11,7 +11,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/LBNLBSD", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:17Z", + "timestamp": "2024-08-19T17:38:34Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause-Modification.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause-Modification.json index 817a1cad96..02baa10600 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause-Modification.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause-Modification.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing:BSD#Modification_Variant", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:46Z", + "timestamp": "2024-08-19T17:35:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Military-License.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Military-License.json index dddcbead9a..c8e93881f5 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Military-License.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Military-License.json @@ -11,7 +11,7 @@ "url": "https://github.com/greymass/swift-eosio/blob/master/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:33Z", + "timestamp": "2024-08-19T17:48:47Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://gitlab.syncad.com/hive/dhive/-/blob/master/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:33Z", + "timestamp": "2024-08-19T17:48:47Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Nuclear-License-2014.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Nuclear-License-2014.json index 70629ad4d1..3242f033b3 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Nuclear-License-2014.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Nuclear-License-2014.json @@ -11,7 +11,7 @@ "url": "https://java.net/projects/javaeetutorial/pages/BerkeleyLicense", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:29:06Z", + "timestamp": "2024-08-19T17:40:31Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Nuclear-License.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Nuclear-License.json index 7e220eb771..ddd9e52efd 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Nuclear-License.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Nuclear-License.json @@ -11,7 +11,7 @@ "url": "http://download.oracle.com/otn-pub/java/licenses/bsd.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:31Z", + "timestamp": "2024-08-19T17:50:07Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Nuclear-Warranty.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Nuclear-Warranty.json index f8933d6d25..6a3e43b976 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Nuclear-Warranty.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause-No-Nuclear-Warranty.json @@ -11,7 +11,7 @@ "url": "https://jogamp.org/git/?p\u003dgluegen.git;a\u003dblob_plain;f\u003dLICENSE.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:46Z", + "timestamp": "2024-08-19T17:35:22Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause-Open-MPI.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause-Open-MPI.json index cf28a142b7..5b0ecc1828 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause-Open-MPI.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause-Open-MPI.json @@ -8,21 +8,21 @@ "crossRef": [ { "match": "false", - "url": "http://www.netlib.org/lapack/LICENSE.txt", + "url": "https://www.open-mpi.org/community/license.php", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:03Z", + "timestamp": "2024-08-19T17:49:36Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { "match": "false", - "url": "https://www.open-mpi.org/community/license.php", + "url": "http://www.netlib.org/lapack/LICENSE.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:08Z", + "timestamp": "2024-08-19T17:49:37Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause-Sun.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause-Sun.json index ddc032104c..2969b34abe 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause-Sun.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause-Sun.json @@ -11,7 +11,7 @@ "url": "https://github.com/xmlark/msv/blob/b9316e2f2270bc1606952ea4939ec87fbba157f3/xsdlib/src/main/java/com/sun/msv/datatype/regexp/InternalImpl.java", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:44Z", + "timestamp": "2024-08-19T17:37:56Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause-acpica.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause-acpica.json index 4adb4ca8b4..ca0c662d97 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause-acpica.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause-acpica.json @@ -10,7 +10,7 @@ "url": "https://github.com/acpica/acpica/blob/master/source/common/acfileio.c#L119", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:54Z", + "timestamp": "2024-08-19T17:39:37Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause-flex.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause-flex.json index b883adc30f..5073b3015e 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause-flex.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause-flex.json @@ -10,7 +10,7 @@ "url": "https://github.com/westes/flex/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:22Z", + "timestamp": "2024-08-19T17:35:22Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-3-Clause.json b/src/main/resources/license-list-data/json/details/BSD-3-Clause.json index 40df29aa2a..6e4aa4e72b 100644 --- a/src/main/resources/license-list-data/json/details/BSD-3-Clause.json +++ b/src/main/resources/license-list-data/json/details/BSD-3-Clause.json @@ -2,28 +2,28 @@ "isDeprecatedLicenseId": false, "isFsfLibre": true, "licenseText": "Copyright (c) \u003cyear\u003e \u003cowner\u003e. \n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n", - "standardLicenseTemplate": "\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright (c) \u003cyear\u003e \u003cowner\u003e. \";match\u003d\".{0,5000}\"\u003e\u003e\n\nRedistribution and use in source and binary forms\u003c\u003cvar;name\u003d\"theme\";original\u003d\"\";match\u003d\"()|( of the theme)\"\u003e\u003e, with or without modification, \u003c\u003cvar;name\u003d\"tobe\";original\u003d\"are\";match\u003d\"are|is\"\u003e\u003e permitted provided that the following conditions are met:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.\";match\u003d\".{0,20}\"\u003e\u003e Redistributions of \u003c\u003cvar;name\u003d\"code\";original\u003d\"source code\";match\u003d\"source code|works\"\u003e\u003e must retain the \u003c\u003cvar;name\u003d\"above\";original\u003d\"above\";match\u003d\"above|original\"\u003e\u003e copyright notice, this list of conditions and the following disclaimer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.\";match\u003d\".{0,20}\"\u003e\u003e Redistributions in binary form must reproduce the \u003c\u003cvar;name\u003d\"above2\";original\u003d\"above\";match\u003d\"above|original\"\u003e\u003e copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.\";match\u003d\".{0,20}\"\u003e\u003e \u003c\u003cvar;name\u003d\"organizationClause3\";original\u003d\"Neither the name of the copyright holder nor the names of its contributors may\";match\u003d\"(The\\s+name\\s+of.+may\\s+not)|(Neither\\s+the\\s+names?\\s+of.+nor\\s+the\\s+names\\s+of\\s+its\\s+contributors\\s+may)|(Neither\\s+the\\s+names?\\s+of.+nor\\s+the\\s+names\\s+of\\s+their\\s+contributors\\s+may)|(\\s*Neither\\s+the\\s+name\\s+of.+nor\\s+the\\s+names\\s+of\\s+its\\s+authors\\s+and\\s+contributors\\s+may)|(Neither\\s+the\\s+name\\s+of.+nor\\s+the\\s+names\\s+of\\s+(specific\\s+)?contributors,?\\s+may)|(Neither\\s+the\\s+name.+nor\\s+the\\s+names\\s+of\\s+contributors\\s+may)|(The\\s+names\\s+of\\s+its\\s+contributors\\s+may\\s+not)|(The\\s+names\\s+of\\s+any\\s+contributors\\s+may\\s+not)|(The\\s+names\\s+of\\s+the\\s+contributors\\s+may\\s+not)|(None\\s+of\\s+the\\s+names\\s+of.+and\\s+any\\s+contributors\\s+may)|(Neither\\s+my\\s+name.+nor\\s+the\\s+names\\s+of\\s+contributors\\s+to\\s+this\\s+code\\s+may)\"\u003e\u003e be used to endorse or promote products derived from this \u003c\u003cvar;name\u003d\"software\";original\u003d\"software\";match\u003d\"software|work\"\u003e\u003e without specific prior written permission.\n\nTHIS \u003c\u003cvar;name\u003d\"software2\";original\u003d\"SOFTWARE\";match\u003d\"(SOFTWARE)|(THEME)\"\u003e\u003e IS PROVIDED \u003c\u003cvar;name\u003d\"copyrightHolderAsIs\";original\u003d\"BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\";match\u003d\".*\"\u003e\u003e \"AS IS\" AND ANY \u003c\u003cvar;name\u003d\"express\";original\u003d\"EXPRESS\";match\u003d\"EXPRESS(ED)?\"\u003e\u003e OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL \u003c\u003cvar;name\u003d\"copyrightHolderLiability\";original\u003d\"THE COPYRIGHT HOLDER OR CONTRIBUTORS\";match\u003d\".+\"\u003e\u003e BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS \u003c\u003cvar;name\u003d\"software3\";original\u003d\"SOFTWARE\";match\u003d\"(SOFTWARE)|(THEME)\"\u003e\u003e , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n", + "standardLicenseTemplate": "\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright (c) \u003cyear\u003e \u003cowner\u003e. \";match\u003d\".{0,5000}\"\u003e\u003e\n\nRedistribution and use in source and binary forms\u003c\u003cvar;name\u003d\"theme\";original\u003d\"\";match\u003d\"()|( of the theme)\"\u003e\u003e, with or without modification, \u003c\u003cvar;name\u003d\"tobe\";original\u003d\"are\";match\u003d\"are|is\"\u003e\u003e permitted provided that the following conditions are met:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.\";match\u003d\".{0,20}\"\u003e\u003e Redistributions of \u003c\u003cvar;name\u003d\"code\";original\u003d\"source code\";match\u003d\"source code|works\"\u003e\u003e must retain the \u003c\u003cvar;name\u003d\"above\";original\u003d\"above\";match\u003d\"above|original\"\u003e\u003e copyright notice, this list of conditions and the following disclaimer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.\";match\u003d\".{0,20}\"\u003e\u003e Redistributions in binary form must reproduce the \u003c\u003cvar;name\u003d\"above2\";original\u003d\"above\";match\u003d\"above|original\"\u003e\u003e copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.\";match\u003d\".{0,20}\"\u003e\u003e \u003c\u003cvar;name\u003d\"organizationClause3\";original\u003d\"Neither the name of the copyright holder nor the names of its contributors may\";match\u003d\"(The\\s+name\\s+of.+may\\s+not)|(Neither\\s+the\\s+names?\\s+of.+nor\\s+the\\s+names\\s+of\\s+its\\s+contributors\\s+may)|(Neither\\s+the\\s+names?\\s+of.+nor\\s+the\\s+names\\s+of\\s+their\\s+contributors\\s+may)|(\\s*Neither\\s+the\\s+name\\s+of.+nor\\s+the\\s+names\\s+of\\s+its\\s+authors\\s+and\\s+contributors\\s+may)|(Neither\\s+the\\s+name\\s+of.+nor\\s+the\\s+names\\s+of\\s+(specific\\s+)?contributors,?\\s+may)|(Neither\\s+the\\s+name.+nor\\s+the\\s+names\\s+of\\s+contributors\\s+may)|(The\\s+names\\s+of\\s+its\\s+contributors\\s+may\\s+not)|(The\\s+names\\s+of\\s+any\\s+contributors\\s+may\\s+not)|(The\\s+names\\s+of\\s+the\\s+contributors\\s+may\\s+not)|(None\\s+of\\s+the\\s+names\\s+of.+and\\s+any\\s+contributors\\s+may)|(Neither\\s+my\\s+name.+nor\\s+the\\s+names\\s+of\\s+contributors\\s+to\\s+this\\s+code\\s+may)|(Neither\\s+name\\s+of\\s+copyright\\s+holders\\s+nor\\s+the\\s+names\\s+of\\s+its\\s+contributors\\s+may)\"\u003e\u003e be used to endorse or promote products derived from this \u003c\u003cvar;name\u003d\"software\";original\u003d\"software\";match\u003d\"software|work\"\u003e\u003e without specific prior written permission.\n\nTHIS \u003c\u003cvar;name\u003d\"software2\";original\u003d\"SOFTWARE\";match\u003d\"(SOFTWARE)|(THEME)\"\u003e\u003e IS PROVIDED \u003c\u003cvar;name\u003d\"copyrightHolderAsIs\";original\u003d\"BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\";match\u003d\".*\"\u003e\u003e \"AS IS\" AND ANY \u003c\u003cvar;name\u003d\"express\";original\u003d\"EXPRESS\";match\u003d\"EXPRESS(ED)?\"\u003e\u003e OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL \u003c\u003cvar;name\u003d\"copyrightHolderLiability\";original\u003d\"THE COPYRIGHT HOLDER OR CONTRIBUTORS\";match\u003d\".+\"\u003e\u003e BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS \u003c\u003cvar;name\u003d\"software3\";original\u003d\"SOFTWARE\";match\u003d\"(SOFTWARE)|(THEME)\"\u003e\u003e , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n", "name": "BSD 3-Clause \"New\" or \"Revised\" License", "licenseComments": "Note for matching purposes, this license contains a number of equivalent variations, particularly in the third clause. See the XML file for more details. Also note that the Eclipse Distribution License - v 1.0 (EDL 1.0) is a match to BSD-3-Clause, even though it uses a different name.", "licenseId": "BSD-3-Clause", "crossRef": [ { - "match": "N/A", - "url": "https://opensource.org/licenses/BSD-3-Clause", + "match": "false", + "url": "https://www.eclipse.org/org/documents/edl-v10.php", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:18Z", + "timestamp": "2024-08-19T17:47:31Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { - "match": "false", - "url": "https://www.eclipse.org/org/documents/edl-v10.php", + "match": "N/A", + "url": "https://opensource.org/licenses/BSD-3-Clause", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:18Z", + "timestamp": "2024-08-19T17:47:31Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/BSD-4-Clause-Shortened.json b/src/main/resources/license-list-data/json/details/BSD-4-Clause-Shortened.json index 357e7a631e..405671a5d2 100644 --- a/src/main/resources/license-list-data/json/details/BSD-4-Clause-Shortened.json +++ b/src/main/resources/license-list-data/json/details/BSD-4-Clause-Shortened.json @@ -10,7 +10,7 @@ "url": "https://metadata.ftp-master.debian.org/changelogs//main/a/arpwatch/arpwatch_2.1a15-7_copyright", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:21:04Z", + "timestamp": "2024-08-19T17:37:00Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-4-Clause-UC.json b/src/main/resources/license-list-data/json/details/BSD-4-Clause-UC.json index 664ede0721..15565f4c09 100644 --- a/src/main/resources/license-list-data/json/details/BSD-4-Clause-UC.json +++ b/src/main/resources/license-list-data/json/details/BSD-4-Clause-UC.json @@ -11,7 +11,7 @@ "url": "http://www.freebsd.org/copyright/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:52Z", + "timestamp": "2024-08-19T17:35:45Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-4-Clause.json b/src/main/resources/license-list-data/json/details/BSD-4-Clause.json index c7f5f0b27c..cb26645157 100644 --- a/src/main/resources/license-list-data/json/details/BSD-4-Clause.json +++ b/src/main/resources/license-list-data/json/details/BSD-4-Clause.json @@ -12,7 +12,7 @@ "url": "http://directory.fsf.org/wiki/License:BSD_4Clause", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:58Z", + "timestamp": "2024-08-19T17:37:37Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-4.3RENO.json b/src/main/resources/license-list-data/json/details/BSD-4.3RENO.json index e175a97076..fd4cb90038 100644 --- a/src/main/resources/license-list-data/json/details/BSD-4.3RENO.json +++ b/src/main/resources/license-list-data/json/details/BSD-4.3RENO.json @@ -10,7 +10,7 @@ "url": "https://git.openldap.org/openldap/openldap/-/blob/master/COPYRIGHT#L55-63", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:10Z", + "timestamp": "2024-08-19T17:40:04Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://sourceware.org/git/?p\u003dbinutils-gdb.git;a\u003dblob;f\u003dlibiberty/strcasecmp.c;h\u003d131d81c2ce7881fa48c363dc5bf5fb302c61ce0b;hb\u003dHEAD", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:11Z", + "timestamp": "2024-08-19T17:40:04Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-4.3TAHOE.json b/src/main/resources/license-list-data/json/details/BSD-4.3TAHOE.json index f7f1b03e0f..1e85890238 100644 --- a/src/main/resources/license-list-data/json/details/BSD-4.3TAHOE.json +++ b/src/main/resources/license-list-data/json/details/BSD-4.3TAHOE.json @@ -7,21 +7,21 @@ "crossRef": [ { "match": "false", - "url": "https://github.com/389ds/389-ds-base/blob/main/ldap/include/sysexits-compat.h#L15", + "url": "https://git.savannah.gnu.org/cgit/indent.git/tree/doc/indent.texi?id\u003da74c6b4ee49397cf330b333da1042bffa60ed14f#n1788", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:26Z", + "timestamp": "2024-08-19T17:35:19Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "https://git.savannah.gnu.org/cgit/indent.git/tree/doc/indent.texi?id\u003da74c6b4ee49397cf330b333da1042bffa60ed14f#n1788", + "url": "https://github.com/389ds/389-ds-base/blob/main/ldap/include/sysexits-compat.h#L15", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:26Z", + "timestamp": "2024-08-19T17:35:20Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/BSD-Advertising-Acknowledgement.json b/src/main/resources/license-list-data/json/details/BSD-Advertising-Acknowledgement.json index 101abc2f13..bd3ece067f 100644 --- a/src/main/resources/license-list-data/json/details/BSD-Advertising-Acknowledgement.json +++ b/src/main/resources/license-list-data/json/details/BSD-Advertising-Acknowledgement.json @@ -11,7 +11,7 @@ "url": "https://github.com/python-excel/xlrd/blob/master/LICENSE#L33", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:19Z", + "timestamp": "2024-08-19T17:38:32Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-Attribution-HPND-disclaimer.json b/src/main/resources/license-list-data/json/details/BSD-Attribution-HPND-disclaimer.json index 43bed1eb8f..9bfa35337a 100644 --- a/src/main/resources/license-list-data/json/details/BSD-Attribution-HPND-disclaimer.json +++ b/src/main/resources/license-list-data/json/details/BSD-Attribution-HPND-disclaimer.json @@ -10,7 +10,7 @@ "url": "https://github.com/cyrusimap/cyrus-sasl/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:43Z", + "timestamp": "2024-08-19T17:35:31Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-Inferno-Nettverk.json b/src/main/resources/license-list-data/json/details/BSD-Inferno-Nettverk.json index 58ecac8a96..6563af39c5 100644 --- a/src/main/resources/license-list-data/json/details/BSD-Inferno-Nettverk.json +++ b/src/main/resources/license-list-data/json/details/BSD-Inferno-Nettverk.json @@ -11,7 +11,7 @@ "url": "https://www.inet.no/dante/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:35Z", + "timestamp": "2024-08-19T17:35:33Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-Protection.json b/src/main/resources/license-list-data/json/details/BSD-Protection.json index 9e81653779..f89f73e590 100644 --- a/src/main/resources/license-list-data/json/details/BSD-Protection.json +++ b/src/main/resources/license-list-data/json/details/BSD-Protection.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/BSD_Protection_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:47Z", + "timestamp": "2024-08-19T17:40:44Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-Source-Code.json b/src/main/resources/license-list-data/json/details/BSD-Source-Code.json index 8087282f25..0187e09856 100644 --- a/src/main/resources/license-list-data/json/details/BSD-Source-Code.json +++ b/src/main/resources/license-list-data/json/details/BSD-Source-Code.json @@ -10,7 +10,7 @@ "url": "https://github.com/robbiehanson/CocoaHTTPServer/blob/master/LICENSE.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:32Z", + "timestamp": "2024-08-19T17:48:19Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-Source-beginning-file.json b/src/main/resources/license-list-data/json/details/BSD-Source-beginning-file.json index b734dad258..7506078922 100644 --- a/src/main/resources/license-list-data/json/details/BSD-Source-beginning-file.json +++ b/src/main/resources/license-list-data/json/details/BSD-Source-beginning-file.json @@ -11,7 +11,7 @@ "url": "https://github.com/lattera/freebsd/blob/master/sys/cam/cam.c#L4", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:55Z", + "timestamp": "2024-08-19T17:40:31Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-Systemics-W3Works.json b/src/main/resources/license-list-data/json/details/BSD-Systemics-W3Works.json index 5f51ccbd5f..d9c24004f5 100644 --- a/src/main/resources/license-list-data/json/details/BSD-Systemics-W3Works.json +++ b/src/main/resources/license-list-data/json/details/BSD-Systemics-W3Works.json @@ -10,7 +10,7 @@ "url": "https://metacpan.org/release/DPARIS/Crypt-Blowfish-2.14/source/COPYRIGHT#L7", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:28Z", + "timestamp": "2024-08-19T17:42:00Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSD-Systemics.json b/src/main/resources/license-list-data/json/details/BSD-Systemics.json index 1767c7eabe..f795a1e516 100644 --- a/src/main/resources/license-list-data/json/details/BSD-Systemics.json +++ b/src/main/resources/license-list-data/json/details/BSD-Systemics.json @@ -10,7 +10,7 @@ "url": "https://metacpan.org/release/DPARIS/Crypt-DES-2.07/source/COPYRIGHT", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:49Z", + "timestamp": "2024-08-19T17:39:10Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BSL-1.0.json b/src/main/resources/license-list-data/json/details/BSL-1.0.json index 6a0d643bfb..b3342e529a 100644 --- a/src/main/resources/license-list-data/json/details/BSL-1.0.json +++ b/src/main/resources/license-list-data/json/details/BSL-1.0.json @@ -12,7 +12,7 @@ "url": "https://opensource.org/licenses/BSL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:21Z", + "timestamp": "2024-08-19T17:39:15Z", "isWayBackLink": false, "order": 1 }, @@ -21,7 +21,7 @@ "url": "http://www.boost.org/LICENSE_1_0.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:21Z", + "timestamp": "2024-08-19T17:39:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BUSL-1.1.json b/src/main/resources/license-list-data/json/details/BUSL-1.1.json index c899ee7c65..0cf01524ee 100644 --- a/src/main/resources/license-list-data/json/details/BUSL-1.1.json +++ b/src/main/resources/license-list-data/json/details/BUSL-1.1.json @@ -13,7 +13,7 @@ "url": "https://mariadb.com/bsl11/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:34Z", + "timestamp": "2024-08-19T17:38:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Baekmuk.json b/src/main/resources/license-list-data/json/details/Baekmuk.json index 7d512ae4a1..1130a8c39e 100644 --- a/src/main/resources/license-list-data/json/details/Baekmuk.json +++ b/src/main/resources/license-list-data/json/details/Baekmuk.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing:Baekmuk?rd\u003dLicensing/Baekmuk", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:37Z", + "timestamp": "2024-08-19T17:40:33Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Bahyph.json b/src/main/resources/license-list-data/json/details/Bahyph.json index 72c0f367a9..197cc58413 100644 --- a/src/main/resources/license-list-data/json/details/Bahyph.json +++ b/src/main/resources/license-list-data/json/details/Bahyph.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Bahyph", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:33Z", + "timestamp": "2024-08-19T17:40:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Barr.json b/src/main/resources/license-list-data/json/details/Barr.json index 448232fc2f..f8dee63b8e 100644 --- a/src/main/resources/license-list-data/json/details/Barr.json +++ b/src/main/resources/license-list-data/json/details/Barr.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Barr", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:39Z", + "timestamp": "2024-08-19T17:36:40Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Beerware.json b/src/main/resources/license-list-data/json/details/Beerware.json index e746ef27cc..8385cf6acc 100644 --- a/src/main/resources/license-list-data/json/details/Beerware.json +++ b/src/main/resources/license-list-data/json/details/Beerware.json @@ -10,7 +10,7 @@ "url": "https://people.freebsd.org/~phk/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:23Z", + "timestamp": "2024-08-19T17:48:47Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Beerware", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:23Z", + "timestamp": "2024-08-19T17:48:47Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BitTorrent-1.0.json b/src/main/resources/license-list-data/json/details/BitTorrent-1.0.json index 75751fec82..186a4b1373 100644 --- a/src/main/resources/license-list-data/json/details/BitTorrent-1.0.json +++ b/src/main/resources/license-list-data/json/details/BitTorrent-1.0.json @@ -12,7 +12,7 @@ "url": "http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1\u003d1.1\u0026r2\u003d1.1.1.1\u0026diff_format\u003ds", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:58Z", + "timestamp": "2024-08-19T17:35:37Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/BitTorrent-1.1.json b/src/main/resources/license-list-data/json/details/BitTorrent-1.1.json index 45ebce7009..14105eb9ba 100644 --- a/src/main/resources/license-list-data/json/details/BitTorrent-1.1.json +++ b/src/main/resources/license-list-data/json/details/BitTorrent-1.1.json @@ -14,7 +14,7 @@ "url": "http://directory.fsf.org/wiki/License:BitTorrentOSL1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:52Z", + "timestamp": "2024-08-19T17:45:48Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Bitstream-Charter.json b/src/main/resources/license-list-data/json/details/Bitstream-Charter.json index 311b293c98..7f20c6b8a2 100644 --- a/src/main/resources/license-list-data/json/details/Bitstream-Charter.json +++ b/src/main/resources/license-list-data/json/details/Bitstream-Charter.json @@ -10,7 +10,7 @@ "url": "https://raw.githubusercontent.com/blackhole89/notekit/master/data/fonts/Charter%20license.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:57Z", + "timestamp": "2024-08-19T17:42:43Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Charter#License_Text", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:57Z", + "timestamp": "2024-08-19T17:42:43Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Bitstream-Vera.json b/src/main/resources/license-list-data/json/details/Bitstream-Vera.json index 89f7d48241..1c06ddc678 100644 --- a/src/main/resources/license-list-data/json/details/Bitstream-Vera.json +++ b/src/main/resources/license-list-data/json/details/Bitstream-Vera.json @@ -6,22 +6,22 @@ "licenseId": "Bitstream-Vera", "crossRef": [ { - "match": "N/A", - "url": "https://web.archive.org/web/20080207013128/http://www.gnome.org/fonts/", - "isValid": false, - "isLive": false, - "timestamp": "2024-05-22T17:30:32Z", + "match": "false", + "url": "https://docubrain.com/sites/default/files/licenses/bitstream-vera.html", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:40:11Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "N/A", - "url": "https://docubrain.com/sites/default/files/licenses/bitstream-vera.html", - "isValid": true, + "url": "https://web.archive.org/web/20080207013128/http://www.gnome.org/fonts/", + "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:30:40Z", + "timestamp": "2024-08-19T17:40:11Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/BlueOak-1.0.0.json b/src/main/resources/license-list-data/json/details/BlueOak-1.0.0.json index ce5743e66e..6233f85cb5 100644 --- a/src/main/resources/license-list-data/json/details/BlueOak-1.0.0.json +++ b/src/main/resources/license-list-data/json/details/BlueOak-1.0.0.json @@ -11,7 +11,7 @@ "url": "https://blueoakcouncil.org/license/1.0.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:42Z", + "timestamp": "2024-08-19T17:48:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Boehm-GC.json b/src/main/resources/license-list-data/json/details/Boehm-GC.json index dd834500bd..076b2db7f2 100644 --- a/src/main/resources/license-list-data/json/details/Boehm-GC.json +++ b/src/main/resources/license-list-data/json/details/Boehm-GC.json @@ -7,30 +7,30 @@ "crossRef": [ { "match": "false", - "url": "https://fedoraproject.org/wiki/Licensing:MIT#Another_Minimal_variant_(found_in_libatomic_ops)", + "url": "https://github.com/ivmai/libatomic_ops/blob/master/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:43Z", + "timestamp": "2024-08-19T17:35:51Z", "isWayBackLink": false, - "order": 0 + "order": 2 }, { "match": "false", - "url": "https://github.com/ivmai/libatomic_ops/blob/master/LICENSE", + "url": "https://github.com/uim/libgcroots/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:44Z", + "timestamp": "2024-08-19T17:35:52Z", "isWayBackLink": false, - "order": 2 + "order": 1 }, { "match": "false", - "url": "https://github.com/uim/libgcroots/blob/master/COPYING", + "url": "https://fedoraproject.org/wiki/Licensing:MIT#Another_Minimal_variant_(found_in_libatomic_ops)", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:44Z", + "timestamp": "2024-08-19T17:35:52Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Borceux.json b/src/main/resources/license-list-data/json/details/Borceux.json index 96d0b17143..871293a3d4 100644 --- a/src/main/resources/license-list-data/json/details/Borceux.json +++ b/src/main/resources/license-list-data/json/details/Borceux.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Borceux", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:39Z", + "timestamp": "2024-08-19T17:47:06Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Brian-Gladman-2-Clause.json b/src/main/resources/license-list-data/json/details/Brian-Gladman-2-Clause.json index b11151207f..1329d10c05 100644 --- a/src/main/resources/license-list-data/json/details/Brian-Gladman-2-Clause.json +++ b/src/main/resources/license-list-data/json/details/Brian-Gladman-2-Clause.json @@ -11,7 +11,7 @@ "url": "https://web.mit.edu/kerberos/krb5-1.21/doc/mitK5license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:11Z", + "timestamp": "2024-08-19T17:41:45Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L140-L156", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:11Z", + "timestamp": "2024-08-19T17:41:45Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Brian-Gladman-3-Clause.json b/src/main/resources/license-list-data/json/details/Brian-Gladman-3-Clause.json index 977959ac4e..58aaba20b1 100644 --- a/src/main/resources/license-list-data/json/details/Brian-Gladman-3-Clause.json +++ b/src/main/resources/license-list-data/json/details/Brian-Gladman-3-Clause.json @@ -10,7 +10,7 @@ "url": "https://github.com/SWI-Prolog/packages-clib/blob/master/sha1/brg_endian.h", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:57Z", + "timestamp": "2024-08-19T17:38:22Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/C-UDA-1.0.json b/src/main/resources/license-list-data/json/details/C-UDA-1.0.json index 1162868791..a4ae36cb90 100644 --- a/src/main/resources/license-list-data/json/details/C-UDA-1.0.json +++ b/src/main/resources/license-list-data/json/details/C-UDA-1.0.json @@ -10,7 +10,7 @@ "url": "https://cdla.dev/computational-use-of-data-agreement-v1-0/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:32Z", + "timestamp": "2024-08-19T17:36:14Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://github.com/microsoft/Computational-Use-of-Data-Agreement/blob/master/C-UDA-1.0.md", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:32Z", + "timestamp": "2024-08-19T17:36:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CAL-1.0-Combined-Work-Exception.json b/src/main/resources/license-list-data/json/details/CAL-1.0-Combined-Work-Exception.json index 8b98344ba0..861ea6fb48 100644 --- a/src/main/resources/license-list-data/json/details/CAL-1.0-Combined-Work-Exception.json +++ b/src/main/resources/license-list-data/json/details/CAL-1.0-Combined-Work-Exception.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/CAL-1.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:20:28Z", + "timestamp": "2024-08-19T17:39:21Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "http://cryptographicautonomylicense.com/license-text.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:28Z", + "timestamp": "2024-08-19T17:39:21Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CAL-1.0.json b/src/main/resources/license-list-data/json/details/CAL-1.0.json index 66ad832312..02f1c08670 100644 --- a/src/main/resources/license-list-data/json/details/CAL-1.0.json +++ b/src/main/resources/license-list-data/json/details/CAL-1.0.json @@ -6,23 +6,23 @@ "licenseComments": "The first draft of this license was released February 2019, and the fourth revision was approved by the OSI February 2020.", "licenseId": "CAL-1.0", "crossRef": [ - { - "match": "false", - "url": "http://cryptographicautonomylicense.com/license-text.html", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:19:35Z", - "isWayBackLink": false, - "order": 0 - }, { "match": "N/A", "url": "https://opensource.org/licenses/CAL-1.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:19:35Z", + "timestamp": "2024-08-19T17:35:19Z", "isWayBackLink": false, "order": 1 + }, + { + "match": "false", + "url": "http://cryptographicautonomylicense.com/license-text.html", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:35:19Z", + "isWayBackLink": false, + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/CATOSL-1.1.json b/src/main/resources/license-list-data/json/details/CATOSL-1.1.json index 9a361a3200..60a7db1315 100644 --- a/src/main/resources/license-list-data/json/details/CATOSL-1.1.json +++ b/src/main/resources/license-list-data/json/details/CATOSL-1.1.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/CATOSL-1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:03Z", + "timestamp": "2024-08-19T17:35:56Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-1.0.json b/src/main/resources/license-list-data/json/details/CC-BY-1.0.json index 0d08a693d5..ac73afc12e 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-1.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-1.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by/1.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:35Z", + "timestamp": "2024-08-19T17:41:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-2.0.json b/src/main/resources/license-list-data/json/details/CC-BY-2.0.json index 4dd89b9243..2f8bed4f89 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-2.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-2.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by/2.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:44Z", + "timestamp": "2024-08-19T17:41:56Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-2.5-AU.json b/src/main/resources/license-list-data/json/details/CC-BY-2.5-AU.json index 32e0befc0e..33133b1bb8 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-2.5-AU.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-2.5-AU.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by/2.5/au/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:43Z", + "timestamp": "2024-08-19T17:40:43Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-2.5.json b/src/main/resources/license-list-data/json/details/CC-BY-2.5.json index f2d9c1bfef..542b9ac8b4 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-2.5.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-2.5.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by/2.5/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:14Z", + "timestamp": "2024-08-19T17:47:10Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-3.0-AT.json b/src/main/resources/license-list-data/json/details/CC-BY-3.0-AT.json index fbc83efd28..078ab2488e 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-3.0-AT.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-3.0-AT.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by/3.0/at/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:47Z", + "timestamp": "2024-08-19T17:35:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-3.0-AU.json b/src/main/resources/license-list-data/json/details/CC-BY-3.0-AU.json index ccbccf5802..3dd58e1e01 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-3.0-AU.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-3.0-AU.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by/3.0/au/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:16Z", + "timestamp": "2024-08-19T17:40:50Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-3.0-DE.json b/src/main/resources/license-list-data/json/details/CC-BY-3.0-DE.json index bbcf6bbbf6..792aff87c0 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-3.0-DE.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-3.0-DE.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by/3.0/de/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:48Z", + "timestamp": "2024-08-19T17:35:14Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-3.0-IGO.json b/src/main/resources/license-list-data/json/details/CC-BY-3.0-IGO.json index 46be42c24e..a708e944f3 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-3.0-IGO.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-3.0-IGO.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by/3.0/igo/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:31Z", + "timestamp": "2024-08-19T17:47:55Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-3.0-NL.json b/src/main/resources/license-list-data/json/details/CC-BY-3.0-NL.json index 8e8d5158b3..6c2ad382ec 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-3.0-NL.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-3.0-NL.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by/3.0/nl/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:53Z", + "timestamp": "2024-08-19T17:36:10Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-3.0-US.json b/src/main/resources/license-list-data/json/details/CC-BY-3.0-US.json index 74acbfcf8e..a00b7e2b03 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-3.0-US.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-3.0-US.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by/3.0/us/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:15Z", + "timestamp": "2024-08-19T17:40:58Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-3.0.json b/src/main/resources/license-list-data/json/details/CC-BY-3.0.json index af38c8f8de..59673c1bd4 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-3.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-3.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by/3.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:55Z", + "timestamp": "2024-08-19T17:35:54Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-4.0.json b/src/main/resources/license-list-data/json/details/CC-BY-4.0.json index 6a37c4fdae..8e25cbcd11 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-4.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-4.0.json @@ -11,7 +11,7 @@ "url": "https://creativecommons.org/licenses/by/4.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:20Z", + "timestamp": "2024-08-19T17:42:10Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-1.0.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-1.0.json index 1a2377f057..4b02e18537 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-1.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-1.0.json @@ -11,7 +11,7 @@ "url": "https://creativecommons.org/licenses/by-nc/1.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:40Z", + "timestamp": "2024-08-19T17:49:50Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-2.0.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-2.0.json index 185bbef683..7c528f55e8 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-2.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-2.0.json @@ -11,7 +11,7 @@ "url": "https://creativecommons.org/licenses/by-nc/2.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:52Z", + "timestamp": "2024-08-19T17:35:30Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-2.5.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-2.5.json index 8443e4b465..4962a10237 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-2.5.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-2.5.json @@ -11,7 +11,7 @@ "url": "https://creativecommons.org/licenses/by-nc/2.5/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:05Z", + "timestamp": "2024-08-19T17:43:08Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-3.0-DE.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-3.0-DE.json index f2e8d8fcc4..146682c5e5 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-3.0-DE.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-3.0-DE.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc/3.0/de/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:26Z", + "timestamp": "2024-08-19T17:39:53Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-3.0.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-3.0.json index 7cf3c69b1c..98c33dc86a 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-3.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-3.0.json @@ -11,7 +11,7 @@ "url": "https://creativecommons.org/licenses/by-nc/3.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:07Z", + "timestamp": "2024-08-19T17:37:14Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-4.0.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-4.0.json index 49ca4ac8a0..9089f9b977 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-4.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-4.0.json @@ -11,7 +11,7 @@ "url": "https://creativecommons.org/licenses/by-nc/4.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:58Z", + "timestamp": "2024-08-19T17:35:22Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-1.0.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-1.0.json index 8d1d2d53de..e07d2bc50f 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-1.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-1.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nd-nc/1.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:49Z", + "timestamp": "2024-08-19T17:35:32Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-2.0.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-2.0.json index a504f7e9e2..38b45e39a2 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-2.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-2.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-nd/2.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:06Z", + "timestamp": "2024-08-19T17:41:58Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-2.5.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-2.5.json index b23c75bb11..e2ec150910 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-2.5.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-2.5.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-nd/2.5/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:41Z", + "timestamp": "2024-08-19T17:42:22Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-3.0-DE.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-3.0-DE.json index 7c46a21565..d80c4dd5c9 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-3.0-DE.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-3.0-DE.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-nd/3.0/de/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:50Z", + "timestamp": "2024-08-19T17:35:48Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-3.0-IGO.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-3.0-IGO.json index f04511a3bc..dbf3ba0500 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-3.0-IGO.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-3.0-IGO.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-nd/3.0/igo/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:56Z", + "timestamp": "2024-08-19T17:36:19Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-3.0.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-3.0.json index de5a9d20a4..8b26f78ffd 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-3.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-3.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-nd/3.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:23Z", + "timestamp": "2024-08-19T17:38:34Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-4.0.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-4.0.json index a1bfacde5a..ae0a7e14ef 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-4.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-ND-4.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:05Z", + "timestamp": "2024-08-19T17:36:29Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-1.0.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-1.0.json index 51357fb6f0..7a419edf1c 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-1.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-1.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-sa/1.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:09Z", + "timestamp": "2024-08-19T17:44:06Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0-DE.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0-DE.json index d4e42f7829..914e6d716a 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0-DE.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0-DE.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-sa/2.0/de/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:55Z", + "timestamp": "2024-08-19T17:37:45Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0-FR.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0-FR.json index 4fa63e0db1..1ba1af4883 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0-FR.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0-FR.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-sa/2.0/fr/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:04Z", + "timestamp": "2024-08-19T17:36:11Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0-UK.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0-UK.json index 080ff60cf7..17d906609a 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0-UK.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0-UK.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-sa/2.0/uk/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:01Z", + "timestamp": "2024-08-19T17:35:17Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0.json index 7734736573..8851b9f16a 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-sa/2.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:05Z", + "timestamp": "2024-08-19T17:39:52Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.5.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.5.json index 658fa51dda..b372aef524 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.5.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-2.5.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-sa/2.5/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:59Z", + "timestamp": "2024-08-19T17:37:03Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-3.0-DE.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-3.0-DE.json index f1002bf049..54416abbcd 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-3.0-DE.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-3.0-DE.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-sa/3.0/de/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:27Z", + "timestamp": "2024-08-19T17:45:11Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-3.0-IGO.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-3.0-IGO.json index 657b00e6ff..14ea49d243 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-3.0-IGO.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-3.0-IGO.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-sa/3.0/igo/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:04Z", + "timestamp": "2024-08-19T17:37:26Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-3.0.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-3.0.json index d788ba1938..6d6127729e 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-3.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-3.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-sa/3.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:46Z", + "timestamp": "2024-08-19T17:37:38Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-4.0.json b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-4.0.json index 8bcb936fb8..c9f8b6f375 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-4.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-NC-SA-4.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:29Z", + "timestamp": "2024-08-19T17:44:43Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-ND-1.0.json b/src/main/resources/license-list-data/json/details/CC-BY-ND-1.0.json index ffe3edc5e6..71e134a025 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-ND-1.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-ND-1.0.json @@ -11,7 +11,7 @@ "url": "https://creativecommons.org/licenses/by-nd/1.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:41Z", + "timestamp": "2024-08-19T17:43:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-ND-2.0.json b/src/main/resources/license-list-data/json/details/CC-BY-ND-2.0.json index b2f71ecc5f..62f6577e10 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-ND-2.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-ND-2.0.json @@ -11,7 +11,7 @@ "url": "https://creativecommons.org/licenses/by-nd/2.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:33:06Z", + "timestamp": "2024-08-19T17:39:57Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-ND-2.5.json b/src/main/resources/license-list-data/json/details/CC-BY-ND-2.5.json index 5849648fa2..dbfa813e58 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-ND-2.5.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-ND-2.5.json @@ -11,7 +11,7 @@ "url": "https://creativecommons.org/licenses/by-nd/2.5/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:38Z", + "timestamp": "2024-08-19T17:37:45Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-ND-3.0-DE.json b/src/main/resources/license-list-data/json/details/CC-BY-ND-3.0-DE.json index c2538ae29d..7f194f773c 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-ND-3.0-DE.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-ND-3.0-DE.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-nd/3.0/de/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:48Z", + "timestamp": "2024-08-19T17:36:45Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-ND-3.0.json b/src/main/resources/license-list-data/json/details/CC-BY-ND-3.0.json index b01dfab216..8f7e5f72b2 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-ND-3.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-ND-3.0.json @@ -11,7 +11,7 @@ "url": "https://creativecommons.org/licenses/by-nd/3.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:45Z", + "timestamp": "2024-08-19T17:45:21Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-ND-4.0.json b/src/main/resources/license-list-data/json/details/CC-BY-ND-4.0.json index b21119b70a..01af74dbc0 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-ND-4.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-ND-4.0.json @@ -11,7 +11,7 @@ "url": "https://creativecommons.org/licenses/by-nd/4.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:57Z", + "timestamp": "2024-08-19T17:43:36Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-SA-1.0.json b/src/main/resources/license-list-data/json/details/CC-BY-SA-1.0.json index 6f2117e8d1..337448460d 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-SA-1.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-SA-1.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-sa/1.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:02Z", + "timestamp": "2024-08-19T17:47:36Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-SA-2.0-UK.json b/src/main/resources/license-list-data/json/details/CC-BY-SA-2.0-UK.json index 53f1935c9f..9ad59a1f1e 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-SA-2.0-UK.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-SA-2.0-UK.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-sa/2.0/uk/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:22Z", + "timestamp": "2024-08-19T17:40:37Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-SA-2.0.json b/src/main/resources/license-list-data/json/details/CC-BY-SA-2.0.json index 40c8279638..436df58bd1 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-SA-2.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-SA-2.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-sa/2.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:45Z", + "timestamp": "2024-08-19T17:36:25Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-SA-2.1-JP.json b/src/main/resources/license-list-data/json/details/CC-BY-SA-2.1-JP.json index 937b2bb3e2..d9a9ce5be4 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-SA-2.1-JP.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-SA-2.1-JP.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-sa/2.1/jp/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:34Z", + "timestamp": "2024-08-19T17:35:12Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-SA-2.5.json b/src/main/resources/license-list-data/json/details/CC-BY-SA-2.5.json index f1636ad4ad..ff8fb08e31 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-SA-2.5.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-SA-2.5.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-sa/2.5/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:15Z", + "timestamp": "2024-08-19T17:48:22Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0-AT.json b/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0-AT.json index 9bb4506b20..09f11f6499 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0-AT.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0-AT.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-sa/3.0/at/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:34Z", + "timestamp": "2024-08-19T17:41:05Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0-DE.json b/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0-DE.json index 38ef89b7ae..48963c3c69 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0-DE.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0-DE.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-sa/3.0/de/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:50Z", + "timestamp": "2024-08-19T17:35:47Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0-IGO.json b/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0-IGO.json index 77769e8f53..6d1ebe10f7 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0-IGO.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0-IGO.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-sa/3.0/igo/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:32Z", + "timestamp": "2024-08-19T17:44:58Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0.json b/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0.json index b2e518b24f..82862919df 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-SA-3.0.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/by-sa/3.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:09Z", + "timestamp": "2024-08-19T17:35:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-BY-SA-4.0.json b/src/main/resources/license-list-data/json/details/CC-BY-SA-4.0.json index 6e9c706ac0..2dc11014f1 100644 --- a/src/main/resources/license-list-data/json/details/CC-BY-SA-4.0.json +++ b/src/main/resources/license-list-data/json/details/CC-BY-SA-4.0.json @@ -11,7 +11,7 @@ "url": "https://creativecommons.org/licenses/by-sa/4.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:31Z", + "timestamp": "2024-08-19T17:35:11Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC-PDDC.json b/src/main/resources/license-list-data/json/details/CC-PDDC.json index 5264712cd5..513c681b22 100644 --- a/src/main/resources/license-list-data/json/details/CC-PDDC.json +++ b/src/main/resources/license-list-data/json/details/CC-PDDC.json @@ -10,7 +10,7 @@ "url": "https://creativecommons.org/licenses/publicdomain/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:27Z", + "timestamp": "2024-08-19T17:36:17Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CC0-1.0.json b/src/main/resources/license-list-data/json/details/CC0-1.0.json index 317a4c0bdc..d2f43f0d85 100644 --- a/src/main/resources/license-list-data/json/details/CC0-1.0.json +++ b/src/main/resources/license-list-data/json/details/CC0-1.0.json @@ -11,7 +11,7 @@ "url": "https://creativecommons.org/publicdomain/zero/1.0/legalcode", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:44Z", + "timestamp": "2024-08-19T17:43:57Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CDDL-1.0.json b/src/main/resources/license-list-data/json/details/CDDL-1.0.json index 5a7dce4466..f1cbf251bc 100644 --- a/src/main/resources/license-list-data/json/details/CDDL-1.0.json +++ b/src/main/resources/license-list-data/json/details/CDDL-1.0.json @@ -12,7 +12,7 @@ "url": "https://opensource.org/licenses/cddl1", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:21:41Z", + "timestamp": "2024-08-19T17:36:30Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CDDL-1.1.json b/src/main/resources/license-list-data/json/details/CDDL-1.1.json index 10b8c5112c..67c1efdfcd 100644 --- a/src/main/resources/license-list-data/json/details/CDDL-1.1.json +++ b/src/main/resources/license-list-data/json/details/CDDL-1.1.json @@ -11,7 +11,7 @@ "url": "https://javaee.github.io/glassfish/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:03Z", + "timestamp": "2024-08-19T17:43:15Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "http://glassfish.java.net/public/CDDL+GPL_1_1.html", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:19:11Z", + "timestamp": "2024-08-19T17:43:24Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CDL-1.0.json b/src/main/resources/license-list-data/json/details/CDL-1.0.json index 7e1295a616..2c2f44cd8f 100644 --- a/src/main/resources/license-list-data/json/details/CDL-1.0.json +++ b/src/main/resources/license-list-data/json/details/CDL-1.0.json @@ -7,30 +7,30 @@ "crossRef": [ { "match": "false", - "url": "https://www.gnu.org/licenses/license-list.html#ACDL", + "url": "http://www.opensource.apple.com/cdl/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:06Z", + "timestamp": "2024-08-19T17:38:40Z", "isWayBackLink": false, - "order": 2 + "order": 0 }, { "match": "false", - "url": "https://fedoraproject.org/wiki/Licensing/Common_Documentation_License", + "url": "https://www.gnu.org/licenses/license-list.html#ACDL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:06Z", + "timestamp": "2024-08-19T17:38:41Z", "isWayBackLink": false, - "order": 1 + "order": 2 }, { "match": "false", - "url": "http://www.opensource.apple.com/cdl/", + "url": "https://fedoraproject.org/wiki/Licensing/Common_Documentation_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:06Z", + "timestamp": "2024-08-19T17:38:41Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/CDLA-Permissive-1.0.json b/src/main/resources/license-list-data/json/details/CDLA-Permissive-1.0.json index 6272c36069..61d0f5d39d 100644 --- a/src/main/resources/license-list-data/json/details/CDLA-Permissive-1.0.json +++ b/src/main/resources/license-list-data/json/details/CDLA-Permissive-1.0.json @@ -10,7 +10,7 @@ "url": "https://cdla.io/permissive-1-0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:39Z", + "timestamp": "2024-08-19T17:40:39Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CDLA-Permissive-2.0.json b/src/main/resources/license-list-data/json/details/CDLA-Permissive-2.0.json index 7effffbbe3..2879ef30c6 100644 --- a/src/main/resources/license-list-data/json/details/CDLA-Permissive-2.0.json +++ b/src/main/resources/license-list-data/json/details/CDLA-Permissive-2.0.json @@ -10,7 +10,7 @@ "url": "https://cdla.dev/permissive-2-0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:40Z", + "timestamp": "2024-08-19T17:47:40Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CDLA-Sharing-1.0.json b/src/main/resources/license-list-data/json/details/CDLA-Sharing-1.0.json index 0b30206021..538b71f87e 100644 --- a/src/main/resources/license-list-data/json/details/CDLA-Sharing-1.0.json +++ b/src/main/resources/license-list-data/json/details/CDLA-Sharing-1.0.json @@ -10,7 +10,7 @@ "url": "https://cdla.io/sharing-1-0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:49Z", + "timestamp": "2024-08-19T17:35:51Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CECILL-1.0.json b/src/main/resources/license-list-data/json/details/CECILL-1.0.json index a395f23ee8..461470dbc4 100644 --- a/src/main/resources/license-list-data/json/details/CECILL-1.0.json +++ b/src/main/resources/license-list-data/json/details/CECILL-1.0.json @@ -11,7 +11,7 @@ "url": "http://www.cecill.info/licences/Licence_CeCILL_V1-fr.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:30Z", + "timestamp": "2024-08-19T17:49:14Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CECILL-1.1.json b/src/main/resources/license-list-data/json/details/CECILL-1.1.json index 71e481cca0..6fd1859899 100644 --- a/src/main/resources/license-list-data/json/details/CECILL-1.1.json +++ b/src/main/resources/license-list-data/json/details/CECILL-1.1.json @@ -11,7 +11,7 @@ "url": "http://www.cecill.info/licences/Licence_CeCILL_V1.1-US.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:35Z", + "timestamp": "2024-08-19T17:39:09Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CECILL-2.0.json b/src/main/resources/license-list-data/json/details/CECILL-2.0.json index bc2dd2c654..697d8dcbed 100644 --- a/src/main/resources/license-list-data/json/details/CECILL-2.0.json +++ b/src/main/resources/license-list-data/json/details/CECILL-2.0.json @@ -12,7 +12,7 @@ "url": "http://www.cecill.info/licences/Licence_CeCILL_V2-en.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:45Z", + "timestamp": "2024-08-19T17:43:03Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CECILL-2.1.json b/src/main/resources/license-list-data/json/details/CECILL-2.1.json index bedc6e32ec..dd37bb4dd6 100644 --- a/src/main/resources/license-list-data/json/details/CECILL-2.1.json +++ b/src/main/resources/license-list-data/json/details/CECILL-2.1.json @@ -11,7 +11,7 @@ "url": "http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:59Z", + "timestamp": "2024-08-19T17:35:36Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CECILL-B.json b/src/main/resources/license-list-data/json/details/CECILL-B.json index ab349458ab..30ccd95a12 100644 --- a/src/main/resources/license-list-data/json/details/CECILL-B.json +++ b/src/main/resources/license-list-data/json/details/CECILL-B.json @@ -12,7 +12,7 @@ "url": "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:39Z", + "timestamp": "2024-08-19T17:35:54Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CECILL-C.json b/src/main/resources/license-list-data/json/details/CECILL-C.json index 463897f083..675cf647c3 100644 --- a/src/main/resources/license-list-data/json/details/CECILL-C.json +++ b/src/main/resources/license-list-data/json/details/CECILL-C.json @@ -12,7 +12,7 @@ "url": "http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:28Z", + "timestamp": "2024-08-19T17:36:24Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CERN-OHL-1.1.json b/src/main/resources/license-list-data/json/details/CERN-OHL-1.1.json index 7b60852fd6..b853083974 100644 --- a/src/main/resources/license-list-data/json/details/CERN-OHL-1.1.json +++ b/src/main/resources/license-list-data/json/details/CERN-OHL-1.1.json @@ -10,7 +10,7 @@ "url": "https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:33Z", + "timestamp": "2024-08-19T17:36:03Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CERN-OHL-1.2.json b/src/main/resources/license-list-data/json/details/CERN-OHL-1.2.json index b7862fb892..18b3e00582 100644 --- a/src/main/resources/license-list-data/json/details/CERN-OHL-1.2.json +++ b/src/main/resources/license-list-data/json/details/CERN-OHL-1.2.json @@ -10,7 +10,7 @@ "url": "https://www.ohwr.org/project/licenses/wikis/cern-ohl-v1.2", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:53Z", + "timestamp": "2024-08-19T17:50:13Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CERN-OHL-P-2.0.json b/src/main/resources/license-list-data/json/details/CERN-OHL-P-2.0.json index cc6076323b..14f2c4ca1f 100644 --- a/src/main/resources/license-list-data/json/details/CERN-OHL-P-2.0.json +++ b/src/main/resources/license-list-data/json/details/CERN-OHL-P-2.0.json @@ -10,7 +10,7 @@ "url": "https://www.ohwr.org/project/cernohl/wikis/Documents/CERN-OHL-version-2", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:04Z", + "timestamp": "2024-08-19T17:45:57Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CERN-OHL-S-2.0.json b/src/main/resources/license-list-data/json/details/CERN-OHL-S-2.0.json index 52aface620..465b056f0a 100644 --- a/src/main/resources/license-list-data/json/details/CERN-OHL-S-2.0.json +++ b/src/main/resources/license-list-data/json/details/CERN-OHL-S-2.0.json @@ -10,7 +10,7 @@ "url": "https://www.ohwr.org/project/cernohl/wikis/Documents/CERN-OHL-version-2", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:59Z", + "timestamp": "2024-08-19T17:41:00Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CERN-OHL-W-2.0.json b/src/main/resources/license-list-data/json/details/CERN-OHL-W-2.0.json index 94e35ef1a2..3b3affb7ac 100644 --- a/src/main/resources/license-list-data/json/details/CERN-OHL-W-2.0.json +++ b/src/main/resources/license-list-data/json/details/CERN-OHL-W-2.0.json @@ -10,7 +10,7 @@ "url": "https://www.ohwr.org/project/cernohl/wikis/Documents/CERN-OHL-version-2", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:46Z", + "timestamp": "2024-08-19T17:48:35Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CFITSIO.json b/src/main/resources/license-list-data/json/details/CFITSIO.json index 856db299bc..22d2151e9c 100644 --- a/src/main/resources/license-list-data/json/details/CFITSIO.json +++ b/src/main/resources/license-list-data/json/details/CFITSIO.json @@ -7,21 +7,21 @@ "crossRef": [ { "match": "false", - "url": "https://heasarc.gsfc.nasa.gov/docs/software/fitsio/c/f_user/node9.html", + "url": "https://heasarc.gsfc.nasa.gov/docs/software/ftools/fv/doc/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:55Z", + "timestamp": "2024-08-19T17:46:58Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "https://heasarc.gsfc.nasa.gov/docs/software/ftools/fv/doc/license.html", + "url": "https://heasarc.gsfc.nasa.gov/docs/software/fitsio/c/f_user/node9.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:55Z", + "timestamp": "2024-08-19T17:46:58Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/CMU-Mach-nodoc.json b/src/main/resources/license-list-data/json/details/CMU-Mach-nodoc.json index 37b9caeb5f..51172d1f40 100644 --- a/src/main/resources/license-list-data/json/details/CMU-Mach-nodoc.json +++ b/src/main/resources/license-list-data/json/details/CMU-Mach-nodoc.json @@ -11,7 +11,7 @@ "url": "https://web.mit.edu/kerberos/krb5-1.21/doc/mitK5license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:33Z", + "timestamp": "2024-08-19T17:35:09Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L718-L728", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:33Z", + "timestamp": "2024-08-19T17:35:09Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CMU-Mach.json b/src/main/resources/license-list-data/json/details/CMU-Mach.json index e566febbc5..abd7cb528a 100644 --- a/src/main/resources/license-list-data/json/details/CMU-Mach.json +++ b/src/main/resources/license-list-data/json/details/CMU-Mach.json @@ -10,7 +10,7 @@ "url": "https://www.cs.cmu.edu/~410/licenses.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:48Z", + "timestamp": "2024-08-19T17:35:27Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CNRI-Jython.json b/src/main/resources/license-list-data/json/details/CNRI-Jython.json index bf44e0720b..59fcfc06a9 100644 --- a/src/main/resources/license-list-data/json/details/CNRI-Jython.json +++ b/src/main/resources/license-list-data/json/details/CNRI-Jython.json @@ -11,7 +11,7 @@ "url": "http://www.jython.org/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:09Z", + "timestamp": "2024-08-19T17:38:24Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CNRI-Python-GPL-Compatible.json b/src/main/resources/license-list-data/json/details/CNRI-Python-GPL-Compatible.json index 34c5ac45bf..79e77fb0e1 100644 --- a/src/main/resources/license-list-data/json/details/CNRI-Python-GPL-Compatible.json +++ b/src/main/resources/license-list-data/json/details/CNRI-Python-GPL-Compatible.json @@ -10,7 +10,7 @@ "url": "http://www.python.org/download/releases/1.6.1/download_win/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:01Z", + "timestamp": "2024-08-19T17:37:04Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CNRI-Python.json b/src/main/resources/license-list-data/json/details/CNRI-Python.json index 227239f3e4..e86dfdf770 100644 --- a/src/main/resources/license-list-data/json/details/CNRI-Python.json +++ b/src/main/resources/license-list-data/json/details/CNRI-Python.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/CNRI-Python", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:40Z", + "timestamp": "2024-08-19T17:41:12Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/COIL-1.0.json b/src/main/resources/license-list-data/json/details/COIL-1.0.json index 2430ee0c5c..da923b47a5 100644 --- a/src/main/resources/license-list-data/json/details/COIL-1.0.json +++ b/src/main/resources/license-list-data/json/details/COIL-1.0.json @@ -10,7 +10,7 @@ "url": "https://coil.apotheon.org/plaintext/01.0.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:09Z", + "timestamp": "2024-08-19T17:39:39Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CPAL-1.0.json b/src/main/resources/license-list-data/json/details/CPAL-1.0.json index 9ccf2157a3..660bf63c99 100644 --- a/src/main/resources/license-list-data/json/details/CPAL-1.0.json +++ b/src/main/resources/license-list-data/json/details/CPAL-1.0.json @@ -14,7 +14,7 @@ "url": "https://opensource.org/licenses/CPAL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:33Z", + "timestamp": "2024-08-19T17:46:08Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CPL-1.0.json b/src/main/resources/license-list-data/json/details/CPL-1.0.json index 61e58d1c9d..3711ee8769 100644 --- a/src/main/resources/license-list-data/json/details/CPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/CPL-1.0.json @@ -12,7 +12,7 @@ "url": "https://opensource.org/licenses/CPL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:19Z", + "timestamp": "2024-08-19T17:35:43Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CPOL-1.02.json b/src/main/resources/license-list-data/json/details/CPOL-1.02.json index 695e973975..6018cb925b 100644 --- a/src/main/resources/license-list-data/json/details/CPOL-1.02.json +++ b/src/main/resources/license-list-data/json/details/CPOL-1.02.json @@ -11,7 +11,7 @@ "url": "http://www.codeproject.com/info/cpol10.aspx", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:02Z", + "timestamp": "2024-08-19T17:35:08Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CUA-OPL-1.0.json b/src/main/resources/license-list-data/json/details/CUA-OPL-1.0.json index c578980852..5ca751ac32 100644 --- a/src/main/resources/license-list-data/json/details/CUA-OPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/CUA-OPL-1.0.json @@ -13,7 +13,7 @@ "url": "https://opensource.org/licenses/CUA-OPL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:26Z", + "timestamp": "2024-08-19T17:41:27Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Caldera-no-preamble.json b/src/main/resources/license-list-data/json/details/Caldera-no-preamble.json index 6460ae3a51..b30af1d061 100644 --- a/src/main/resources/license-list-data/json/details/Caldera-no-preamble.json +++ b/src/main/resources/license-list-data/json/details/Caldera-no-preamble.json @@ -11,7 +11,7 @@ "url": "https://github.com/apache/apr/blob/trunk/LICENSE#L298C6-L298C29", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:53Z", + "timestamp": "2024-08-19T17:37:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Caldera.json b/src/main/resources/license-list-data/json/details/Caldera.json index a5b5567534..3ade15ee97 100644 --- a/src/main/resources/license-list-data/json/details/Caldera.json +++ b/src/main/resources/license-list-data/json/details/Caldera.json @@ -10,7 +10,7 @@ "url": "http://www.lemis.com/grog/UNIX/ancient-source-all.pdf", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:11Z", + "timestamp": "2024-08-19T17:45:24Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Catharon.json b/src/main/resources/license-list-data/json/details/Catharon.json index 95f66bad51..73cfb0ec67 100644 --- a/src/main/resources/license-list-data/json/details/Catharon.json +++ b/src/main/resources/license-list-data/json/details/Catharon.json @@ -10,7 +10,7 @@ "url": "https://github.com/scummvm/scummvm/blob/v2.8.0/LICENSES/CatharonLicense.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:33Z", + "timestamp": "2024-08-19T17:39:30Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ClArtistic.json b/src/main/resources/license-list-data/json/details/ClArtistic.json index 43078f04dd..a1385eb549 100644 --- a/src/main/resources/license-list-data/json/details/ClArtistic.json +++ b/src/main/resources/license-list-data/json/details/ClArtistic.json @@ -8,21 +8,21 @@ "crossRef": [ { "match": "false", - "url": "http://gianluca.dellavedova.org/2011/01/03/clarified-artistic-license/", + "url": "http://www.ncftp.com/ncftp/doc/LICENSE.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:43Z", + "timestamp": "2024-08-19T17:37:13Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "http://www.ncftp.com/ncftp/doc/LICENSE.txt", + "url": "http://gianluca.dellavedova.org/2011/01/03/clarified-artistic-license/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:44Z", + "timestamp": "2024-08-19T17:37:13Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Clips.json b/src/main/resources/license-list-data/json/details/Clips.json index 4a5053fdc7..fda1be9ed0 100644 --- a/src/main/resources/license-list-data/json/details/Clips.json +++ b/src/main/resources/license-list-data/json/details/Clips.json @@ -10,7 +10,7 @@ "url": "https://github.com/DrItanium/maya/blob/master/LICENSE.CLIPS", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:48Z", + "timestamp": "2024-08-19T17:41:54Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Community-Spec-1.0.json b/src/main/resources/license-list-data/json/details/Community-Spec-1.0.json index 8f32a03009..e505d70ab8 100644 --- a/src/main/resources/license-list-data/json/details/Community-Spec-1.0.json +++ b/src/main/resources/license-list-data/json/details/Community-Spec-1.0.json @@ -11,7 +11,7 @@ "url": "https://github.com/CommunitySpecification/1.0/blob/master/1._Community_Specification_License-v1.md", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:35Z", + "timestamp": "2024-08-19T17:35:18Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Condor-1.1.json b/src/main/resources/license-list-data/json/details/Condor-1.1.json index f50bea57dc..0c5e2089db 100644 --- a/src/main/resources/license-list-data/json/details/Condor-1.1.json +++ b/src/main/resources/license-list-data/json/details/Condor-1.1.json @@ -7,23 +7,23 @@ "licenseComments": "This license was released 30 October 2003", "licenseId": "Condor-1.1", "crossRef": [ - { - "match": "false", - "url": "http://research.cs.wisc.edu/condor/license.html#condor", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:21:03Z", - "isWayBackLink": false, - "order": 0 - }, { "match": "N/A", "url": "http://web.archive.org/web/20111123062036/http://research.cs.wisc.edu/condor/license.html#condor", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:21:04Z", + "timestamp": "2024-08-19T17:35:26Z", "isWayBackLink": false, "order": 1 + }, + { + "match": "false", + "url": "http://research.cs.wisc.edu/condor/license.html#condor", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:35:26Z", + "isWayBackLink": false, + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Cornell-Lossless-JPEG.json b/src/main/resources/license-list-data/json/details/Cornell-Lossless-JPEG.json index 48a6d8d41b..3c904cd68a 100644 --- a/src/main/resources/license-list-data/json/details/Cornell-Lossless-JPEG.json +++ b/src/main/resources/license-list-data/json/details/Cornell-Lossless-JPEG.json @@ -7,30 +7,30 @@ "crossRef": [ { "match": "false", - "url": "https://gitlab.freedesktop.org/libopenraw/libopenraw/blob/master/lib/ljpegdecompressor.cpp#L32", + "url": "https://www.mssl.ucl.ac.uk/~mcrw/src/20050920/proto.h", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:41Z", + "timestamp": "2024-08-19T17:35:28Z", "isWayBackLink": false, - "order": 2 + "order": 1 }, { "match": "false", "url": "https://android.googlesource.com/platform/external/dng_sdk/+/refs/heads/master/source/dng_lossless_jpeg.cpp#16", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:42Z", + "timestamp": "2024-08-19T17:35:28Z", "isWayBackLink": false, "order": 0 }, { "match": "false", - "url": "https://www.mssl.ucl.ac.uk/~mcrw/src/20050920/proto.h", + "url": "https://gitlab.freedesktop.org/libopenraw/libopenraw/blob/master/lib/ljpegdecompressor.cpp#L32", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:43Z", + "timestamp": "2024-08-19T17:35:31Z", "isWayBackLink": false, - "order": 1 + "order": 2 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Cronyx.json b/src/main/resources/license-list-data/json/details/Cronyx.json index a1c3a026c1..2d530559a2 100644 --- a/src/main/resources/license-list-data/json/details/Cronyx.json +++ b/src/main/resources/license-list-data/json/details/Cronyx.json @@ -7,37 +7,37 @@ "crossRef": [ { "match": "false", - "url": "https://gitlab.freedesktop.org/xorg/font/cronyx-cyrillic/-/blob/master/COPYING", + "url": "https://gitlab.freedesktop.org/xorg/font/screen-cyrillic/-/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:08Z", + "timestamp": "2024-08-19T17:49:52Z", "isWayBackLink": false, - "order": 1 + "order": 3 }, { "match": "false", - "url": "https://gitlab.freedesktop.org/xorg/font/screen-cyrillic/-/blob/master/COPYING", + "url": "https://gitlab.freedesktop.org/xorg/font/misc-cyrillic/-/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:09Z", + "timestamp": "2024-08-19T17:49:52Z", "isWayBackLink": false, - "order": 3 + "order": 2 }, { "match": "false", - "url": "https://gitlab.freedesktop.org/xorg/font/misc-cyrillic/-/blob/master/COPYING", + "url": "https://gitlab.freedesktop.org/xorg/font/cronyx-cyrillic/-/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:09Z", + "timestamp": "2024-08-19T17:49:53Z", "isWayBackLink": false, - "order": 2 + "order": 1 }, { "match": "false", "url": "https://gitlab.freedesktop.org/xorg/font/alias/-/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:09Z", + "timestamp": "2024-08-19T17:49:54Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Crossword.json b/src/main/resources/license-list-data/json/details/Crossword.json index 7121b6470d..d2991d74f9 100644 --- a/src/main/resources/license-list-data/json/details/Crossword.json +++ b/src/main/resources/license-list-data/json/details/Crossword.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Crossword", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:11Z", + "timestamp": "2024-08-19T17:47:41Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/CrystalStacker.json b/src/main/resources/license-list-data/json/details/CrystalStacker.json index 1d6c730054..96576feecc 100644 --- a/src/main/resources/license-list-data/json/details/CrystalStacker.json +++ b/src/main/resources/license-list-data/json/details/CrystalStacker.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing:CrystalStacker?rd\u003dLicensing/CrystalStacker", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:09Z", + "timestamp": "2024-08-19T17:37:06Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Cube.json b/src/main/resources/license-list-data/json/details/Cube.json index 1ad2d7dae3..f289c3e20c 100644 --- a/src/main/resources/license-list-data/json/details/Cube.json +++ b/src/main/resources/license-list-data/json/details/Cube.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Cube", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:46Z", + "timestamp": "2024-08-19T17:36:00Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/D-FSL-1.0.json b/src/main/resources/license-list-data/json/details/D-FSL-1.0.json index 1db21cb383..984d72c8dc 100644 --- a/src/main/resources/license-list-data/json/details/D-FSL-1.0.json +++ b/src/main/resources/license-list-data/json/details/D-FSL-1.0.json @@ -8,75 +8,75 @@ "crossRef": [ { "match": "false", - "url": "http://www.dipp.nrw.de/d-fsl/lizenzen/", + "url": "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:25Z", + "timestamp": "2024-08-19T17:35:37Z", "isWayBackLink": false, - "order": 0 + "order": 3 }, { "match": "false", "url": "http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/en/D-FSL-1_0_en.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:26Z", + "timestamp": "2024-08-19T17:35:38Z", "isWayBackLink": false, "order": 2 }, { "match": "false", - "url": "http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/de/D-FSL-1_0_de.txt", + "url": "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/german-free-software-license", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:27Z", + "timestamp": "2024-08-19T17:35:39Z", "isWayBackLink": false, - "order": 1 + "order": 5 }, { - "match": "N/A", - "url": "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_en.txt/at_download/file", + "match": "false", + "url": "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/deutsche-freie-software-lizenz", "isValid": true, - "isLive": false, - "timestamp": "2024-05-22T17:19:27Z", + "isLive": true, + "timestamp": "2024-08-19T17:35:40Z", "isWayBackLink": false, - "order": 7 + "order": 4 }, { "match": "false", - "url": "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/deutsche-freie-software-lizenz", + "url": "http://www.dipp.nrw.de/d-fsl/index_html/lizenzen/de/D-FSL-1_0_de.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:28Z", + "timestamp": "2024-08-19T17:35:41Z", "isWayBackLink": false, - "order": 4 + "order": 1 }, { "match": "false", - "url": "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl", + "url": "http://www.dipp.nrw.de/d-fsl/lizenzen/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:28Z", + "timestamp": "2024-08-19T17:35:42Z", "isWayBackLink": false, - "order": 3 + "order": 0 }, { "match": "N/A", "url": "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_de.txt/at_download/file", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:19:28Z", + "timestamp": "2024-08-19T17:35:43Z", "isWayBackLink": false, "order": 6 }, { - "match": "false", - "url": "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/german-free-software-license", + "match": "N/A", + "url": "https://www.hbz-nrw.de/produkte/open-access/lizenzen/dfsl/D-FSL-1_0_en.txt/at_download/file", "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:19:28Z", + "isLive": false, + "timestamp": "2024-08-19T17:35:43Z", "isWayBackLink": false, - "order": 5 + "order": 7 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/DEC-3-Clause.json b/src/main/resources/license-list-data/json/details/DEC-3-Clause.json index 9bdfd2ab68..2511a41488 100644 --- a/src/main/resources/license-list-data/json/details/DEC-3-Clause.json +++ b/src/main/resources/license-list-data/json/details/DEC-3-Clause.json @@ -10,7 +10,7 @@ "url": "https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/COPYING?ref_type\u003dheads#L239", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:31Z", + "timestamp": "2024-08-19T17:44:42Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/DL-DE-BY-2.0.json b/src/main/resources/license-list-data/json/details/DL-DE-BY-2.0.json index e410b6ff49..58a2e5507d 100644 --- a/src/main/resources/license-list-data/json/details/DL-DE-BY-2.0.json +++ b/src/main/resources/license-list-data/json/details/DL-DE-BY-2.0.json @@ -10,7 +10,7 @@ "url": "https://www.govdata.de/dl-de/by-2-0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:34Z", + "timestamp": "2024-08-19T17:35:30Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/DL-DE-ZERO-2.0.json b/src/main/resources/license-list-data/json/details/DL-DE-ZERO-2.0.json index 2019368ad3..9966241178 100644 --- a/src/main/resources/license-list-data/json/details/DL-DE-ZERO-2.0.json +++ b/src/main/resources/license-list-data/json/details/DL-DE-ZERO-2.0.json @@ -10,7 +10,7 @@ "url": "https://www.govdata.de/dl-de/zero-2-0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:15Z", + "timestamp": "2024-08-19T17:45:06Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/DOC.json b/src/main/resources/license-list-data/json/details/DOC.json index 320724d06a..8cbf5e5582 100644 --- a/src/main/resources/license-list-data/json/details/DOC.json +++ b/src/main/resources/license-list-data/json/details/DOC.json @@ -7,21 +7,21 @@ "crossRef": [ { "match": "false", - "url": "http://www.cs.wustl.edu/~schmidt/ACE-copying.html", + "url": "https://www.dre.vanderbilt.edu/~schmidt/ACE-copying.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:40Z", + "timestamp": "2024-08-19T17:50:05Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "https://www.dre.vanderbilt.edu/~schmidt/ACE-copying.html", + "url": "http://www.cs.wustl.edu/~schmidt/ACE-copying.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:40Z", + "timestamp": "2024-08-19T17:50:05Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/DRL-1.0.json b/src/main/resources/license-list-data/json/details/DRL-1.0.json index bdd842dcfc..eb85dca56e 100644 --- a/src/main/resources/license-list-data/json/details/DRL-1.0.json +++ b/src/main/resources/license-list-data/json/details/DRL-1.0.json @@ -10,7 +10,7 @@ "url": "https://github.com/Neo23x0/sigma/blob/master/LICENSE.Detection.Rules.md", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:31Z", + "timestamp": "2024-08-19T17:41:30Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/DRL-1.1.json b/src/main/resources/license-list-data/json/details/DRL-1.1.json index fd3f801634..c63ad8dc76 100644 --- a/src/main/resources/license-list-data/json/details/DRL-1.1.json +++ b/src/main/resources/license-list-data/json/details/DRL-1.1.json @@ -10,7 +10,7 @@ "url": "https://github.com/SigmaHQ/Detection-Rule-License/blob/6ec7fbde6101d101b5b5d1fcb8f9b69fbc76c04a/LICENSE.Detection.Rules.md", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:41Z", + "timestamp": "2024-08-19T17:37:53Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/DSDP.json b/src/main/resources/license-list-data/json/details/DSDP.json index aa9f264010..c76d814af4 100644 --- a/src/main/resources/license-list-data/json/details/DSDP.json +++ b/src/main/resources/license-list-data/json/details/DSDP.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/DSDP", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:14Z", + "timestamp": "2024-08-19T17:36:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/DocBook-Schema.json b/src/main/resources/license-list-data/json/details/DocBook-Schema.json new file mode 100644 index 0000000000..3223b8c9a8 --- /dev/null +++ b/src/main/resources/license-list-data/json/details/DocBook-Schema.json @@ -0,0 +1,23 @@ +{ + "isDeprecatedLicenseId": false, + "licenseText": "Copyright 1992-2011 HaL Computer Systems, Inc.,\nO\u0027Reilly \u0026 Associates, Inc., ArborText, Inc., Fujitsu Software\nCorporation, Norman Walsh, Sun Microsystems, Inc., and the\nOrganization for the Advancement of Structured Information\nStandards (OASIS).\n \nPermission to use, copy, modify and distribute the DocBook schema\nand its accompanying documentation for any purpose and without fee\nis hereby granted in perpetuity, provided that the above copyright\nnotice and this paragraph appear in all copies. The copyright\nholders make no representation about the suitability of the schema\nfor any purpose. It is provided \"as is\" without expressed or implied\nwarranty.\n \nIf you modify the DocBook schema in any way, label your schema as a\nvariant of DocBook. See the reference documentation\n(http://docbook.org/tdg5/en/html/ch05.html#s-notdocbook)\nfor more information.\n \nPlease direct all questions, bug reports, or suggestions for changes\nto the docbook@lists.oasis-open.org mailing list. For more\ninformation, see http://www.oasis-open.org/docbook/.\n", + "standardLicenseTemplate": "\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright 1992-2011 HaL Computer Systems, Inc., O\u0027Reilly \u0026 Associates, Inc., ArborText, Inc., Fujitsu Software Corporation, Norman Walsh, Sun Microsystems, Inc., and the Organization for the Advancement of Structured Information Standards (OASIS).\";match\u003d\".{0,5000}\"\u003e\u003e\n\nPermission to use, copy, modify and distribute the DocBook schema and its accompanying documentation for any purpose and without fee is hereby granted in perpetuity, provided that the above copyright notice and this paragraph appear in all copies. The copyright holders make no representation about the suitability of the schema for any purpose. It is provided \"as is\" without expressed or implied warranty.\n\nIf you modify the DocBook schema in any way, label your schema as a variant of DocBook. See the reference documentation (http://docbook.org/tdg5/en/html/ch05.html#s-notdocbook) for more information.\n\nPlease direct all questions, bug reports, or suggestions for changes to the docbook@lists.oasis-open.org mailing list. For more information, see http://www.oasis-open.org/docbook/.\n\n", + "name": "DocBook Schema License", + "licenseId": "DocBook-Schema", + "crossRef": [ + { + "match": "false", + "url": "https://github.com/docbook/xslt10-stylesheets/blob/efd62655c11cc8773708df7a843613fa1e932bf8/xsl/assembly/schema/docbook51b7.rnc", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:36:08Z", + "isWayBackLink": false, + "order": 0 + } + ], + "seeAlso": [ + "https://github.com/docbook/xslt10-stylesheets/blob/efd62655c11cc8773708df7a843613fa1e932bf8/xsl/assembly/schema/docbook51b7.rnc" + ], + "isOsiApproved": false, + "licenseTextHtml": "\n \u003cvar class\u003d\"replaceable-license-text\"\u003e \n Copyright 1992-2011 HaL Computer Systems, Inc., O\u0026apos;Reilly \u0026amp;\n Associates, Inc., ArborText, Inc., Fujitsu Software Corporation,\n Norman Walsh, Sun Microsystems, Inc., and the Organization for\n the Advancement of Structured Information Standards (OASIS).\n \u003c/var\u003e\n \u003cp\u003e\n Permission to use, copy, modify and distribute the DocBook\n schema and its accompanying documentation for any purpose\n and without fee is hereby granted in perpetuity, provided\n that the above copyright notice and this paragraph appear\n in all copies. The copyright holders make no representation\n about the suitability of the schema for any purpose. It\n is provided \u0026quot;as is\u0026quot; without expressed or implied warranty.\n \u003c/p\u003e\n\n \u003cp\u003e\n If you modify the DocBook schema in any way, label your schema\n as a variant of DocBook. See the reference documentation\n (http://docbook.org/tdg5/en/html/ch05.html#s-notdocbook)\n for more information.\n \u003c/p\u003e\n\n \u003cp\u003e\n Please direct all questions, bug reports, or suggestions for\n changes to the docbook@lists.oasis-open.org mailing list.\n For more information, see http://www.oasis-open.org/docbook/.\n \u003c/p\u003e\n\n " +} \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/DocBook-XML.json b/src/main/resources/license-list-data/json/details/DocBook-XML.json new file mode 100644 index 0000000000..15bc8546eb --- /dev/null +++ b/src/main/resources/license-list-data/json/details/DocBook-XML.json @@ -0,0 +1,23 @@ +{ + "isDeprecatedLicenseId": false, + "licenseText": "Copyright\n---------\nCopyright (C) 1999-2007 Norman Walsh\nCopyright (C) 2003 Jiří Kosek\nCopyright (C) 2004-2007 Steve Ball\nCopyright (C) 2005-2014 The DocBook Project\nCopyright (C) 2011-2012 O\u0027Reilly Media\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the ``Software\u0027\u0027), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or\nsell copies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nExcept as contained in this notice, the names of individuals\ncredited with contribution to this software shall not be used in\nadvertising or otherwise to promote the sale, use or other\ndealings in this Software without prior written authorization\nfrom the individuals in question.\n\nAny stylesheet derived from this Software that is publically\ndistributed will be identified with a different name and the\nversion strings in any derived Software will be changed so that\nno possibility of confusion between the derived package and this\nSoftware will exist.\n\nWarranty\n--------\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL NORMAN WALSH OR ANY OTHER\nCONTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nContacting the Author\n---------------------\nThe DocBook XSL stylesheets are maintained by Norman Walsh,\n\u003cndw@nwalsh.com\u003e, and members of the DocBook Project,\n\u003cdocbook-developers@sf.net\u003e\n", + "standardLicenseTemplate": "\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright --------- Copyright (C) 1999-2007 Norman Walsh Copyright (C) 2003 Jiří Kosek Copyright (C) 2004-2007 Steve Ball Copyright (C) 2005-2014 The DocBook Project Copyright (C) 2011-2012 O\u0027Reilly Media\";match\u003d\".{0,5000}\"\u003e\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ``Software\u0027\u0027), 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:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nExcept as contained in this notice, the names of individuals credited with contribution to this software shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the individuals in question.\n\nAny stylesheet derived from this Software that is publically distributed will be identified with a different name and the version strings in any derived Software will be changed so that no possibility of confusion between the derived package and this Software will exist.\n\nWarranty\n\n--------\n\nTHE 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 NORMAN WALSH OR ANY OTHER CONTRIBUTOR 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.\n\n\u003c\u003cbeginOptional\u003e\u003eContacting the Author\n\n---------------------\n\nThe DocBook XSL stylesheets are maintained by Norman Walsh, \u003cndw@nwalsh.com\u003e, and members of the DocBook Project, \u003cdocbook-developers@sf.net\u003e\n\n\u003c\u003cendOptional\u003e\u003e", + "name": "DocBook XML License", + "licenseId": "DocBook-XML", + "crossRef": [ + { + "match": "false", + "url": "https://github.com/docbook/xslt10-stylesheets/blob/efd62655c11cc8773708df7a843613fa1e932bf8/xsl/COPYING#L27", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:44:02Z", + "isWayBackLink": false, + "order": 0 + } + ], + "seeAlso": [ + "https://github.com/docbook/xslt10-stylesheets/blob/efd62655c11cc8773708df7a843613fa1e932bf8/xsl/COPYING#L27" + ], + "isOsiApproved": false, + "licenseTextHtml": "\n \u003cvar class\u003d\"replaceable-license-text\"\u003e \n Copyright\u003cbr /\u003e\n\n \u003cbr /\u003e\n\n ---------\u003cbr /\u003e\n\n \u003cbr /\u003e\n\n Copyright (C) 1999-2007 Norman Walsh\n \u003cbr /\u003e\n\n Copyright (C) 2003 Jiří Kosek\n \u003cbr /\u003e\n\n Copyright (C) 2004-2007 Steve Ball\n \u003cbr /\u003e\n\n Copyright (C) 2005-2014 The DocBook Project\n \u003cbr /\u003e\n\n Copyright (C) 2011-2012 O\u0026apos;Reilly Media\n \u003c/var\u003e\n \u003cp\u003e\n Permission is hereby granted, free of charge, to any person\n obtaining a copy of this software and associated documentation\n files (the ``Software\u0026apos;\u0026apos;), to deal in the Software without\n restriction, including without limitation the rights to use,\n copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software\n is furnished to do so, subject to the following conditions:\n \u003c/p\u003e\n\n \u003cp\u003e\n The above copyright notice and this permission notice shall be\n included in all copies or substantial portions of the Software.\n \u003c/p\u003e\n\n \u003cp\u003e\n Except as contained in this notice, the names of individuals\n credited with contribution to this software shall not\n be used in advertising or otherwise to promote the sale,\n use or other dealings in this Software without prior\n written authorization from the individuals in question.\n \u003c/p\u003e\n\n \u003cp\u003e\n Any stylesheet derived from this Software that is\n publically distributed will be identified with a different\n name and the version strings in any derived Software\n will be changed so that no possibility of confusion\n between the derived package and this Software will exist.\n \u003c/p\u003e\n\n \u003cp\u003e\n Warranty\n \u003cbr /\u003e\n\n--------\n \u003cbr /\u003e\n\n THE SOFTWARE IS PROVIDED \u0026quot;AS IS\u0026quot;, WITHOUT WARRANTY OF ANY\n KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\n PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL NORMAN WALSH\n OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES\n OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT\n OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH\n THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n \u003c/p\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eContacting the Author\n \u003cbr /\u003e\n\n---------------------\n \u003cbr /\u003e\n\nThe DocBook XSL stylesheets are maintained by\n Norman Walsh, \u0026lt;ndw@nwalsh.com\u0026gt;, and members of\n the DocBook Project, \u0026lt;docbook-developers@sf.net\u0026gt; \u003c/p\u003e\n\n \u003c/div\u003e\n " +} \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/Dotseqn.json b/src/main/resources/license-list-data/json/details/Dotseqn.json index a5d1d759ad..2278a3794e 100644 --- a/src/main/resources/license-list-data/json/details/Dotseqn.json +++ b/src/main/resources/license-list-data/json/details/Dotseqn.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Dotseqn", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:16Z", + "timestamp": "2024-08-19T17:45:40Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ECL-1.0.json b/src/main/resources/license-list-data/json/details/ECL-1.0.json index 44d00956a9..0d5c703a74 100644 --- a/src/main/resources/license-list-data/json/details/ECL-1.0.json +++ b/src/main/resources/license-list-data/json/details/ECL-1.0.json @@ -12,7 +12,7 @@ "url": "https://opensource.org/licenses/ECL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:09Z", + "timestamp": "2024-08-19T17:37:51Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ECL-2.0.json b/src/main/resources/license-list-data/json/details/ECL-2.0.json index 2fbdf1d97b..26239d1490 100644 --- a/src/main/resources/license-list-data/json/details/ECL-2.0.json +++ b/src/main/resources/license-list-data/json/details/ECL-2.0.json @@ -14,7 +14,7 @@ "url": "https://opensource.org/licenses/ECL-2.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:42Z", + "timestamp": "2024-08-19T17:40:06Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/EFL-1.0.json b/src/main/resources/license-list-data/json/details/EFL-1.0.json index 8ca7684ceb..2a448ce8bf 100644 --- a/src/main/resources/license-list-data/json/details/EFL-1.0.json +++ b/src/main/resources/license-list-data/json/details/EFL-1.0.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/EFL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:07Z", + "timestamp": "2024-08-19T17:35:24Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "http://www.eiffel-nice.org/license/forum.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:07Z", + "timestamp": "2024-08-19T17:35:24Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/EFL-2.0.json b/src/main/resources/license-list-data/json/details/EFL-2.0.json index 1ce40a4ae1..d42f145009 100644 --- a/src/main/resources/license-list-data/json/details/EFL-2.0.json +++ b/src/main/resources/license-list-data/json/details/EFL-2.0.json @@ -7,22 +7,22 @@ "licenseId": "EFL-2.0", "crossRef": [ { - "match": "false", - "url": "http://www.eiffel-nice.org/license/eiffel-forum-license-2.html", + "match": "N/A", + "url": "https://opensource.org/licenses/EFL-2.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:49Z", + "timestamp": "2024-08-19T17:42:33Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { - "match": "N/A", - "url": "https://opensource.org/licenses/EFL-2.0", + "match": "false", + "url": "http://www.eiffel-nice.org/license/eiffel-forum-license-2.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:49Z", + "timestamp": "2024-08-19T17:42:33Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/EPICS.json b/src/main/resources/license-list-data/json/details/EPICS.json index 1696642438..8ccb114ee8 100644 --- a/src/main/resources/license-list-data/json/details/EPICS.json +++ b/src/main/resources/license-list-data/json/details/EPICS.json @@ -10,7 +10,7 @@ "url": "https://epics.anl.gov/license/open.php", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:07Z", + "timestamp": "2024-08-19T17:45:38Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/EPL-1.0.json b/src/main/resources/license-list-data/json/details/EPL-1.0.json index c1ba9a6cb0..7ebaa7dc34 100644 --- a/src/main/resources/license-list-data/json/details/EPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/EPL-1.0.json @@ -8,22 +8,22 @@ "licenseId": "EPL-1.0", "crossRef": [ { - "match": "true", - "url": "http://www.eclipse.org/legal/epl-v10.html", + "match": "N/A", + "url": "https://opensource.org/licenses/EPL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:49Z", + "timestamp": "2024-08-19T17:35:44Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { - "match": "N/A", - "url": "https://opensource.org/licenses/EPL-1.0", + "match": "true", + "url": "http://www.eclipse.org/legal/epl-v10.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:50Z", + "timestamp": "2024-08-19T17:35:44Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/EPL-2.0.json b/src/main/resources/license-list-data/json/details/EPL-2.0.json index 53419ade14..67967b6856 100644 --- a/src/main/resources/license-list-data/json/details/EPL-2.0.json +++ b/src/main/resources/license-list-data/json/details/EPL-2.0.json @@ -9,21 +9,21 @@ "crossRef": [ { "match": "true", - "url": "https://www.eclipse.org/legal/epl-2.0", + "url": "https://www.opensource.org/licenses/EPL-2.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:51Z", + "timestamp": "2024-08-19T17:38:11Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { - "match": "true", - "url": "https://www.opensource.org/licenses/EPL-2.0", + "match": "false", + "url": "https://www.eclipse.org/legal/epl-2.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:52Z", + "timestamp": "2024-08-19T17:38:12Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/EUDatagrid.json b/src/main/resources/license-list-data/json/details/EUDatagrid.json index 1fe66f1f9e..22cb4651e4 100644 --- a/src/main/resources/license-list-data/json/details/EUDatagrid.json +++ b/src/main/resources/license-list-data/json/details/EUDatagrid.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/EUDatagrid", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:35Z", + "timestamp": "2024-08-19T17:40:54Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "http://eu-datagrid.web.cern.ch/eu-datagrid/license.html", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:20:35Z", + "timestamp": "2024-08-19T17:40:54Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/EUPL-1.0.json b/src/main/resources/license-list-data/json/details/EUPL-1.0.json index 4a90ba581f..3d6b3b134b 100644 --- a/src/main/resources/license-list-data/json/details/EUPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/EUPL-1.0.json @@ -8,21 +8,21 @@ "crossRef": [ { "match": "false", - "url": "http://ec.europa.eu/idabc/servlets/Doc027f.pdf?id\u003d31096", + "url": "http://ec.europa.eu/idabc/en/document/7330.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:33Z", + "timestamp": "2024-08-19T17:40:35Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { "match": "false", - "url": "http://ec.europa.eu/idabc/en/document/7330.html", + "url": "http://ec.europa.eu/idabc/servlets/Doc027f.pdf?id\u003d31096", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:34Z", + "timestamp": "2024-08-19T17:40:38Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/EUPL-1.1.json b/src/main/resources/license-list-data/json/details/EUPL-1.1.json index 7ec5351d98..75ef902a69 100644 --- a/src/main/resources/license-list-data/json/details/EUPL-1.1.json +++ b/src/main/resources/license-list-data/json/details/EUPL-1.1.json @@ -12,7 +12,7 @@ "url": "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl1.1.-licence-en_0.pdf", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:22Z", + "timestamp": "2024-08-19T17:44:24Z", "isWayBackLink": false, "order": 1 }, @@ -21,7 +21,7 @@ "url": "https://joinup.ec.europa.eu/software/page/eupl/licence-eupl", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:23Z", + "timestamp": "2024-08-19T17:44:24Z", "isWayBackLink": false, "order": 0 }, @@ -30,7 +30,7 @@ "url": "https://opensource.org/licenses/EUPL-1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:23Z", + "timestamp": "2024-08-19T17:44:24Z", "isWayBackLink": false, "order": 2 } diff --git a/src/main/resources/license-list-data/json/details/EUPL-1.2.json b/src/main/resources/license-list-data/json/details/EUPL-1.2.json index 8adcd629a3..69d68c0f76 100644 --- a/src/main/resources/license-list-data/json/details/EUPL-1.2.json +++ b/src/main/resources/license-list-data/json/details/EUPL-1.2.json @@ -8,29 +8,29 @@ "licenseId": "EUPL-1.2", "crossRef": [ { - "match": "false", - "url": "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl_v1.2_en.pdf", + "match": "N/A", + "url": "https://opensource.org/licenses/EUPL-1.2", "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:29:52Z", + "isLive": false, + "timestamp": "2024-08-19T17:35:09Z", "isWayBackLink": false, - "order": 1 + "order": 5 }, { "match": "false", - "url": "https://joinup.ec.europa.eu/page/eupl-text-11-12", + "url": "http://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri\u003dCELEX:32017D0863", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:53Z", + "timestamp": "2024-08-19T17:35:09Z", "isWayBackLink": false, - "order": 0 + "order": 4 }, { "match": "N/A", "url": "https://joinup.ec.europa.eu/sites/default/files/inline-files/EUPL%20v1_2%20EN(1).txt", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:29:53Z", + "timestamp": "2024-08-19T17:35:10Z", "isWayBackLink": false, "order": 3 }, @@ -39,27 +39,27 @@ "url": "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/2020-03/EUPL-1.2%20EN.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:53Z", + "timestamp": "2024-08-19T17:35:10Z", "isWayBackLink": false, "order": 2 }, { - "match": "N/A", - "url": "https://opensource.org/licenses/EUPL-1.2", + "match": "false", + "url": "https://joinup.ec.europa.eu/sites/default/files/custom-page/attachment/eupl_v1.2_en.pdf", "isValid": true, - "isLive": false, - "timestamp": "2024-05-22T17:29:53Z", + "isLive": true, + "timestamp": "2024-08-19T17:35:10Z", "isWayBackLink": false, - "order": 5 + "order": 1 }, { "match": "false", - "url": "http://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri\u003dCELEX:32017D0863", + "url": "https://joinup.ec.europa.eu/page/eupl-text-11-12", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:53Z", + "timestamp": "2024-08-19T17:35:11Z", "isWayBackLink": false, - "order": 4 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Elastic-2.0.json b/src/main/resources/license-list-data/json/details/Elastic-2.0.json index e5231462e8..847d0c47d0 100644 --- a/src/main/resources/license-list-data/json/details/Elastic-2.0.json +++ b/src/main/resources/license-list-data/json/details/Elastic-2.0.json @@ -11,7 +11,7 @@ "url": "https://github.com/elastic/elasticsearch/blob/master/licenses/ELASTIC-LICENSE-2.0.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:16Z", + "timestamp": "2024-08-19T17:41:03Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://www.elastic.co/licensing/elastic-license", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:16Z", + "timestamp": "2024-08-19T17:41:03Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Entessa.json b/src/main/resources/license-list-data/json/details/Entessa.json index 7ba5ba8d5c..0a0d6c4bb3 100644 --- a/src/main/resources/license-list-data/json/details/Entessa.json +++ b/src/main/resources/license-list-data/json/details/Entessa.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/Entessa", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:29Z", + "timestamp": "2024-08-19T17:36:43Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ErlPL-1.1.json b/src/main/resources/license-list-data/json/details/ErlPL-1.1.json index 54b06fb645..af3075dc32 100644 --- a/src/main/resources/license-list-data/json/details/ErlPL-1.1.json +++ b/src/main/resources/license-list-data/json/details/ErlPL-1.1.json @@ -11,7 +11,7 @@ "url": "http://www.erlang.org/EPLICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:24Z", + "timestamp": "2024-08-19T17:45:12Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Eurosym.json b/src/main/resources/license-list-data/json/details/Eurosym.json index bac529126a..c8cf3f75cd 100644 --- a/src/main/resources/license-list-data/json/details/Eurosym.json +++ b/src/main/resources/license-list-data/json/details/Eurosym.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Eurosym", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:39Z", + "timestamp": "2024-08-19T17:48:59Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/FBM.json b/src/main/resources/license-list-data/json/details/FBM.json index 90e94ced72..df23c09119 100644 --- a/src/main/resources/license-list-data/json/details/FBM.json +++ b/src/main/resources/license-list-data/json/details/FBM.json @@ -10,7 +10,7 @@ "url": "https://github.com/SWI-Prolog/packages-xpce/blob/161a40cd82004f731ba48024f9d30af388a7edf5/src/img/gifwrite.c#L21-L26", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:47Z", + "timestamp": "2024-08-19T17:49:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/FDK-AAC.json b/src/main/resources/license-list-data/json/details/FDK-AAC.json index 70be630487..544d9b2a8d 100644 --- a/src/main/resources/license-list-data/json/details/FDK-AAC.json +++ b/src/main/resources/license-list-data/json/details/FDK-AAC.json @@ -10,7 +10,7 @@ "url": "https://directory.fsf.org/wiki/License:Fdk", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:37Z", + "timestamp": "2024-08-19T17:39:39Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/FDK-AAC", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:39Z", + "timestamp": "2024-08-19T17:39:39Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/FSFAP-no-warranty-disclaimer.json b/src/main/resources/license-list-data/json/details/FSFAP-no-warranty-disclaimer.json index 9f346558a8..88451601cb 100644 --- a/src/main/resources/license-list-data/json/details/FSFAP-no-warranty-disclaimer.json +++ b/src/main/resources/license-list-data/json/details/FSFAP-no-warranty-disclaimer.json @@ -11,7 +11,7 @@ "url": "https://git.savannah.gnu.org/cgit/wget.git/tree/util/trunc.c?h\u003dv1.21.3\u0026id\u003d40747a11e44ced5a8ac628a41f879ced3e2ebce9#n6", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:16Z", + "timestamp": "2024-08-19T17:46:05Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/FSFAP.json b/src/main/resources/license-list-data/json/details/FSFAP.json index 504f6a0129..8acc188839 100644 --- a/src/main/resources/license-list-data/json/details/FSFAP.json +++ b/src/main/resources/license-list-data/json/details/FSFAP.json @@ -11,7 +11,7 @@ "url": "https://www.gnu.org/prep/maintain/html_node/License-Notices-for-Other-Files.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:35Z", + "timestamp": "2024-08-19T17:42:14Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/FSFUL.json b/src/main/resources/license-list-data/json/details/FSFUL.json index 8a6c0f6598..602f2da789 100644 --- a/src/main/resources/license-list-data/json/details/FSFUL.json +++ b/src/main/resources/license-list-data/json/details/FSFUL.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:03Z", + "timestamp": "2024-08-19T17:35:08Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/FSFULLR.json b/src/main/resources/license-list-data/json/details/FSFULLR.json index 03e1b118fc..cde30cbc59 100644 --- a/src/main/resources/license-list-data/json/details/FSFULLR.json +++ b/src/main/resources/license-list-data/json/details/FSFULLR.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License#License_Retention_Variant", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:16Z", + "timestamp": "2024-08-19T17:44:28Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/FSFULLRWD.json b/src/main/resources/license-list-data/json/details/FSFULLRWD.json index 7380c83bec..4872da2705 100644 --- a/src/main/resources/license-list-data/json/details/FSFULLRWD.json +++ b/src/main/resources/license-list-data/json/details/FSFULLRWD.json @@ -10,7 +10,7 @@ "url": "https://lists.gnu.org/archive/html/autoconf/2012-04/msg00061.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:06Z", + "timestamp": "2024-08-19T17:49:48Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/FTL.json b/src/main/resources/license-list-data/json/details/FTL.json index 683b2c061b..418bcc6609 100644 --- a/src/main/resources/license-list-data/json/details/FTL.json +++ b/src/main/resources/license-list-data/json/details/FTL.json @@ -7,12 +7,21 @@ "licenseComments": "This license was released 27 Jan 2006", "licenseId": "FTL", "crossRef": [ + { + "match": "N/A", + "url": "http://freetype.fis.uniroma2.it/FTL.TXT", + "isValid": true, + "isLive": false, + "timestamp": "2024-08-19T17:37:30Z", + "isWayBackLink": false, + "order": 0 + }, { "match": "N/A", "url": "http://gitlab.freedesktop.org/freetype/freetype/-/raw/master/docs/FTL.TXT", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:25:31Z", + "timestamp": "2024-08-19T17:37:30Z", "isWayBackLink": false, "order": 2 }, @@ -21,18 +30,9 @@ "url": "http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:31Z", + "timestamp": "2024-08-19T17:37:31Z", "isWayBackLink": false, "order": 1 - }, - { - "match": "N/A", - "url": "http://freetype.fis.uniroma2.it/FTL.TXT", - "isValid": true, - "isLive": false, - "timestamp": "2024-05-22T17:25:41Z", - "isWayBackLink": false, - "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Fair.json b/src/main/resources/license-list-data/json/details/Fair.json index e57afac0e2..b203ec5258 100644 --- a/src/main/resources/license-list-data/json/details/Fair.json +++ b/src/main/resources/license-list-data/json/details/Fair.json @@ -10,7 +10,7 @@ "url": "https://web.archive.org/web/20150926120323/http://fairlicense.org/", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:30:17Z", + "timestamp": "2024-08-19T17:37:17Z", "isWayBackLink": false, "order": 0 }, @@ -19,7 +19,7 @@ "url": "https://opensource.org/licenses/Fair", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:17Z", + "timestamp": "2024-08-19T17:37:17Z", "isWayBackLink": false, "order": 1 } diff --git a/src/main/resources/license-list-data/json/details/Ferguson-Twofish.json b/src/main/resources/license-list-data/json/details/Ferguson-Twofish.json index 020df08bf3..9f30291335 100644 --- a/src/main/resources/license-list-data/json/details/Ferguson-Twofish.json +++ b/src/main/resources/license-list-data/json/details/Ferguson-Twofish.json @@ -10,7 +10,7 @@ "url": "https://github.com/wernerd/ZRTPCPP/blob/6b3cd8e6783642292bad0c21e3e5e5ce45ff3e03/cryptcommon/twofish.c#L113C3-L127", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:55Z", + "timestamp": "2024-08-19T17:40:05Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Frameworx-1.0.json b/src/main/resources/license-list-data/json/details/Frameworx-1.0.json index 2fa29fc1e2..d39b220fb5 100644 --- a/src/main/resources/license-list-data/json/details/Frameworx-1.0.json +++ b/src/main/resources/license-list-data/json/details/Frameworx-1.0.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/Frameworx-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:39Z", + "timestamp": "2024-08-19T17:36:26Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/FreeBSD-DOC.json b/src/main/resources/license-list-data/json/details/FreeBSD-DOC.json index bf34ac36da..c19bd048c0 100644 --- a/src/main/resources/license-list-data/json/details/FreeBSD-DOC.json +++ b/src/main/resources/license-list-data/json/details/FreeBSD-DOC.json @@ -10,7 +10,7 @@ "url": "https://www.freebsd.org/copyright/freebsd-doc-license/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:04Z", + "timestamp": "2024-08-19T17:36:07Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/FreeImage.json b/src/main/resources/license-list-data/json/details/FreeImage.json index 3c469a2b08..42c00c984f 100644 --- a/src/main/resources/license-list-data/json/details/FreeImage.json +++ b/src/main/resources/license-list-data/json/details/FreeImage.json @@ -11,7 +11,7 @@ "url": "http://freeimage.sourceforge.net/freeimage-license.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:48Z", + "timestamp": "2024-08-19T17:37:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Furuseth.json b/src/main/resources/license-list-data/json/details/Furuseth.json index d55ae3153d..e1758b5731 100644 --- a/src/main/resources/license-list-data/json/details/Furuseth.json +++ b/src/main/resources/license-list-data/json/details/Furuseth.json @@ -10,7 +10,7 @@ "url": "https://git.openldap.org/openldap/openldap/-/blob/master/COPYRIGHT?ref_type\u003dheads#L39-51", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:12Z", + "timestamp": "2024-08-19T17:38:01Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GCR-docs.json b/src/main/resources/license-list-data/json/details/GCR-docs.json index f8977f04d2..875604ffc4 100644 --- a/src/main/resources/license-list-data/json/details/GCR-docs.json +++ b/src/main/resources/license-list-data/json/details/GCR-docs.json @@ -10,7 +10,7 @@ "url": "https://github.com/GNOME/gcr/blob/master/docs/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:04Z", + "timestamp": "2024-08-19T17:38:01Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GD.json b/src/main/resources/license-list-data/json/details/GD.json index 720f4012e4..5706e18a32 100644 --- a/src/main/resources/license-list-data/json/details/GD.json +++ b/src/main/resources/license-list-data/json/details/GD.json @@ -10,7 +10,7 @@ "url": "https://libgd.github.io/manuals/2.3.0/files/license-txt.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:01Z", + "timestamp": "2024-08-19T17:49:10Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.1-invariants-only.json b/src/main/resources/license-list-data/json/details/GFDL-1.1-invariants-only.json index 3cbc41d775..330385da81 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.1-invariants-only.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.1-invariants-only.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:23Z", + "timestamp": "2024-08-19T17:36:57Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.1-invariants-or-later.json b/src/main/resources/license-list-data/json/details/GFDL-1.1-invariants-or-later.json index 798dd3b3d7..efd820b267 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.1-invariants-or-later.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.1-invariants-or-later.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:56Z", + "timestamp": "2024-08-19T17:35:23Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.1-no-invariants-only.json b/src/main/resources/license-list-data/json/details/GFDL-1.1-no-invariants-only.json index 649d9716bb..df15340336 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.1-no-invariants-only.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.1-no-invariants-only.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:31Z", + "timestamp": "2024-08-19T17:35:11Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.1-no-invariants-or-later.json b/src/main/resources/license-list-data/json/details/GFDL-1.1-no-invariants-or-later.json index f32d0ca00d..a9fed4875c 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.1-no-invariants-or-later.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.1-no-invariants-or-later.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:34Z", + "timestamp": "2024-08-19T17:38:21Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.1-only.json b/src/main/resources/license-list-data/json/details/GFDL-1.1-only.json index 1183ea5fb0..a5b10b5cc1 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.1-only.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.1-only.json @@ -14,7 +14,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:21Z", + "timestamp": "2024-08-19T17:50:21Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.1-or-later.json b/src/main/resources/license-list-data/json/details/GFDL-1.1-or-later.json index 5a3f337f70..720fcfe015 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.1-or-later.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.1-or-later.json @@ -14,7 +14,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:33:18Z", + "timestamp": "2024-08-19T17:47:00Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.1.json b/src/main/resources/license-list-data/json/details/GFDL-1.1.json index 753b8953a9..4cd6c66614 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.1.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.1.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.1.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:04Z", + "timestamp": "2024-08-19T17:36:24Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.2-invariants-only.json b/src/main/resources/license-list-data/json/details/GFDL-1.2-invariants-only.json index 33ad5a68f4..c548cdc399 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.2-invariants-only.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.2-invariants-only.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:36Z", + "timestamp": "2024-08-19T17:42:52Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.2-invariants-or-later.json b/src/main/resources/license-list-data/json/details/GFDL-1.2-invariants-or-later.json index 08cdf8a0d2..cdcb5bdd8a 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.2-invariants-or-later.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.2-invariants-or-later.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:29Z", + "timestamp": "2024-08-19T17:40:49Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.2-no-invariants-only.json b/src/main/resources/license-list-data/json/details/GFDL-1.2-no-invariants-only.json index e830f2b6ea..6379611601 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.2-no-invariants-only.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.2-no-invariants-only.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:47Z", + "timestamp": "2024-08-19T17:36:34Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.2-no-invariants-or-later.json b/src/main/resources/license-list-data/json/details/GFDL-1.2-no-invariants-or-later.json index 2081cfa254..cec0db7fbd 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.2-no-invariants-or-later.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.2-no-invariants-or-later.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:31Z", + "timestamp": "2024-08-19T17:36:50Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.2-only.json b/src/main/resources/license-list-data/json/details/GFDL-1.2-only.json index 81d45b4e81..540e1fa8d7 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.2-only.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.2-only.json @@ -14,7 +14,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:33:30Z", + "timestamp": "2024-08-19T17:38:34Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.2-or-later.json b/src/main/resources/license-list-data/json/details/GFDL-1.2-or-later.json index 6884151c5a..cfafacba28 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.2-or-later.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.2-or-later.json @@ -14,7 +14,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:39Z", + "timestamp": "2024-08-19T17:46:43Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.2.json b/src/main/resources/license-list-data/json/details/GFDL-1.2.json index 586fa5a333..35fcbb4436 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.2.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.2.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/fdl-1.2.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:43Z", + "timestamp": "2024-08-19T17:37:18Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.3-invariants-only.json b/src/main/resources/license-list-data/json/details/GFDL-1.3-invariants-only.json index 4819979be9..c98aa85c47 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.3-invariants-only.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.3-invariants-only.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/fdl-1.3.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:55Z", + "timestamp": "2024-08-19T17:36:29Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.3-invariants-or-later.json b/src/main/resources/license-list-data/json/details/GFDL-1.3-invariants-or-later.json index 1ddc4b910e..6d5345bc76 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.3-invariants-or-later.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.3-invariants-or-later.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/fdl-1.3.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:42Z", + "timestamp": "2024-08-19T17:39:42Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.3-no-invariants-only.json b/src/main/resources/license-list-data/json/details/GFDL-1.3-no-invariants-only.json index 0a464971b9..69f62edca2 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.3-no-invariants-only.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.3-no-invariants-only.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/fdl-1.3.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:06Z", + "timestamp": "2024-08-19T17:35:24Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.3-no-invariants-or-later.json b/src/main/resources/license-list-data/json/details/GFDL-1.3-no-invariants-or-later.json index 2500a7abe7..ad44222429 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.3-no-invariants-or-later.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.3-no-invariants-or-later.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/fdl-1.3.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:15Z", + "timestamp": "2024-08-19T17:36:21Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.3-only.json b/src/main/resources/license-list-data/json/details/GFDL-1.3-only.json index 117446fb9a..46ceb604bf 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.3-only.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.3-only.json @@ -14,7 +14,7 @@ "url": "https://www.gnu.org/licenses/fdl-1.3.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:18Z", + "timestamp": "2024-08-19T17:49:41Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.3-or-later.json b/src/main/resources/license-list-data/json/details/GFDL-1.3-or-later.json index 8b0f49da00..6e10caf170 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.3-or-later.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.3-or-later.json @@ -14,7 +14,7 @@ "url": "https://www.gnu.org/licenses/fdl-1.3.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:54Z", + "timestamp": "2024-08-19T17:37:55Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GFDL-1.3.json b/src/main/resources/license-list-data/json/details/GFDL-1.3.json index b21a2e33fb..e049a22d55 100644 --- a/src/main/resources/license-list-data/json/details/GFDL-1.3.json +++ b/src/main/resources/license-list-data/json/details/GFDL-1.3.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/fdl-1.3.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:26Z", + "timestamp": "2024-08-19T17:35:21Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GL2PS.json b/src/main/resources/license-list-data/json/details/GL2PS.json index 5a7f426f38..11a41bb715 100644 --- a/src/main/resources/license-list-data/json/details/GL2PS.json +++ b/src/main/resources/license-list-data/json/details/GL2PS.json @@ -10,7 +10,7 @@ "url": "http://www.geuz.org/gl2ps/COPYING.GL2PS", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:35Z", + "timestamp": "2024-08-19T17:37:14Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GLWTPL.json b/src/main/resources/license-list-data/json/details/GLWTPL.json index 1c2b8c835c..13ef921195 100644 --- a/src/main/resources/license-list-data/json/details/GLWTPL.json +++ b/src/main/resources/license-list-data/json/details/GLWTPL.json @@ -10,7 +10,7 @@ "url": "https://github.com/me-shaon/GLWTPL/commit/da5f6bc734095efbacb442c0b31e33a65b9d6e85", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:01Z", + "timestamp": "2024-08-19T17:36:08Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-1.0+.json b/src/main/resources/license-list-data/json/details/GPL-1.0+.json index 60f61116b3..fb72d98431 100644 --- a/src/main/resources/license-list-data/json/details/GPL-1.0+.json +++ b/src/main/resources/license-list-data/json/details/GPL-1.0+.json @@ -12,7 +12,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:26Z", + "timestamp": "2024-08-19T17:35:25Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-1.0-only.json b/src/main/resources/license-list-data/json/details/GPL-1.0-only.json index 67466b953f..ea7e972202 100644 --- a/src/main/resources/license-list-data/json/details/GPL-1.0-only.json +++ b/src/main/resources/license-list-data/json/details/GPL-1.0-only.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:29Z", + "timestamp": "2024-08-19T17:46:45Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-1.0-or-later.json b/src/main/resources/license-list-data/json/details/GPL-1.0-or-later.json index 3ce01464c8..325138d898 100644 --- a/src/main/resources/license-list-data/json/details/GPL-1.0-or-later.json +++ b/src/main/resources/license-list-data/json/details/GPL-1.0-or-later.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:33:12Z", + "timestamp": "2024-08-19T17:46:32Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-1.0.json b/src/main/resources/license-list-data/json/details/GPL-1.0.json index 97322517cc..69d901ef60 100644 --- a/src/main/resources/license-list-data/json/details/GPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/GPL-1.0.json @@ -12,7 +12,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/gpl-1.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:39Z", + "timestamp": "2024-08-19T17:40:09Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-2.0+.json b/src/main/resources/license-list-data/json/details/GPL-2.0+.json index c8eea9001c..6b1c108400 100644 --- a/src/main/resources/license-list-data/json/details/GPL-2.0+.json +++ b/src/main/resources/license-list-data/json/details/GPL-2.0+.json @@ -8,23 +8,23 @@ "licenseId": "GPL-2.0+", "standardLicenseHeader": "\u003cone line to give the program\u0027s name and an idea of what it does.\u003e\n\nCopyright (C) \u003cyyyy\u003e \u003cname of author\u003e\n\nThis program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n\nThis program 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 General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA .\n\n", "crossRef": [ - { - "match": "false", - "url": "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:18:31Z", - "isWayBackLink": false, - "order": 0 - }, { "match": "N/A", "url": "https://opensource.org/licenses/GPL-2.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:31Z", + "timestamp": "2024-08-19T17:35:30Z", "isWayBackLink": false, "order": 1 + }, + { + "match": "false", + "url": "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:35:30Z", + "isWayBackLink": false, + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/GPL-2.0-only.json b/src/main/resources/license-list-data/json/details/GPL-2.0-only.json index 2e81f840eb..e949e550cc 100644 --- a/src/main/resources/license-list-data/json/details/GPL-2.0-only.json +++ b/src/main/resources/license-list-data/json/details/GPL-2.0-only.json @@ -14,7 +14,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:57Z", + "timestamp": "2024-08-19T17:43:41Z", "isWayBackLink": false, "order": 1 }, @@ -23,7 +23,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:57Z", + "timestamp": "2024-08-19T17:43:41Z", "isWayBackLink": false, "order": 0 }, @@ -32,7 +32,7 @@ "url": "https://opensource.org/licenses/GPL-2.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:31:57Z", + "timestamp": "2024-08-19T17:43:41Z", "isWayBackLink": false, "order": 2 } diff --git a/src/main/resources/license-list-data/json/details/GPL-2.0-or-later.json b/src/main/resources/license-list-data/json/details/GPL-2.0-or-later.json index 2fb92470c9..45a2dfee54 100644 --- a/src/main/resources/license-list-data/json/details/GPL-2.0-or-later.json +++ b/src/main/resources/license-list-data/json/details/GPL-2.0-or-later.json @@ -9,23 +9,23 @@ "licenseId": "GPL-2.0-or-later", "standardLicenseHeader": "\u003cone line to give the program\u0027s name and an idea of what it does.\u003e\n\nCopyright (C) \u003cyyyy\u003e \u003cname of author\u003e\n\nThis program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n\nThis program 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 General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA .\n\n", "crossRef": [ - { - "match": "N/A", - "url": "https://opensource.org/licenses/GPL-2.0", - "isValid": true, - "isLive": false, - "timestamp": "2024-05-22T17:19:37Z", - "isWayBackLink": false, - "order": 1 - }, { "match": "false", "url": "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:37Z", + "timestamp": "2024-08-19T17:39:47Z", "isWayBackLink": false, "order": 0 + }, + { + "match": "N/A", + "url": "https://opensource.org/licenses/GPL-2.0", + "isValid": true, + "isLive": false, + "timestamp": "2024-08-19T17:39:47Z", + "isWayBackLink": false, + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/GPL-2.0-with-GCC-exception.json b/src/main/resources/license-list-data/json/details/GPL-2.0-with-GCC-exception.json index e13c645708..4d07695af7 100644 --- a/src/main/resources/license-list-data/json/details/GPL-2.0-with-GCC-exception.json +++ b/src/main/resources/license-list-data/json/details/GPL-2.0-with-GCC-exception.json @@ -11,7 +11,7 @@ "url": "https://gcc.gnu.org/git/?p\u003dgcc.git;a\u003dblob;f\u003dgcc/libgcc1.c;h\u003d762f5143fc6eed57b6797c82710f3538aa52b40b;hb\u003dcb143a3ce4fb417c68f5fa2691a1b1b1053dfba9#l10", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:44Z", + "timestamp": "2024-08-19T17:42:36Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-2.0-with-autoconf-exception.json b/src/main/resources/license-list-data/json/details/GPL-2.0-with-autoconf-exception.json index cf0dafd783..f78c734392 100644 --- a/src/main/resources/license-list-data/json/details/GPL-2.0-with-autoconf-exception.json +++ b/src/main/resources/license-list-data/json/details/GPL-2.0-with-autoconf-exception.json @@ -11,7 +11,7 @@ "url": "http://ac-archive.sourceforge.net/doc/copyright.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:26Z", + "timestamp": "2024-08-19T17:43:25Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-2.0-with-bison-exception.json b/src/main/resources/license-list-data/json/details/GPL-2.0-with-bison-exception.json index fe5782bff6..02938cb0b6 100644 --- a/src/main/resources/license-list-data/json/details/GPL-2.0-with-bison-exception.json +++ b/src/main/resources/license-list-data/json/details/GPL-2.0-with-bison-exception.json @@ -11,7 +11,7 @@ "url": "http://git.savannah.gnu.org/cgit/bison.git/tree/data/yacc.c?id\u003d193d7c7054ba7197b0789e14965b739162319b5e#n141", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:04Z", + "timestamp": "2024-08-19T17:43:56Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-2.0-with-classpath-exception.json b/src/main/resources/license-list-data/json/details/GPL-2.0-with-classpath-exception.json index b7e169d8ee..b044beee20 100644 --- a/src/main/resources/license-list-data/json/details/GPL-2.0-with-classpath-exception.json +++ b/src/main/resources/license-list-data/json/details/GPL-2.0-with-classpath-exception.json @@ -11,7 +11,7 @@ "url": "https://www.gnu.org/software/classpath/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:02Z", + "timestamp": "2024-08-19T17:36:02Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-2.0-with-font-exception.json b/src/main/resources/license-list-data/json/details/GPL-2.0-with-font-exception.json index 050e7d5155..08cdeee72e 100644 --- a/src/main/resources/license-list-data/json/details/GPL-2.0-with-font-exception.json +++ b/src/main/resources/license-list-data/json/details/GPL-2.0-with-font-exception.json @@ -11,7 +11,7 @@ "url": "https://www.gnu.org/licenses/gpl-faq.html#FontException", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:25Z", + "timestamp": "2024-08-19T17:47:24Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-2.0.json b/src/main/resources/license-list-data/json/details/GPL-2.0.json index eed8b617e5..02ed8add73 100644 --- a/src/main/resources/license-list-data/json/details/GPL-2.0.json +++ b/src/main/resources/license-list-data/json/details/GPL-2.0.json @@ -13,7 +13,7 @@ "url": "https://opensource.org/licenses/GPL-2.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:51Z", + "timestamp": "2024-08-19T17:48:41Z", "isWayBackLink": false, "order": 1 }, @@ -22,7 +22,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:51Z", + "timestamp": "2024-08-19T17:48:41Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-3.0+.json b/src/main/resources/license-list-data/json/details/GPL-3.0+.json index 5be45a9337..95858e0acc 100644 --- a/src/main/resources/license-list-data/json/details/GPL-3.0+.json +++ b/src/main/resources/license-list-data/json/details/GPL-3.0+.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/gpl-3.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:30Z", + "timestamp": "2024-08-19T17:47:34Z", "isWayBackLink": false, "order": 0 }, @@ -22,7 +22,7 @@ "url": "https://opensource.org/licenses/GPL-3.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:21:30Z", + "timestamp": "2024-08-19T17:47:34Z", "isWayBackLink": false, "order": 1 } diff --git a/src/main/resources/license-list-data/json/details/GPL-3.0-only.json b/src/main/resources/license-list-data/json/details/GPL-3.0-only.json index 0a9d903531..f80e19190b 100644 --- a/src/main/resources/license-list-data/json/details/GPL-3.0-only.json +++ b/src/main/resources/license-list-data/json/details/GPL-3.0-only.json @@ -14,7 +14,7 @@ "url": "https://www.gnu.org/licenses/gpl-3.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:17Z", + "timestamp": "2024-08-19T17:35:19Z", "isWayBackLink": false, "order": 0 }, @@ -23,7 +23,7 @@ "url": "https://opensource.org/licenses/GPL-3.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:19:17Z", + "timestamp": "2024-08-19T17:35:19Z", "isWayBackLink": false, "order": 1 } diff --git a/src/main/resources/license-list-data/json/details/GPL-3.0-or-later.json b/src/main/resources/license-list-data/json/details/GPL-3.0-or-later.json index 0fcb381302..bbe8103a4b 100644 --- a/src/main/resources/license-list-data/json/details/GPL-3.0-or-later.json +++ b/src/main/resources/license-list-data/json/details/GPL-3.0-or-later.json @@ -14,7 +14,7 @@ "url": "https://opensource.org/licenses/GPL-3.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:24:11Z", + "timestamp": "2024-08-19T17:37:50Z", "isWayBackLink": false, "order": 1 }, @@ -23,7 +23,7 @@ "url": "https://www.gnu.org/licenses/gpl-3.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:11Z", + "timestamp": "2024-08-19T17:37:50Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-3.0-with-GCC-exception.json b/src/main/resources/license-list-data/json/details/GPL-3.0-with-GCC-exception.json index 4d450292d0..20ecb3990d 100644 --- a/src/main/resources/license-list-data/json/details/GPL-3.0-with-GCC-exception.json +++ b/src/main/resources/license-list-data/json/details/GPL-3.0-with-GCC-exception.json @@ -11,7 +11,7 @@ "url": "https://www.gnu.org/licenses/gcc-exception-3.1.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:34Z", + "timestamp": "2024-08-19T17:46:07Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-3.0-with-autoconf-exception.json b/src/main/resources/license-list-data/json/details/GPL-3.0-with-autoconf-exception.json index d214239756..51251a0fc6 100644 --- a/src/main/resources/license-list-data/json/details/GPL-3.0-with-autoconf-exception.json +++ b/src/main/resources/license-list-data/json/details/GPL-3.0-with-autoconf-exception.json @@ -11,7 +11,7 @@ "url": "https://www.gnu.org/licenses/autoconf-exception-3.0.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:14Z", + "timestamp": "2024-08-19T17:36:45Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/GPL-3.0.json b/src/main/resources/license-list-data/json/details/GPL-3.0.json index d04c88e603..2de29e60fd 100644 --- a/src/main/resources/license-list-data/json/details/GPL-3.0.json +++ b/src/main/resources/license-list-data/json/details/GPL-3.0.json @@ -9,22 +9,22 @@ "standardLicenseHeader": "Copyright (C) \u003cyear\u003e \u003cname of author\u003e\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3.\n\nThis program 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 General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n", "crossRef": [ { - "match": "false", - "url": "https://www.gnu.org/licenses/gpl-3.0-standalone.html", + "match": "N/A", + "url": "https://opensource.org/licenses/GPL-3.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:46Z", + "timestamp": "2024-08-19T17:42:13Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { - "match": "N/A", - "url": "https://opensource.org/licenses/GPL-3.0", + "match": "false", + "url": "https://www.gnu.org/licenses/gpl-3.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:47Z", + "timestamp": "2024-08-19T17:42:13Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Giftware.json b/src/main/resources/license-list-data/json/details/Giftware.json index 18c4e145d4..0378ebbfcf 100644 --- a/src/main/resources/license-list-data/json/details/Giftware.json +++ b/src/main/resources/license-list-data/json/details/Giftware.json @@ -11,7 +11,7 @@ "url": "http://liballeg.org/license.html#allegro-4-the-giftware-license", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:34Z", + "timestamp": "2024-08-19T17:48:54Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Glide.json b/src/main/resources/license-list-data/json/details/Glide.json index 47759ce088..83c2cbd67d 100644 --- a/src/main/resources/license-list-data/json/details/Glide.json +++ b/src/main/resources/license-list-data/json/details/Glide.json @@ -10,7 +10,7 @@ "url": "http://www.users.on.net/~triforce/glidexp/COPYING.txt", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:20:00Z", + "timestamp": "2024-08-19T17:49:00Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Glulxe.json b/src/main/resources/license-list-data/json/details/Glulxe.json index 0ba9552e5e..ec821e8e08 100644 --- a/src/main/resources/license-list-data/json/details/Glulxe.json +++ b/src/main/resources/license-list-data/json/details/Glulxe.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Glulxe", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:50Z", + "timestamp": "2024-08-19T17:42:05Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Graphics-Gems.json b/src/main/resources/license-list-data/json/details/Graphics-Gems.json index 0916b01e4d..98c9c96edb 100644 --- a/src/main/resources/license-list-data/json/details/Graphics-Gems.json +++ b/src/main/resources/license-list-data/json/details/Graphics-Gems.json @@ -10,7 +10,7 @@ "url": "https://github.com/erich666/GraphicsGems/blob/master/LICENSE.md", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:26Z", + "timestamp": "2024-08-19T17:42:18Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Gutmann.json b/src/main/resources/license-list-data/json/details/Gutmann.json index a418bfe550..537e2d7cca 100644 --- a/src/main/resources/license-list-data/json/details/Gutmann.json +++ b/src/main/resources/license-list-data/json/details/Gutmann.json @@ -10,7 +10,7 @@ "url": "https://www.cs.auckland.ac.nz/~pgut001/dumpasn1.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:43Z", + "timestamp": "2024-08-19T17:35:39Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HIDAPI.json b/src/main/resources/license-list-data/json/details/HIDAPI.json new file mode 100644 index 0000000000..112bc6d360 --- /dev/null +++ b/src/main/resources/license-list-data/json/details/HIDAPI.json @@ -0,0 +1,23 @@ +{ + "isDeprecatedLicenseId": false, + "licenseText": "This software may be used by anyone for any reason so long\nas the copyright notice in the source files remains intact.\n", + "standardLicenseTemplate": "This software may be used by anyone for any reason so long as the copyright notice in the source files remains intact.\n\n", + "name": "HIDAPI License", + "licenseId": "HIDAPI", + "crossRef": [ + { + "match": "false", + "url": "https://github.com/signal11/hidapi/blob/master/LICENSE-orig.txt", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:39:00Z", + "isWayBackLink": false, + "order": 0 + } + ], + "seeAlso": [ + "https://github.com/signal11/hidapi/blob/master/LICENSE-orig.txt" + ], + "isOsiApproved": false, + "licenseTextHtml": "\n \u003cp\u003e\n This software may be used by anyone for any reason so long\n as the copyright notice in the source files remains intact.\n \u003c/p\u003e\n\n " +} \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/HP-1986.json b/src/main/resources/license-list-data/json/details/HP-1986.json index 3671239d1f..5b0fe0ccd0 100644 --- a/src/main/resources/license-list-data/json/details/HP-1986.json +++ b/src/main/resources/license-list-data/json/details/HP-1986.json @@ -10,7 +10,7 @@ "url": "https://sourceware.org/git/?p\u003dnewlib-cygwin.git;a\u003dblob;f\u003dnewlib/libc/machine/hppa/memchr.S;h\u003d1cca3e5e8867aa4bffef1f75a5c1bba25c0c441e;hb\u003dHEAD#l2", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:39Z", + "timestamp": "2024-08-19T17:43:28Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HP-1989.json b/src/main/resources/license-list-data/json/details/HP-1989.json index 814e2a5bc3..bace4b34c3 100644 --- a/src/main/resources/license-list-data/json/details/HP-1989.json +++ b/src/main/resources/license-list-data/json/details/HP-1989.json @@ -10,7 +10,7 @@ "url": "https://github.com/bleargh45/Data-UUID/blob/master/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:33:05Z", + "timestamp": "2024-08-19T17:50:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-DEC.json b/src/main/resources/license-list-data/json/details/HPND-DEC.json index c6f85174fc..155674d978 100644 --- a/src/main/resources/license-list-data/json/details/HPND-DEC.json +++ b/src/main/resources/license-list-data/json/details/HPND-DEC.json @@ -11,7 +11,7 @@ "url": "https://gitlab.freedesktop.org/xorg/app/xkbcomp/-/blob/master/COPYING?ref_type\u003dheads#L69", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:31Z", + "timestamp": "2024-08-19T17:47:54Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-Fenneberg-Livingston.json b/src/main/resources/license-list-data/json/details/HPND-Fenneberg-Livingston.json index 35811e16e2..e9dde01c69 100644 --- a/src/main/resources/license-list-data/json/details/HPND-Fenneberg-Livingston.json +++ b/src/main/resources/license-list-data/json/details/HPND-Fenneberg-Livingston.json @@ -11,7 +11,7 @@ "url": "https://github.com/radcli/radcli/blob/master/COPYRIGHT#L34", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:24Z", + "timestamp": "2024-08-19T17:49:55Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://github.com/FreeRADIUS/freeradius-client/blob/master/COPYRIGHT#L32", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:25Z", + "timestamp": "2024-08-19T17:49:56Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-INRIA-IMAG.json b/src/main/resources/license-list-data/json/details/HPND-INRIA-IMAG.json index 226b8e0bef..56cc61ec02 100644 --- a/src/main/resources/license-list-data/json/details/HPND-INRIA-IMAG.json +++ b/src/main/resources/license-list-data/json/details/HPND-INRIA-IMAG.json @@ -10,7 +10,7 @@ "url": "https://github.com/ppp-project/ppp/blob/master/pppd/ipv6cp.c#L75-L83", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:50Z", + "timestamp": "2024-08-19T17:47:30Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-Intel.json b/src/main/resources/license-list-data/json/details/HPND-Intel.json index b2b0a1510d..b220062510 100644 --- a/src/main/resources/license-list-data/json/details/HPND-Intel.json +++ b/src/main/resources/license-list-data/json/details/HPND-Intel.json @@ -11,7 +11,7 @@ "url": "https://sourceware.org/git/?p\u003dnewlib-cygwin.git;a\u003dblob;f\u003dnewlib/libc/machine/i960/memcpy.S;hb\u003dHEAD", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:05Z", + "timestamp": "2024-08-19T17:35:11Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-Kevlin-Henney.json b/src/main/resources/license-list-data/json/details/HPND-Kevlin-Henney.json index ff4de88e44..10c808c515 100644 --- a/src/main/resources/license-list-data/json/details/HPND-Kevlin-Henney.json +++ b/src/main/resources/license-list-data/json/details/HPND-Kevlin-Henney.json @@ -11,7 +11,7 @@ "url": "https://github.com/mruby/mruby/blob/83d12f8d52522cdb7c8cc46fad34821359f453e6/mrbgems/mruby-dir/src/Win/dirent.c#L127-L140", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:17Z", + "timestamp": "2024-08-19T17:49:43Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-MIT-disclaimer.json b/src/main/resources/license-list-data/json/details/HPND-MIT-disclaimer.json index 3ca9c3c15c..3f2a9de255 100644 --- a/src/main/resources/license-list-data/json/details/HPND-MIT-disclaimer.json +++ b/src/main/resources/license-list-data/json/details/HPND-MIT-disclaimer.json @@ -11,7 +11,7 @@ "url": "https://metacpan.org/release/NLNETLABS/Net-DNS-SEC-1.22/source/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:55Z", + "timestamp": "2024-08-19T17:48:27Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-Markus-Kuhn.json b/src/main/resources/license-list-data/json/details/HPND-Markus-Kuhn.json index 4324dac27b..5bc3b5542c 100644 --- a/src/main/resources/license-list-data/json/details/HPND-Markus-Kuhn.json +++ b/src/main/resources/license-list-data/json/details/HPND-Markus-Kuhn.json @@ -7,21 +7,21 @@ "crossRef": [ { "match": "false", - "url": "https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c", + "url": "https://sourceware.org/git/?p\u003dbinutils-gdb.git;a\u003dblob;f\u003dreadline/readline/support/wcwidth.c;h\u003d0f5ec995796f4813abbcf4972aec0378ab74722a;hb\u003dHEAD#l55", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:29Z", + "timestamp": "2024-08-19T17:36:19Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "https://sourceware.org/git/?p\u003dbinutils-gdb.git;a\u003dblob;f\u003dreadline/readline/support/wcwidth.c;h\u003d0f5ec995796f4813abbcf4972aec0378ab74722a;hb\u003dHEAD#l55", + "url": "https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:30Z", + "timestamp": "2024-08-19T17:36:20Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/HPND-Netrek.json b/src/main/resources/license-list-data/json/details/HPND-Netrek.json new file mode 100644 index 0000000000..e04d2b69c7 --- /dev/null +++ b/src/main/resources/license-list-data/json/details/HPND-Netrek.json @@ -0,0 +1,11 @@ +{ + "isDeprecatedLicenseId": false, + "licenseText": "Copyright (C) 1995 S. M. Patel (smpatel@wam.umd.edu)\n\nPermission to use, copy, modify, and distribute this\nsoftware and its documentation for any purpose and without\nfee is hereby granted, provided that the above copyright\nnotice appear in all copies and that both that copyright\nnotice and this permission notice appear in supporting\ndocumentation. No representations are made about the\nsuitability of this software for any purpose. It is\nprovided \"as is\" without express or implied warranty.\n", + "standardLicenseTemplate": "\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright (C) 1995 S. M. Patel (smpatel@wam.umd.edu) \";match\u003d\".{0,5000}\"\u003e\u003e\n\nPermission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. No representations are made about the suitability of this software for any purpose. It is provided \"as is\" without express or implied warranty.\n\n", + "name": "Historical Permission Notice and Disclaimer - Netrek variant", + "licenseId": "HPND-Netrek", + "crossRef": [], + "seeAlso": [], + "isOsiApproved": false, + "licenseTextHtml": "\n \u003cdiv class\u003d\"replaceable-license-text\"\u003e \n \u003cp\u003e\n Copyright (C) 1995 S. M. Patel (smpatel@wam.umd.edu)\n \u003c/p\u003e\n\n \u003c/div\u003e\n \u003cp\u003e\n Permission to use, copy, modify, and distribute this software\n and its documentation for any purpose and without fee is hereby\n granted, provided that the above copyright notice appear in all\n copies and that both that copyright notice and this permission\n notice appear in supporting documentation. No representations\n are made about the suitability of this software for any purpose.\n It is provided \u0026quot;as is\u0026quot; without express or implied warranty.\n \u003c/p\u003e\n\n " +} \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/HPND-Pbmplus.json b/src/main/resources/license-list-data/json/details/HPND-Pbmplus.json index bb32d6bfee..d07aa00caa 100644 --- a/src/main/resources/license-list-data/json/details/HPND-Pbmplus.json +++ b/src/main/resources/license-list-data/json/details/HPND-Pbmplus.json @@ -11,7 +11,7 @@ "url": "https://sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/netpbm.c#l8", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:31Z", + "timestamp": "2024-08-19T17:36:58Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-UC-export-US.json b/src/main/resources/license-list-data/json/details/HPND-UC-export-US.json index 75261f6e01..ffb9014165 100644 --- a/src/main/resources/license-list-data/json/details/HPND-UC-export-US.json +++ b/src/main/resources/license-list-data/json/details/HPND-UC-export-US.json @@ -11,7 +11,7 @@ "url": "https://github.com/RTimothyEdwards/magic/blob/master/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:58Z", + "timestamp": "2024-08-19T17:35:18Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-UC.json b/src/main/resources/license-list-data/json/details/HPND-UC.json index 6e94629f8b..ac387c1444 100644 --- a/src/main/resources/license-list-data/json/details/HPND-UC.json +++ b/src/main/resources/license-list-data/json/details/HPND-UC.json @@ -11,7 +11,7 @@ "url": "https://core.tcl-lang.org/tk/file?name\u003dcompat/unistd.h", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:21Z", + "timestamp": "2024-08-19T17:41:55Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-doc-sell.json b/src/main/resources/license-list-data/json/details/HPND-doc-sell.json index 9a405cfd0c..e4dba0b24d 100644 --- a/src/main/resources/license-list-data/json/details/HPND-doc-sell.json +++ b/src/main/resources/license-list-data/json/details/HPND-doc-sell.json @@ -11,7 +11,7 @@ "url": "https://gitlab.freedesktop.org/xorg/lib/libxext/-/blob/master/COPYING?ref_type\u003dheads#L153-162", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:34Z", + "timestamp": "2024-08-19T17:50:09Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://gitlab.freedesktop.org/xorg/lib/libxtst/-/blob/master/COPYING?ref_type\u003dheads#L108-117", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:35Z", + "timestamp": "2024-08-19T17:50:10Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-doc.json b/src/main/resources/license-list-data/json/details/HPND-doc.json index 323be336ab..5dfea2a55d 100644 --- a/src/main/resources/license-list-data/json/details/HPND-doc.json +++ b/src/main/resources/license-list-data/json/details/HPND-doc.json @@ -11,7 +11,7 @@ "url": "https://gitlab.freedesktop.org/xorg/lib/libxext/-/blob/master/COPYING?ref_type\u003dheads#L185-197", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:28Z", + "timestamp": "2024-08-19T17:40:35Z", "isWayBackLink": false, "order": 0 }, @@ -20,7 +20,7 @@ "url": "https://gitlab.freedesktop.org/xorg/lib/libxtst/-/blob/master/COPYING?ref_type\u003dheads#L70-77", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:28Z", + "timestamp": "2024-08-19T17:40:36Z", "isWayBackLink": false, "order": 1 } diff --git a/src/main/resources/license-list-data/json/details/HPND-export-US-acknowledgement.json b/src/main/resources/license-list-data/json/details/HPND-export-US-acknowledgement.json index 8bfe8f9106..8b51d7e48e 100644 --- a/src/main/resources/license-list-data/json/details/HPND-export-US-acknowledgement.json +++ b/src/main/resources/license-list-data/json/details/HPND-export-US-acknowledgement.json @@ -11,7 +11,7 @@ "url": "https://web.mit.edu/kerberos/krb5-1.21/doc/mitK5license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:44Z", + "timestamp": "2024-08-19T17:44:44Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L831-L852", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:44Z", + "timestamp": "2024-08-19T17:44:45Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-export-US-modify.json b/src/main/resources/license-list-data/json/details/HPND-export-US-modify.json index 989eaafab8..8a45649764 100644 --- a/src/main/resources/license-list-data/json/details/HPND-export-US-modify.json +++ b/src/main/resources/license-list-data/json/details/HPND-export-US-modify.json @@ -11,7 +11,7 @@ "url": "https://github.com/pythongssapi/k5test/blob/v0.10.3/K5TEST-LICENSE.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:43Z", + "timestamp": "2024-08-19T17:35:47Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L1157-L1182", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:44Z", + "timestamp": "2024-08-19T17:35:48Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-export-US.json b/src/main/resources/license-list-data/json/details/HPND-export-US.json index dffa6b49aa..11ce7226f3 100644 --- a/src/main/resources/license-list-data/json/details/HPND-export-US.json +++ b/src/main/resources/license-list-data/json/details/HPND-export-US.json @@ -11,7 +11,7 @@ "url": "https://www.kermitproject.org/ck90.html#source", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:24Z", + "timestamp": "2024-08-19T17:46:19Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-export2-US.json b/src/main/resources/license-list-data/json/details/HPND-export2-US.json index bd2570d049..049f7ca7c5 100644 --- a/src/main/resources/license-list-data/json/details/HPND-export2-US.json +++ b/src/main/resources/license-list-data/json/details/HPND-export2-US.json @@ -1,7 +1,7 @@ { "isDeprecatedLicenseId": false, "licenseText": "Copyright 2004-2008 Apple Inc. All Rights Reserved.\n\n Export of this software from the United States of America may\n require a specific license from the United States Government.\n It is the responsibility of any person or organization\n contemplating export to obtain such a license before exporting.\n\nWITHIN THAT CONSTRAINT, permission to use, copy, modify, and\ndistribute this software and its documentation for any purpose and\nwithout fee is hereby granted, provided that the above copyright\nnotice appear in all copies and that both that copyright notice and\nthis permission notice appear in supporting documentation, and that\nthe name of Apple Inc. not be used in advertising or publicity\npertaining to distribution of the software without specific,\nwritten prior permission. Apple Inc. makes no representations\nabout the suitability of this software for any purpose. It is\nprovided \"as is\" without express or implied warranty.\n\nTHIS SOFTWARE IS PROVIDED \"AS IS\" AND WITHOUT ANY EXPRESS OR\nIMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED\nWARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.\n", - "standardLicenseTemplate": "\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright 2004-2008 Apple Inc. All Rights Reserved.\";match\u003d\".{0,5000}\"\u003e\u003e\n\nExport of this software from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting.\n\nWITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Apple Inc. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Apple Inc. makes no representations about the suitability of this software for any purpose. It is provided \"as is\" without express or implied warranty.\n\nTHIS SOFTWARE IS PROVIDED \"AS IS\" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.\n\n", + "standardLicenseTemplate": "\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright 2004-2008 Apple Inc. All Rights Reserved.\";match\u003d\".{0,5000}\"\u003e\u003e\n\nExport of this software from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting.\n\nWITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of \u003c\u003cvar;name\u003d\"copyrightHolder0\";original\u003d\"Apple Inc.\";match\u003d\".*\"\u003e\u003e not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. \u003c\u003cvar;name\u003d\"copyrightHolder1\";original\u003d\"Apple Inc.\";match\u003d\".*\"\u003e\u003e . makes no representations about the suitability of this software for any purpose. It is provided \"as is\" without express or implied warranty.\n\nTHIS SOFTWARE IS PROVIDED \"AS IS\" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.\n\n", "name": "HPND with US Government export control and 2 disclaimers", "licenseComments": "This license is very similar to HPND-export-US, but adds a second disclaimer.", "licenseId": "HPND-export2-US", @@ -11,7 +11,7 @@ "url": "https://web.mit.edu/kerberos/krb5-1.21/doc/mitK5license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:33Z", + "timestamp": "2024-08-19T17:42:30Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L111-L133", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:33Z", + "timestamp": "2024-08-19T17:42:30Z", "isWayBackLink": false, "order": 0 } @@ -30,5 +30,5 @@ "https://web.mit.edu/kerberos/krb5-1.21/doc/mitK5license.html" ], "isOsiApproved": false, - "licenseTextHtml": "\n \u003cvar class\u003d\"replaceable-license-text\"\u003e \n Copyright 2004-2008 Apple Inc. All Rights Reserved.\n \u003c/var\u003e\n \u003cp\u003e\n Export of this software from the United States of America may\n require a specific license from the United States Government.\n It is the responsibility of any person or organization\n contemplating export to obtain such a license before exporting.\n \u003c/p\u003e\n\n \u003cp\u003e\n WITHIN THAT CONSTRAINT, permission to use, copy, modify, and\n distribute this software and its documentation for any purpose and\n without fee is hereby granted, provided that the above copyright\n notice appear in all copies and that both that copyright notice\n and this permission notice appear in supporting documentation,\n and that the name of Apple Inc. not be used in advertising or\n publicity pertaining to distribution of the software without\n specific, written prior permission. Apple Inc. makes no\n representations about the suitability of this software for any\n purpose. It is provided \u0026quot;as is\u0026quot; without express or implied warranty.\n \u003c/p\u003e\n\n \u003cp\u003e\n THIS SOFTWARE IS PROVIDED \u0026quot;AS IS\u0026quot; AND WITHOUT ANY EXPRESS OR\n IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED\n WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.\n \u003c/p\u003e\n\n " + "licenseTextHtml": "\n \u003cvar class\u003d\"replaceable-license-text\"\u003e \n Copyright 2004-2008 Apple Inc. All Rights Reserved.\n \u003c/var\u003e\n \u003cp\u003e\n Export of this software from the United States of America may\n require a specific license from the United States Government.\n It is the responsibility of any person or organization\n contemplating export to obtain such a license before exporting.\n \u003c/p\u003e\n\n \u003cp\u003e\n WITHIN THAT CONSTRAINT, permission to use, copy, modify, and\n distribute this software and its documentation for any purpose and\n without fee is hereby granted, provided that the above copyright\n notice appear in all copies and that both that copyright notice\n and this permission notice appear in supporting documentation,\n and that the name of \u003cvar class\u003d\"replaceable-license-text\"\u003e Apple Inc.\u003c/var\u003e not be used in advertising or\n publicity pertaining to distribution of the software without\n specific, written prior permission. \u003cvar class\u003d\"replaceable-license-text\"\u003e Apple Inc.\u003c/var\u003e. makes no\n representations about the suitability of this software for any\n purpose. It is provided \u0026quot;as is\u0026quot; without express or implied warranty.\n \u003c/p\u003e\n\n \u003cp\u003e\n THIS SOFTWARE IS PROVIDED \u0026quot;AS IS\u0026quot; AND WITHOUT ANY EXPRESS OR\n IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED\n WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.\n \u003c/p\u003e\n\n " } \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/HPND-merchantability-variant.json b/src/main/resources/license-list-data/json/details/HPND-merchantability-variant.json index 95ed2d1915..b668796648 100644 --- a/src/main/resources/license-list-data/json/details/HPND-merchantability-variant.json +++ b/src/main/resources/license-list-data/json/details/HPND-merchantability-variant.json @@ -11,7 +11,7 @@ "url": "https://sourceware.org/git/?p\u003dnewlib-cygwin.git;a\u003dblob;f\u003dnewlib/libc/misc/fini.c;hb\u003dHEAD", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:22Z", + "timestamp": "2024-08-19T17:47:09Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-sell-MIT-disclaimer-xserver.json b/src/main/resources/license-list-data/json/details/HPND-sell-MIT-disclaimer-xserver.json index 3f66bd0cd3..812c07d684 100644 --- a/src/main/resources/license-list-data/json/details/HPND-sell-MIT-disclaimer-xserver.json +++ b/src/main/resources/license-list-data/json/details/HPND-sell-MIT-disclaimer-xserver.json @@ -11,7 +11,7 @@ "url": "https://gitlab.freedesktop.org/xorg/xserver/-/blob/master/COPYING?ref_type\u003dheads#L1781", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:06Z", + "timestamp": "2024-08-19T17:36:10Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-sell-regexpr.json b/src/main/resources/license-list-data/json/details/HPND-sell-regexpr.json index e1461865bc..9b1a0ea1e2 100644 --- a/src/main/resources/license-list-data/json/details/HPND-sell-regexpr.json +++ b/src/main/resources/license-list-data/json/details/HPND-sell-regexpr.json @@ -11,7 +11,7 @@ "url": "https://gitlab.com/bacula-org/bacula/-/blob/Branch-11.0/bacula/LICENSE-FOSS?ref_type\u003dheads#L245", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:41Z", + "timestamp": "2024-08-19T17:35:19Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-sell-variant-MIT-disclaimer-rev.json b/src/main/resources/license-list-data/json/details/HPND-sell-variant-MIT-disclaimer-rev.json index 4e49c284c7..1fb043ff68 100644 --- a/src/main/resources/license-list-data/json/details/HPND-sell-variant-MIT-disclaimer-rev.json +++ b/src/main/resources/license-list-data/json/details/HPND-sell-variant-MIT-disclaimer-rev.json @@ -11,7 +11,7 @@ "url": "https://github.com/sigmavirus24/x11-ssh-askpass/blob/master/dynlist.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:42Z", + "timestamp": "2024-08-19T17:35:11Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-sell-variant-MIT-disclaimer.json b/src/main/resources/license-list-data/json/details/HPND-sell-variant-MIT-disclaimer.json index 56ba828638..6ee8152810 100644 --- a/src/main/resources/license-list-data/json/details/HPND-sell-variant-MIT-disclaimer.json +++ b/src/main/resources/license-list-data/json/details/HPND-sell-variant-MIT-disclaimer.json @@ -11,7 +11,7 @@ "url": "https://github.com/sigmavirus24/x11-ssh-askpass/blob/master/README", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:35Z", + "timestamp": "2024-08-19T17:42:07Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND-sell-variant.json b/src/main/resources/license-list-data/json/details/HPND-sell-variant.json index 2e5dbc18ca..fc372fd6d0 100644 --- a/src/main/resources/license-list-data/json/details/HPND-sell-variant.json +++ b/src/main/resources/license-list-data/json/details/HPND-sell-variant.json @@ -11,7 +11,7 @@ "url": "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/auth_gss/gss_generic_token.c?h\u003dv4.19", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:28Z", + "timestamp": "2024-08-19T17:43:42Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HPND.json b/src/main/resources/license-list-data/json/details/HPND.json index bddf048a3b..31c2262aa2 100644 --- a/src/main/resources/license-list-data/json/details/HPND.json +++ b/src/main/resources/license-list-data/json/details/HPND.json @@ -12,7 +12,7 @@ "url": "http://lists.opensource.org/pipermail/license-discuss_lists.opensource.org/2002-November/006304.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:45Z", + "timestamp": "2024-08-19T17:35:28Z", "isWayBackLink": false, "order": 1 }, @@ -21,7 +21,7 @@ "url": "https://opensource.org/licenses/HPND", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:45Z", + "timestamp": "2024-08-19T17:35:28Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HTMLTIDY.json b/src/main/resources/license-list-data/json/details/HTMLTIDY.json index e2165a6567..7e4ead43d0 100644 --- a/src/main/resources/license-list-data/json/details/HTMLTIDY.json +++ b/src/main/resources/license-list-data/json/details/HTMLTIDY.json @@ -10,7 +10,7 @@ "url": "https://github.com/htacg/tidy-html5/blob/next/README/LICENSE.md", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:17Z", + "timestamp": "2024-08-19T17:42:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/HaskellReport.json b/src/main/resources/license-list-data/json/details/HaskellReport.json index fe192ee009..944d763fbc 100644 --- a/src/main/resources/license-list-data/json/details/HaskellReport.json +++ b/src/main/resources/license-list-data/json/details/HaskellReport.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Haskell_Language_Report_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:20Z", + "timestamp": "2024-08-19T17:39:58Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Hippocratic-2.1.json b/src/main/resources/license-list-data/json/details/Hippocratic-2.1.json index 5e0ddc70ec..a6ef2bb214 100644 --- a/src/main/resources/license-list-data/json/details/Hippocratic-2.1.json +++ b/src/main/resources/license-list-data/json/details/Hippocratic-2.1.json @@ -10,7 +10,7 @@ "url": "https://github.com/EthicalSource/hippocratic-license/blob/58c0e646d64ff6fbee275bfe2b9492f914e3ab2a/LICENSE.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:03Z", + "timestamp": "2024-08-19T17:41:57Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://firstdonoharm.dev/version/2/1/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:03Z", + "timestamp": "2024-08-19T17:41:58Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/IBM-pibs.json b/src/main/resources/license-list-data/json/details/IBM-pibs.json index 32509c4ce0..df3c2e5656 100644 --- a/src/main/resources/license-list-data/json/details/IBM-pibs.json +++ b/src/main/resources/license-list-data/json/details/IBM-pibs.json @@ -10,7 +10,7 @@ "url": "http://git.denx.de/?p\u003du-boot.git;a\u003dblob;f\u003darch/powerpc/cpu/ppc4xx/miiphy.c;h\u003d297155fdafa064b955e53e9832de93bfb0cfb85b;hb\u003d9fab4bf4cc077c21e43941866f3f2c196f28670d", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:57Z", + "timestamp": "2024-08-19T17:48:10Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ICU.json b/src/main/resources/license-list-data/json/details/ICU.json index 41a2a839b7..8941ac4084 100644 --- a/src/main/resources/license-list-data/json/details/ICU.json +++ b/src/main/resources/license-list-data/json/details/ICU.json @@ -10,7 +10,7 @@ "url": "http://source.icu-project.org/repos/icu/icu/trunk/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:43Z", + "timestamp": "2024-08-19T17:40:29Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/IEC-Code-Components-EULA.json b/src/main/resources/license-list-data/json/details/IEC-Code-Components-EULA.json index 6b13ec1bc6..73e4d0cdda 100644 --- a/src/main/resources/license-list-data/json/details/IEC-Code-Components-EULA.json +++ b/src/main/resources/license-list-data/json/details/IEC-Code-Components-EULA.json @@ -10,7 +10,7 @@ "url": "https://www.iec.ch/copyright", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:40Z", + "timestamp": "2024-08-19T17:35:12Z", "isWayBackLink": false, "order": 2 }, @@ -19,7 +19,7 @@ "url": "https://www.iec.ch/CCv1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:40Z", + "timestamp": "2024-08-19T17:35:12Z", "isWayBackLink": false, "order": 1 }, @@ -28,7 +28,7 @@ "url": "https://www.iec.ch/webstore/custserv/pdf/CC-EULA.pdf", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:41Z", + "timestamp": "2024-08-19T17:35:13Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/IJG-short.json b/src/main/resources/license-list-data/json/details/IJG-short.json index 51753c0e43..fdfda66aec 100644 --- a/src/main/resources/license-list-data/json/details/IJG-short.json +++ b/src/main/resources/license-list-data/json/details/IJG-short.json @@ -11,7 +11,7 @@ "url": "https://sourceforge.net/p/xmedcon/code/ci/master/tree/libs/ljpg/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:52Z", + "timestamp": "2024-08-19T17:36:07Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/IJG.json b/src/main/resources/license-list-data/json/details/IJG.json index 8c80e27e19..ca4a6e25b9 100644 --- a/src/main/resources/license-list-data/json/details/IJG.json +++ b/src/main/resources/license-list-data/json/details/IJG.json @@ -11,7 +11,7 @@ "url": "http://dev.w3.org/cvsweb/Amaya/libjpeg/Attic/README?rev\u003d1.2", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:33:08Z", + "timestamp": "2024-08-19T17:40:25Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/IPA.json b/src/main/resources/license-list-data/json/details/IPA.json index 4718f8c368..8824610f25 100644 --- a/src/main/resources/license-list-data/json/details/IPA.json +++ b/src/main/resources/license-list-data/json/details/IPA.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/IPA", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:54Z", + "timestamp": "2024-08-19T17:37:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/IPL-1.0.json b/src/main/resources/license-list-data/json/details/IPL-1.0.json index ea8679158a..dd3e6cd274 100644 --- a/src/main/resources/license-list-data/json/details/IPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/IPL-1.0.json @@ -12,7 +12,7 @@ "url": "https://opensource.org/licenses/IPL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:12Z", + "timestamp": "2024-08-19T17:42:14Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ISC-Veillard.json b/src/main/resources/license-list-data/json/details/ISC-Veillard.json index db6d48582a..3700242797 100644 --- a/src/main/resources/license-list-data/json/details/ISC-Veillard.json +++ b/src/main/resources/license-list-data/json/details/ISC-Veillard.json @@ -11,7 +11,7 @@ "url": "https://github.com/GNOME/libxml2/blob/master/dict.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:18Z", + "timestamp": "2024-08-19T17:46:25Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://raw.githubusercontent.com/GNOME/libxml2/4c2e7c651f6c2f0d1a74f350cbda95f7df3e7017/hash.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:18Z", + "timestamp": "2024-08-19T17:46:25Z", "isWayBackLink": false, "order": 0 }, @@ -29,7 +29,7 @@ "url": "https://sourceforge.net/p/ctrio/git/ci/master/tree/README", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:19Z", + "timestamp": "2024-08-19T17:46:25Z", "isWayBackLink": false, "order": 2 } diff --git a/src/main/resources/license-list-data/json/details/ISC.json b/src/main/resources/license-list-data/json/details/ISC.json index 3695cb02e8..47126db1eb 100644 --- a/src/main/resources/license-list-data/json/details/ISC.json +++ b/src/main/resources/license-list-data/json/details/ISC.json @@ -7,21 +7,12 @@ "licenseComments": "The ISC License text changed \u0027and\u0027 to \u0027and/or\u0027 in July 2007.", "licenseId": "ISC", "crossRef": [ - { - "match": "false", - "url": "https://www.isc.org/downloads/software-support-policy/isc-license/", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:27:22Z", - "isWayBackLink": false, - "order": 1 - }, { "match": "false", "url": "https://www.isc.org/licenses/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:22Z", + "timestamp": "2024-08-19T17:35:54Z", "isWayBackLink": false, "order": 0 }, @@ -30,9 +21,18 @@ "url": "https://opensource.org/licenses/ISC", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:22Z", + "timestamp": "2024-08-19T17:35:54Z", "isWayBackLink": false, "order": 2 + }, + { + "match": "false", + "url": "https://www.isc.org/downloads/software-support-policy/isc-license/", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:35:54Z", + "isWayBackLink": false, + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/ImageMagick.json b/src/main/resources/license-list-data/json/details/ImageMagick.json index 9eece069ab..599a10db06 100644 --- a/src/main/resources/license-list-data/json/details/ImageMagick.json +++ b/src/main/resources/license-list-data/json/details/ImageMagick.json @@ -11,7 +11,7 @@ "url": "http://www.imagemagick.org/script/license.php", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:59Z", + "timestamp": "2024-08-19T17:48:24Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Imlib2.json b/src/main/resources/license-list-data/json/details/Imlib2.json index 213cfeae17..4daf8c8e92 100644 --- a/src/main/resources/license-list-data/json/details/Imlib2.json +++ b/src/main/resources/license-list-data/json/details/Imlib2.json @@ -11,7 +11,7 @@ "url": "https://git.enlightenment.org/legacy/imlib2.git/tree/COPYING", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:19:42Z", + "timestamp": "2024-08-19T17:35:35Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "http://trac.enlightenment.org/e/browser/trunk/imlib2/COPYING", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:19:42Z", + "timestamp": "2024-08-19T17:35:35Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Info-ZIP.json b/src/main/resources/license-list-data/json/details/Info-ZIP.json index 12df205ad9..0e9a92ab22 100644 --- a/src/main/resources/license-list-data/json/details/Info-ZIP.json +++ b/src/main/resources/license-list-data/json/details/Info-ZIP.json @@ -10,7 +10,7 @@ "url": "http://www.info-zip.org/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:23Z", + "timestamp": "2024-08-19T17:42:37Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Inner-Net-2.0.json b/src/main/resources/license-list-data/json/details/Inner-Net-2.0.json index d16044a066..0e22c81db4 100644 --- a/src/main/resources/license-list-data/json/details/Inner-Net-2.0.json +++ b/src/main/resources/license-list-data/json/details/Inner-Net-2.0.json @@ -6,22 +6,22 @@ "licenseId": "Inner-Net-2.0", "crossRef": [ { - "match": "false", - "url": "https://sourceware.org/git/?p\u003dglibc.git;a\u003dblob;f\u003dLICENSES;h\u003d530893b1dc9ea00755603c68fb36bd4fc38a7be8;hb\u003dHEAD#l207", + "match": "true", + "url": "https://fedoraproject.org/wiki/Licensing/Inner_Net_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:22Z", + "timestamp": "2024-08-19T17:35:23Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { - "match": "true", - "url": "https://fedoraproject.org/wiki/Licensing/Inner_Net_License", + "match": "false", + "url": "https://sourceware.org/git/?p\u003dglibc.git;a\u003dblob;f\u003dLICENSES;h\u003d530893b1dc9ea00755603c68fb36bd4fc38a7be8;hb\u003dHEAD#l207", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:23Z", + "timestamp": "2024-08-19T17:35:24Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Intel-ACPI.json b/src/main/resources/license-list-data/json/details/Intel-ACPI.json index a8acb00ab0..49187863d6 100644 --- a/src/main/resources/license-list-data/json/details/Intel-ACPI.json +++ b/src/main/resources/license-list-data/json/details/Intel-ACPI.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Intel_ACPI_Software_License_Agreement", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:21Z", + "timestamp": "2024-08-19T17:38:49Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Intel.json b/src/main/resources/license-list-data/json/details/Intel.json index 8f4fef3fb9..e0ea9df643 100644 --- a/src/main/resources/license-list-data/json/details/Intel.json +++ b/src/main/resources/license-list-data/json/details/Intel.json @@ -12,7 +12,7 @@ "url": "https://opensource.org/licenses/Intel", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:16Z", + "timestamp": "2024-08-19T17:38:58Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Interbase-1.0.json b/src/main/resources/license-list-data/json/details/Interbase-1.0.json index 73a3234838..67630275c0 100644 --- a/src/main/resources/license-list-data/json/details/Interbase-1.0.json +++ b/src/main/resources/license-list-data/json/details/Interbase-1.0.json @@ -13,7 +13,7 @@ "url": "https://web.archive.org/web/20060319014854/http://info.borland.com/devsupport/interbase/opensource/IPL.html", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:25:25Z", + "timestamp": "2024-08-19T17:49:07Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/JPL-image.json b/src/main/resources/license-list-data/json/details/JPL-image.json index c71f0e2cca..6bf42d2665 100644 --- a/src/main/resources/license-list-data/json/details/JPL-image.json +++ b/src/main/resources/license-list-data/json/details/JPL-image.json @@ -10,7 +10,7 @@ "url": "https://www.jpl.nasa.gov/jpl-image-use-policy", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:44Z", + "timestamp": "2024-08-19T17:39:36Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/JPNIC.json b/src/main/resources/license-list-data/json/details/JPNIC.json index db0c9f9d5d..5a2b4e8481 100644 --- a/src/main/resources/license-list-data/json/details/JPNIC.json +++ b/src/main/resources/license-list-data/json/details/JPNIC.json @@ -10,7 +10,7 @@ "url": "https://gitlab.isc.org/isc-projects/bind9/blob/master/COPYRIGHT#L366", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:49Z", + "timestamp": "2024-08-19T17:35:47Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/JSON.json b/src/main/resources/license-list-data/json/details/JSON.json index 713ac56369..6345227aa0 100644 --- a/src/main/resources/license-list-data/json/details/JSON.json +++ b/src/main/resources/license-list-data/json/details/JSON.json @@ -11,7 +11,7 @@ "url": "http://www.json.org/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:44Z", + "timestamp": "2024-08-19T17:36:23Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Jam.json b/src/main/resources/license-list-data/json/details/Jam.json index 95641995c3..b67cfab200 100644 --- a/src/main/resources/license-list-data/json/details/Jam.json +++ b/src/main/resources/license-list-data/json/details/Jam.json @@ -10,7 +10,7 @@ "url": "https://web.archive.org/web/20160330173339/https://swarm.workshop.perforce.com/files/guest/perforce_software/jam/src/README", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:19:02Z", + "timestamp": "2024-08-19T17:39:33Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://www.boost.org/doc/libs/1_35_0/doc/html/jam.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:02Z", + "timestamp": "2024-08-19T17:39:33Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/JasPer-2.0.json b/src/main/resources/license-list-data/json/details/JasPer-2.0.json index 542946ce91..0c0e688581 100644 --- a/src/main/resources/license-list-data/json/details/JasPer-2.0.json +++ b/src/main/resources/license-list-data/json/details/JasPer-2.0.json @@ -10,7 +10,7 @@ "url": "http://www.ece.uvic.ca/~mdadams/jasper/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:20Z", + "timestamp": "2024-08-19T17:47:43Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Kastrup.json b/src/main/resources/license-list-data/json/details/Kastrup.json index ae0ce0a4e1..89969dcf7a 100644 --- a/src/main/resources/license-list-data/json/details/Kastrup.json +++ b/src/main/resources/license-list-data/json/details/Kastrup.json @@ -10,7 +10,7 @@ "url": "https://ctan.math.utah.edu/ctan/tex-archive/macros/generic/kastrup/binhex.dtx", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:36Z", + "timestamp": "2024-08-19T17:36:27Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Kazlib.json b/src/main/resources/license-list-data/json/details/Kazlib.json index 044f7a2967..d67e854b57 100644 --- a/src/main/resources/license-list-data/json/details/Kazlib.json +++ b/src/main/resources/license-list-data/json/details/Kazlib.json @@ -10,7 +10,7 @@ "url": "http://git.savannah.gnu.org/cgit/kazlib.git/tree/except.c?id\u003d0062df360c2d17d57f6af19b0e444c51feb99036", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:39Z", + "timestamp": "2024-08-19T17:36:42Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Knuth-CTAN.json b/src/main/resources/license-list-data/json/details/Knuth-CTAN.json index d7ef90da79..219cbcb64b 100644 --- a/src/main/resources/license-list-data/json/details/Knuth-CTAN.json +++ b/src/main/resources/license-list-data/json/details/Knuth-CTAN.json @@ -11,7 +11,7 @@ "url": "https://ctan.org/license/knuth", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:56Z", + "timestamp": "2024-08-19T17:35:14Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LAL-1.2.json b/src/main/resources/license-list-data/json/details/LAL-1.2.json index 0187564147..5128105792 100644 --- a/src/main/resources/license-list-data/json/details/LAL-1.2.json +++ b/src/main/resources/license-list-data/json/details/LAL-1.2.json @@ -11,7 +11,7 @@ "url": "http://artlibre.org/licence/lal/licence-art-libre-12/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:38Z", + "timestamp": "2024-08-19T17:37:46Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LAL-1.3.json b/src/main/resources/license-list-data/json/details/LAL-1.3.json index 7856c097b0..232697131b 100644 --- a/src/main/resources/license-list-data/json/details/LAL-1.3.json +++ b/src/main/resources/license-list-data/json/details/LAL-1.3.json @@ -11,7 +11,7 @@ "url": "https://artlibre.org/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:26Z", + "timestamp": "2024-08-19T17:45:17Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LGPL-2.0+.json b/src/main/resources/license-list-data/json/details/LGPL-2.0+.json index 03a5dabb1f..fee3b5d588 100644 --- a/src/main/resources/license-list-data/json/details/LGPL-2.0+.json +++ b/src/main/resources/license-list-data/json/details/LGPL-2.0+.json @@ -12,7 +12,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:38Z", + "timestamp": "2024-08-19T17:37:44Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LGPL-2.0-only.json b/src/main/resources/license-list-data/json/details/LGPL-2.0-only.json index cb3f5d2be0..784bae6762 100644 --- a/src/main/resources/license-list-data/json/details/LGPL-2.0-only.json +++ b/src/main/resources/license-list-data/json/details/LGPL-2.0-only.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:38Z", + "timestamp": "2024-08-19T17:44:52Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LGPL-2.0-or-later.json b/src/main/resources/license-list-data/json/details/LGPL-2.0-or-later.json index 1a9ade37b7..2fb4adb79e 100644 --- a/src/main/resources/license-list-data/json/details/LGPL-2.0-or-later.json +++ b/src/main/resources/license-list-data/json/details/LGPL-2.0-or-later.json @@ -13,7 +13,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:50Z", + "timestamp": "2024-08-19T17:42:49Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LGPL-2.0.json b/src/main/resources/license-list-data/json/details/LGPL-2.0.json index 746f8d9f50..8afde9ddd2 100644 --- a/src/main/resources/license-list-data/json/details/LGPL-2.0.json +++ b/src/main/resources/license-list-data/json/details/LGPL-2.0.json @@ -12,7 +12,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/lgpl-2.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:23Z", + "timestamp": "2024-08-19T17:35:29Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LGPL-2.1+.json b/src/main/resources/license-list-data/json/details/LGPL-2.1+.json index e62bb80be5..c32df68b14 100644 --- a/src/main/resources/license-list-data/json/details/LGPL-2.1+.json +++ b/src/main/resources/license-list-data/json/details/LGPL-2.1+.json @@ -13,7 +13,7 @@ "url": "https://opensource.org/licenses/LGPL-2.1", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:31Z", + "timestamp": "2024-08-19T17:35:14Z", "isWayBackLink": false, "order": 1 }, @@ -22,7 +22,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:31Z", + "timestamp": "2024-08-19T17:35:14Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LGPL-2.1-only.json b/src/main/resources/license-list-data/json/details/LGPL-2.1-only.json index ebc1703a55..0ea6608b17 100644 --- a/src/main/resources/license-list-data/json/details/LGPL-2.1-only.json +++ b/src/main/resources/license-list-data/json/details/LGPL-2.1-only.json @@ -14,7 +14,7 @@ "url": "https://opensource.org/licenses/LGPL-2.1", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:19:14Z", + "timestamp": "2024-08-19T17:45:08Z", "isWayBackLink": false, "order": 1 }, @@ -23,7 +23,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:14Z", + "timestamp": "2024-08-19T17:45:09Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LGPL-2.1-or-later.json b/src/main/resources/license-list-data/json/details/LGPL-2.1-or-later.json index f431c040ca..264020e497 100644 --- a/src/main/resources/license-list-data/json/details/LGPL-2.1-or-later.json +++ b/src/main/resources/license-list-data/json/details/LGPL-2.1-or-later.json @@ -14,7 +14,7 @@ "url": "https://opensource.org/licenses/LGPL-2.1", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:26Z", + "timestamp": "2024-08-19T17:39:27Z", "isWayBackLink": false, "order": 1 }, @@ -23,7 +23,7 @@ "url": "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:26Z", + "timestamp": "2024-08-19T17:39:27Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LGPL-2.1.json b/src/main/resources/license-list-data/json/details/LGPL-2.1.json index 41c8cb043a..5547d34c8f 100644 --- a/src/main/resources/license-list-data/json/details/LGPL-2.1.json +++ b/src/main/resources/license-list-data/json/details/LGPL-2.1.json @@ -9,22 +9,22 @@ "standardLicenseHeader": "Copyright (C) year name of author\n\nThis 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; version 2.1.\n\nThis 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.\n\nYou 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\n\n", "crossRef": [ { - "match": "N/A", - "url": "https://opensource.org/licenses/LGPL-2.1", + "match": "false", + "url": "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:13Z", + "timestamp": "2024-08-19T17:43:32Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { - "match": "false", - "url": "https://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html", + "match": "N/A", + "url": "https://opensource.org/licenses/LGPL-2.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:13Z", + "timestamp": "2024-08-19T17:43:32Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/LGPL-3.0+.json b/src/main/resources/license-list-data/json/details/LGPL-3.0+.json index 982fc7ee83..8691b31b93 100644 --- a/src/main/resources/license-list-data/json/details/LGPL-3.0+.json +++ b/src/main/resources/license-list-data/json/details/LGPL-3.0+.json @@ -12,7 +12,7 @@ "url": "https://www.gnu.org/licenses/lgpl+gpl-3.0.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:42Z", + "timestamp": "2024-08-19T17:38:37Z", "isWayBackLink": false, "order": 1 }, @@ -21,7 +21,7 @@ "url": "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:42Z", + "timestamp": "2024-08-19T17:38:37Z", "isWayBackLink": false, "order": 0 }, @@ -30,7 +30,7 @@ "url": "https://opensource.org/licenses/LGPL-3.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:20:42Z", + "timestamp": "2024-08-19T17:38:37Z", "isWayBackLink": false, "order": 2 } diff --git a/src/main/resources/license-list-data/json/details/LGPL-3.0-only.json b/src/main/resources/license-list-data/json/details/LGPL-3.0-only.json index b3b27819c0..20931b99ce 100644 --- a/src/main/resources/license-list-data/json/details/LGPL-3.0-only.json +++ b/src/main/resources/license-list-data/json/details/LGPL-3.0-only.json @@ -12,7 +12,7 @@ "url": "https://www.gnu.org/licenses/lgpl+gpl-3.0.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:26Z", + "timestamp": "2024-08-19T17:37:06Z", "isWayBackLink": false, "order": 1 }, @@ -21,7 +21,7 @@ "url": "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:26Z", + "timestamp": "2024-08-19T17:37:07Z", "isWayBackLink": false, "order": 0 }, @@ -30,7 +30,7 @@ "url": "https://opensource.org/licenses/LGPL-3.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:26Z", + "timestamp": "2024-08-19T17:37:07Z", "isWayBackLink": false, "order": 2 } diff --git a/src/main/resources/license-list-data/json/details/LGPL-3.0-or-later.json b/src/main/resources/license-list-data/json/details/LGPL-3.0-or-later.json index 9eb1b2ddc0..ea4229fecc 100644 --- a/src/main/resources/license-list-data/json/details/LGPL-3.0-or-later.json +++ b/src/main/resources/license-list-data/json/details/LGPL-3.0-or-later.json @@ -12,7 +12,7 @@ "url": "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:15Z", + "timestamp": "2024-08-19T17:41:34Z", "isWayBackLink": false, "order": 0 }, @@ -21,7 +21,7 @@ "url": "https://opensource.org/licenses/LGPL-3.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:21:15Z", + "timestamp": "2024-08-19T17:41:34Z", "isWayBackLink": false, "order": 2 }, @@ -30,7 +30,7 @@ "url": "https://www.gnu.org/licenses/lgpl+gpl-3.0.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:15Z", + "timestamp": "2024-08-19T17:41:34Z", "isWayBackLink": false, "order": 1 } diff --git a/src/main/resources/license-list-data/json/details/LGPL-3.0.json b/src/main/resources/license-list-data/json/details/LGPL-3.0.json index 4ed0b401b4..89a3033c36 100644 --- a/src/main/resources/license-list-data/json/details/LGPL-3.0.json +++ b/src/main/resources/license-list-data/json/details/LGPL-3.0.json @@ -8,31 +8,31 @@ "licenseId": "LGPL-3.0", "crossRef": [ { - "match": "N/A", - "url": "https://opensource.org/licenses/LGPL-3.0", + "match": "false", + "url": "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:12Z", + "timestamp": "2024-08-19T17:36:18Z", "isWayBackLink": false, - "order": 2 + "order": 0 }, { - "match": "false", - "url": "https://www.gnu.org/licenses/lgpl+gpl-3.0.txt", + "match": "N/A", + "url": "https://opensource.org/licenses/LGPL-3.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:12Z", + "timestamp": "2024-08-19T17:36:18Z", "isWayBackLink": false, - "order": 1 + "order": 2 }, { "match": "false", - "url": "https://www.gnu.org/licenses/lgpl-3.0-standalone.html", + "url": "https://www.gnu.org/licenses/lgpl+gpl-3.0.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:12Z", + "timestamp": "2024-08-19T17:36:18Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/LGPLLR.json b/src/main/resources/license-list-data/json/details/LGPLLR.json index 4e447e9bb3..7bcf076816 100644 --- a/src/main/resources/license-list-data/json/details/LGPLLR.json +++ b/src/main/resources/license-list-data/json/details/LGPLLR.json @@ -11,7 +11,7 @@ "url": "http://www-igm.univ-mlv.fr/~unitex/lgpllr.html", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:26:57Z", + "timestamp": "2024-08-19T17:35:31Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LOOP.json b/src/main/resources/license-list-data/json/details/LOOP.json index a4e4fd703b..d0b4aca131 100644 --- a/src/main/resources/license-list-data/json/details/LOOP.json +++ b/src/main/resources/license-list-data/json/details/LOOP.json @@ -7,57 +7,57 @@ "crossRef": [ { "match": "false", - "url": "https://github.com/blakemcbride/eclipse-lisp/blob/master/lisp/loop.lisp", + "url": "https://gitlab.com/embeddable-common-lisp/ecl/-/blob/develop/src/lsp/loop.lsp", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:34Z", + "timestamp": "2024-08-19T17:44:53Z", "isWayBackLink": false, - "order": 4 + "order": 0 }, { "match": "false", - "url": "https://github.com/cl-adams/adams/blob/master/LICENSE.md", + "url": "https://sourceforge.net/p/sbcl/sbcl/ci/master/tree/src/code/loop.lisp", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:34Z", + "timestamp": "2024-08-19T17:44:55Z", "isWayBackLink": false, - "order": 3 + "order": 2 }, { "match": "false", - "url": "https://gitlab.common-lisp.net/cmucl/cmucl/-/blob/master/src/code/loop.lisp", + "url": "http://git.savannah.gnu.org/cgit/gcl.git/tree/gcl/lsp/gcl_loop.lsp?h\u003dVersion_2_6_13pre", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:35Z", + "timestamp": "2024-08-19T17:44:56Z", "isWayBackLink": false, - "order": 5 + "order": 1 }, { "match": "false", - "url": "https://gitlab.com/embeddable-common-lisp/ecl/-/blob/develop/src/lsp/loop.lsp", + "url": "https://github.com/blakemcbride/eclipse-lisp/blob/master/lisp/loop.lisp", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:36Z", + "timestamp": "2024-08-19T17:45:02Z", "isWayBackLink": false, - "order": 0 + "order": 4 }, { "match": "false", - "url": "https://sourceforge.net/p/sbcl/sbcl/ci/master/tree/src/code/loop.lisp", + "url": "https://github.com/cl-adams/adams/blob/master/LICENSE.md", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:37Z", + "timestamp": "2024-08-19T17:45:03Z", "isWayBackLink": false, - "order": 2 + "order": 3 }, { "match": "false", - "url": "http://git.savannah.gnu.org/cgit/gcl.git/tree/gcl/lsp/gcl_loop.lsp?h\u003dVersion_2_6_13pre", + "url": "https://gitlab.common-lisp.net/cmucl/cmucl/-/blob/master/src/code/loop.lisp", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:38Z", + "timestamp": "2024-08-19T17:45:03Z", "isWayBackLink": false, - "order": 1 + "order": 5 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/LPD-document.json b/src/main/resources/license-list-data/json/details/LPD-document.json index 206e034522..0ef0eb7c42 100644 --- a/src/main/resources/license-list-data/json/details/LPD-document.json +++ b/src/main/resources/license-list-data/json/details/LPD-document.json @@ -10,7 +10,7 @@ "url": "https://www.ietf.org/rfc/rfc1952.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:48Z", + "timestamp": "2024-08-19T17:46:42Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://github.com/Cyan4973/xxHash/blob/dev/doc/xxhash_spec.md", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:49Z", + "timestamp": "2024-08-19T17:46:42Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LPL-1.0.json b/src/main/resources/license-list-data/json/details/LPL-1.0.json index 2f8cc9027a..8a22896d10 100644 --- a/src/main/resources/license-list-data/json/details/LPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/LPL-1.0.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/LPL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:05Z", + "timestamp": "2024-08-19T17:37:52Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LPL-1.02.json b/src/main/resources/license-list-data/json/details/LPL-1.02.json index 18237a2ec7..eab00f8702 100644 --- a/src/main/resources/license-list-data/json/details/LPL-1.02.json +++ b/src/main/resources/license-list-data/json/details/LPL-1.02.json @@ -7,22 +7,22 @@ "licenseId": "LPL-1.02", "crossRef": [ { - "match": "N/A", - "url": "https://opensource.org/licenses/LPL-1.02", + "match": "false", + "url": "http://plan9.bell-labs.com/plan9/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:39Z", + "timestamp": "2024-08-19T17:35:49Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { - "match": "false", - "url": "http://plan9.bell-labs.com/plan9/license.html", + "match": "N/A", + "url": "https://opensource.org/licenses/LPL-1.02", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:44Z", + "timestamp": "2024-08-19T17:35:49Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/LPPL-1.0.json b/src/main/resources/license-list-data/json/details/LPPL-1.0.json index ff2c178a9b..2074867866 100644 --- a/src/main/resources/license-list-data/json/details/LPPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/LPPL-1.0.json @@ -13,7 +13,7 @@ "url": "http://www.latex-project.org/lppl/lppl-1-0.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:26Z", + "timestamp": "2024-08-19T17:35:53Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LPPL-1.1.json b/src/main/resources/license-list-data/json/details/LPPL-1.1.json index 2c2eb8e994..870bf2e31b 100644 --- a/src/main/resources/license-list-data/json/details/LPPL-1.1.json +++ b/src/main/resources/license-list-data/json/details/LPPL-1.1.json @@ -13,7 +13,7 @@ "url": "http://www.latex-project.org/lppl/lppl-1-1.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:51Z", + "timestamp": "2024-08-19T17:37:28Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LPPL-1.2.json b/src/main/resources/license-list-data/json/details/LPPL-1.2.json index 4afd286ff1..2e9a96c622 100644 --- a/src/main/resources/license-list-data/json/details/LPPL-1.2.json +++ b/src/main/resources/license-list-data/json/details/LPPL-1.2.json @@ -14,7 +14,7 @@ "url": "http://www.latex-project.org/lppl/lppl-1-2.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:36Z", + "timestamp": "2024-08-19T17:41:23Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LPPL-1.3a.json b/src/main/resources/license-list-data/json/details/LPPL-1.3a.json index a862ecca20..57a24680d3 100644 --- a/src/main/resources/license-list-data/json/details/LPPL-1.3a.json +++ b/src/main/resources/license-list-data/json/details/LPPL-1.3a.json @@ -14,7 +14,7 @@ "url": "http://www.latex-project.org/lppl/lppl-1-3a.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:31Z", + "timestamp": "2024-08-19T17:44:36Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LPPL-1.3c.json b/src/main/resources/license-list-data/json/details/LPPL-1.3c.json index 6a5d6ce2aa..59e7a5d4f8 100644 --- a/src/main/resources/license-list-data/json/details/LPPL-1.3c.json +++ b/src/main/resources/license-list-data/json/details/LPPL-1.3c.json @@ -9,22 +9,22 @@ "standardLicenseHeader": "%% pig.dtx\n\n%% Copyright 2005 M. Y. Name\n\n%\n\n% This work may be distributed and/or modified under the\n\n% conditions of the LaTeX Project Public License, either version 1.3\n\n% of this license or (at your option) any later version.\n\n% The latest version of this license is in\n\n% http://www.latex-project.org/lppl.txt\n\n% and version 1.3 or later is part of all distributions of LaTeX\n\n% version 2005/12/01 or later.\n\n%\n\n% This work has the LPPL maintenance status \" maintained \".\n\n%\n\n% The Current Maintainer of this work is M. Y. Name .\n\n%\n\n% This work consists of the files pig.dtx and pig.ins\n\n% and the derived file pig.sty .\n\n", "crossRef": [ { - "match": "false", - "url": "http://www.latex-project.org/lppl/lppl-1-3c.txt", + "match": "N/A", + "url": "https://opensource.org/licenses/LPPL-1.3c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:49Z", + "timestamp": "2024-08-19T17:38:33Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { - "match": "N/A", - "url": "https://opensource.org/licenses/LPPL-1.3c", + "match": "false", + "url": "http://www.latex-project.org/lppl/lppl-1-3c.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:49Z", + "timestamp": "2024-08-19T17:38:33Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/LZMA-SDK-9.11-to-9.20.json b/src/main/resources/license-list-data/json/details/LZMA-SDK-9.11-to-9.20.json index 61190f5f1d..6073f231e6 100644 --- a/src/main/resources/license-list-data/json/details/LZMA-SDK-9.11-to-9.20.json +++ b/src/main/resources/license-list-data/json/details/LZMA-SDK-9.11-to-9.20.json @@ -11,7 +11,7 @@ "url": "https://www.7-zip.org/sdk.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:28Z", + "timestamp": "2024-08-19T17:36:45Z", "isWayBackLink": false, "order": 0 }, @@ -20,7 +20,7 @@ "url": "https://sourceforge.net/projects/sevenzip/files/LZMA%20SDK/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:28Z", + "timestamp": "2024-08-19T17:36:46Z", "isWayBackLink": false, "order": 1 } diff --git a/src/main/resources/license-list-data/json/details/LZMA-SDK-9.22.json b/src/main/resources/license-list-data/json/details/LZMA-SDK-9.22.json index 9815e1626c..d34f6c2417 100644 --- a/src/main/resources/license-list-data/json/details/LZMA-SDK-9.22.json +++ b/src/main/resources/license-list-data/json/details/LZMA-SDK-9.22.json @@ -11,7 +11,7 @@ "url": "https://sourceforge.net/projects/sevenzip/files/LZMA%20SDK/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:54Z", + "timestamp": "2024-08-19T17:48:08Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://www.7-zip.org/sdk.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:56Z", + "timestamp": "2024-08-19T17:48:09Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Latex2e-translated-notice.json b/src/main/resources/license-list-data/json/details/Latex2e-translated-notice.json index f771ed605d..1f389a628b 100644 --- a/src/main/resources/license-list-data/json/details/Latex2e-translated-notice.json +++ b/src/main/resources/license-list-data/json/details/Latex2e-translated-notice.json @@ -11,7 +11,7 @@ "url": "https://git.savannah.gnu.org/cgit/indent.git/tree/doc/indent.texi?id\u003da74c6b4ee49397cf330b333da1042bffa60ed14f#n74", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:03Z", + "timestamp": "2024-08-19T17:35:38Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Latex2e.json b/src/main/resources/license-list-data/json/details/Latex2e.json index 536fce0d95..6ee6dfd707 100644 --- a/src/main/resources/license-list-data/json/details/Latex2e.json +++ b/src/main/resources/license-list-data/json/details/Latex2e.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Latex2e", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:42Z", + "timestamp": "2024-08-19T17:35:08Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Leptonica.json b/src/main/resources/license-list-data/json/details/Leptonica.json index 568f5d2e09..ab73429e01 100644 --- a/src/main/resources/license-list-data/json/details/Leptonica.json +++ b/src/main/resources/license-list-data/json/details/Leptonica.json @@ -11,7 +11,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Leptonica", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:59Z", + "timestamp": "2024-08-19T17:37:00Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LiLiQ-P-1.1.json b/src/main/resources/license-list-data/json/details/LiLiQ-P-1.1.json index 8faf619fd5..29619a7b2d 100644 --- a/src/main/resources/license-list-data/json/details/LiLiQ-P-1.1.json +++ b/src/main/resources/license-list-data/json/details/LiLiQ-P-1.1.json @@ -6,23 +6,23 @@ "licenseComments": "French is the canonical language for this license. An English translation is provided here: https://forge.gouv.qc.ca/licence/en/liliq-v1-1/#permissive-liliq-p", "licenseId": "LiLiQ-P-1.1", "crossRef": [ - { - "match": "false", - "url": "http://opensource.org/licenses/LiLiQ-P-1.1", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:18:36Z", - "isWayBackLink": false, - "order": 1 - }, { "match": "N/A", "url": "https://forge.gouv.qc.ca/licence/fr/liliq-v1-1/", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:37Z", + "timestamp": "2024-08-19T17:36:04Z", "isWayBackLink": false, "order": 0 + }, + { + "match": "false", + "url": "http://opensource.org/licenses/LiLiQ-P-1.1", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:36:04Z", + "isWayBackLink": false, + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/LiLiQ-R-1.1.json b/src/main/resources/license-list-data/json/details/LiLiQ-R-1.1.json index 3e90d54270..fbc7ebc2e2 100644 --- a/src/main/resources/license-list-data/json/details/LiLiQ-R-1.1.json +++ b/src/main/resources/license-list-data/json/details/LiLiQ-R-1.1.json @@ -11,7 +11,7 @@ "url": "http://opensource.org/licenses/LiLiQ-R-1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:44Z", + "timestamp": "2024-08-19T17:36:45Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-liliq-r-v1-1/", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:46Z", + "timestamp": "2024-08-19T17:36:45Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/LiLiQ-Rplus-1.1.json b/src/main/resources/license-list-data/json/details/LiLiQ-Rplus-1.1.json index 107f6b62c6..4f10211f3b 100644 --- a/src/main/resources/license-list-data/json/details/LiLiQ-Rplus-1.1.json +++ b/src/main/resources/license-list-data/json/details/LiLiQ-Rplus-1.1.json @@ -6,23 +6,23 @@ "licenseComments": "French is the canonical language for this license. An English translation is provided here: https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-in-english/quebec-free-and-open-source-licence-strong-reciprocity-liliq-r-v1-1/", "licenseId": "LiLiQ-Rplus-1.1", "crossRef": [ - { - "match": "N/A", - "url": "https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-forte-liliq-r-v1-1/", - "isValid": true, - "isLive": false, - "timestamp": "2024-05-22T17:18:34Z", - "isWayBackLink": false, - "order": 0 - }, { "match": "false", "url": "http://opensource.org/licenses/LiLiQ-Rplus-1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:34Z", + "timestamp": "2024-08-19T17:38:55Z", "isWayBackLink": false, "order": 1 + }, + { + "match": "N/A", + "url": "https://www.forge.gouv.qc.ca/participez/licence-logicielle/licence-libre-du-quebec-liliq-en-francais/licence-libre-du-quebec-reciprocite-forte-liliq-r-v1-1/", + "isValid": true, + "isLive": false, + "timestamp": "2024-08-19T17:38:56Z", + "isWayBackLink": false, + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Libpng.json b/src/main/resources/license-list-data/json/details/Libpng.json index 396ce8ab51..9e41e8c5b6 100644 --- a/src/main/resources/license-list-data/json/details/Libpng.json +++ b/src/main/resources/license-list-data/json/details/Libpng.json @@ -10,7 +10,7 @@ "url": "http://www.libpng.org/pub/png/src/libpng-LICENSE.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:57Z", + "timestamp": "2024-08-19T17:45:30Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Linux-OpenIB.json b/src/main/resources/license-list-data/json/details/Linux-OpenIB.json index d9f4ab3a4e..9a01bd84b4 100644 --- a/src/main/resources/license-list-data/json/details/Linux-OpenIB.json +++ b/src/main/resources/license-list-data/json/details/Linux-OpenIB.json @@ -11,7 +11,7 @@ "url": "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/sa.h", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:48Z", + "timestamp": "2024-08-19T17:38:05Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Linux-man-pages-1-para.json b/src/main/resources/license-list-data/json/details/Linux-man-pages-1-para.json index cad1423a9a..ee6c5308cd 100644 --- a/src/main/resources/license-list-data/json/details/Linux-man-pages-1-para.json +++ b/src/main/resources/license-list-data/json/details/Linux-man-pages-1-para.json @@ -10,7 +10,7 @@ "url": "https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/getcpu.2#n4", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:23:12Z", + "timestamp": "2024-08-19T17:47:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Linux-man-pages-copyleft-2-para.json b/src/main/resources/license-list-data/json/details/Linux-man-pages-copyleft-2-para.json index d99c37083e..fbc96c1a69 100644 --- a/src/main/resources/license-list-data/json/details/Linux-man-pages-copyleft-2-para.json +++ b/src/main/resources/license-list-data/json/details/Linux-man-pages-copyleft-2-para.json @@ -7,21 +7,21 @@ "crossRef": [ { "match": "N/A", - "url": "https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/migrate_pages.2#n8", + "url": "https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/move_pages.2#n5", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:51Z", + "timestamp": "2024-08-19T17:39:51Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { "match": "N/A", - "url": "https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/move_pages.2#n5", + "url": "https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/migrate_pages.2#n8", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:51Z", + "timestamp": "2024-08-19T17:39:51Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Linux-man-pages-copyleft-var.json b/src/main/resources/license-list-data/json/details/Linux-man-pages-copyleft-var.json index 665c18fd2f..384dfc8ad7 100644 --- a/src/main/resources/license-list-data/json/details/Linux-man-pages-copyleft-var.json +++ b/src/main/resources/license-list-data/json/details/Linux-man-pages-copyleft-var.json @@ -11,7 +11,7 @@ "url": "https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/set_mempolicy.2#n5", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:23:03Z", + "timestamp": "2024-08-19T17:36:32Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Linux-man-pages-copyleft.json b/src/main/resources/license-list-data/json/details/Linux-man-pages-copyleft.json index 0b246a095c..ae1c3d4413 100644 --- a/src/main/resources/license-list-data/json/details/Linux-man-pages-copyleft.json +++ b/src/main/resources/license-list-data/json/details/Linux-man-pages-copyleft.json @@ -10,7 +10,7 @@ "url": "https://www.kernel.org/doc/man-pages/licenses.html", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:30:42Z", + "timestamp": "2024-08-19T17:36:54Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Lucida-Bitmap-Fonts.json b/src/main/resources/license-list-data/json/details/Lucida-Bitmap-Fonts.json index 9d64834fa7..605fed40f7 100644 --- a/src/main/resources/license-list-data/json/details/Lucida-Bitmap-Fonts.json +++ b/src/main/resources/license-list-data/json/details/Lucida-Bitmap-Fonts.json @@ -10,7 +10,7 @@ "url": "https://gitlab.freedesktop.org/xorg/font/bh-100dpi/-/blob/master/COPYING?ref_type\u003dheads", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:43Z", + "timestamp": "2024-08-19T17:40:34Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MIT-0.json b/src/main/resources/license-list-data/json/details/MIT-0.json index b8b06f00ba..771b5e2048 100644 --- a/src/main/resources/license-list-data/json/details/MIT-0.json +++ b/src/main/resources/license-list-data/json/details/MIT-0.json @@ -8,30 +8,30 @@ "crossRef": [ { "match": "false", - "url": "https://github.com/awsdocs/aws-cloud9-user-guide/blob/master/LICENSE-SAMPLECODE", + "url": "https://romanrm.net/mit-zero", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:34Z", + "timestamp": "2024-08-19T17:39:24Z", "isWayBackLink": false, - "order": 2 + "order": 1 }, { "match": "false", - "url": "https://romanrm.net/mit-zero", + "url": "https://github.com/aws/mit-0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:35Z", + "timestamp": "2024-08-19T17:39:25Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { "match": "false", - "url": "https://github.com/aws/mit-0", + "url": "https://github.com/awsdocs/aws-cloud9-user-guide/blob/master/LICENSE-SAMPLECODE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:36Z", + "timestamp": "2024-08-19T17:39:26Z", "isWayBackLink": false, - "order": 0 + "order": 2 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/MIT-CMU.json b/src/main/resources/license-list-data/json/details/MIT-CMU.json index e2d59ee873..c7988cf498 100644 --- a/src/main/resources/license-list-data/json/details/MIT-CMU.json +++ b/src/main/resources/license-list-data/json/details/MIT-CMU.json @@ -10,7 +10,7 @@ "url": "https://github.com/python-pillow/Pillow/blob/fffb426092c8db24a5f4b6df243a8a3c01fb63cd/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:28Z", + "timestamp": "2024-08-19T17:35:16Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://fedoraproject.org/wiki/Licensing:MIT?rd\u003dLicensing/MIT#CMU_Style", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:28Z", + "timestamp": "2024-08-19T17:35:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MIT-Festival.json b/src/main/resources/license-list-data/json/details/MIT-Festival.json index bc54f91010..7a23b2b47f 100644 --- a/src/main/resources/license-list-data/json/details/MIT-Festival.json +++ b/src/main/resources/license-list-data/json/details/MIT-Festival.json @@ -10,7 +10,7 @@ "url": "https://github.com/festvox/speech_tools/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:38Z", + "timestamp": "2024-08-19T17:42:08Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://github.com/festvox/flite/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:38Z", + "timestamp": "2024-08-19T17:42:09Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MIT-Khronos-old.json b/src/main/resources/license-list-data/json/details/MIT-Khronos-old.json index 5820184609..91f1e5c28a 100644 --- a/src/main/resources/license-list-data/json/details/MIT-Khronos-old.json +++ b/src/main/resources/license-list-data/json/details/MIT-Khronos-old.json @@ -11,7 +11,7 @@ "url": "https://github.com/KhronosGroup/SPIRV-Cross/blob/main/LICENSES/LicenseRef-KhronosFreeUse.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:02Z", + "timestamp": "2024-08-19T17:35:10Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MIT-Modern-Variant.json b/src/main/resources/license-list-data/json/details/MIT-Modern-Variant.json index 4bb80b300f..d70adccdfc 100644 --- a/src/main/resources/license-list-data/json/details/MIT-Modern-Variant.json +++ b/src/main/resources/license-list-data/json/details/MIT-Modern-Variant.json @@ -8,30 +8,30 @@ "crossRef": [ { "match": "false", - "url": "https://pirlwww.lpl.arizona.edu/resources/guide/software/PerlTk/Tixlic.html", + "url": "https://ptolemy.berkeley.edu/copyright.htm", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:39Z", + "timestamp": "2024-08-19T17:35:34Z", "isWayBackLink": false, - "order": 2 + "order": 1 }, { "match": "false", - "url": "https://ptolemy.berkeley.edu/copyright.htm", + "url": "https://fedoraproject.org/wiki/Licensing:MIT#Modern_Variants", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:39Z", + "timestamp": "2024-08-19T17:35:34Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { "match": "false", - "url": "https://fedoraproject.org/wiki/Licensing:MIT#Modern_Variants", + "url": "https://pirlwww.lpl.arizona.edu/resources/guide/software/PerlTk/Tixlic.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:40Z", + "timestamp": "2024-08-19T17:35:35Z", "isWayBackLink": false, - "order": 0 + "order": 2 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/MIT-Wu.json b/src/main/resources/license-list-data/json/details/MIT-Wu.json index 4a14421d2e..068fb70e3d 100644 --- a/src/main/resources/license-list-data/json/details/MIT-Wu.json +++ b/src/main/resources/license-list-data/json/details/MIT-Wu.json @@ -10,7 +10,7 @@ "url": "https://github.com/chromium/octane/blob/master/crypto.js", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:01Z", + "timestamp": "2024-08-19T17:36:09Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MIT-advertising.json b/src/main/resources/license-list-data/json/details/MIT-advertising.json index 1da454961e..ea3dde4dd3 100644 --- a/src/main/resources/license-list-data/json/details/MIT-advertising.json +++ b/src/main/resources/license-list-data/json/details/MIT-advertising.json @@ -11,7 +11,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/MIT_With_Advertising", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:54Z", + "timestamp": "2024-08-19T17:35:18Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MIT-enna.json b/src/main/resources/license-list-data/json/details/MIT-enna.json index 003d489633..3f0ec42b10 100644 --- a/src/main/resources/license-list-data/json/details/MIT-enna.json +++ b/src/main/resources/license-list-data/json/details/MIT-enna.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/MIT#enna", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:54Z", + "timestamp": "2024-08-19T17:36:48Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MIT-feh.json b/src/main/resources/license-list-data/json/details/MIT-feh.json index ddfa407a0b..6dbe0d5c4e 100644 --- a/src/main/resources/license-list-data/json/details/MIT-feh.json +++ b/src/main/resources/license-list-data/json/details/MIT-feh.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/MIT#feh", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:40Z", + "timestamp": "2024-08-19T17:36:00Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MIT-open-group.json b/src/main/resources/license-list-data/json/details/MIT-open-group.json index 121521d110..ba7dbb9b3e 100644 --- a/src/main/resources/license-list-data/json/details/MIT-open-group.json +++ b/src/main/resources/license-list-data/json/details/MIT-open-group.json @@ -10,7 +10,7 @@ "url": "https://gitlab.freedesktop.org/xorg/app/xsetroot/-/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:48Z", + "timestamp": "2024-08-19T17:44:40Z", "isWayBackLink": false, "order": 2 }, @@ -19,27 +19,27 @@ "url": "https://gitlab.freedesktop.org/xorg/app/xvinfo/-/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:49Z", + "timestamp": "2024-08-19T17:44:51Z", "isWayBackLink": false, "order": 1 }, { "match": "false", - "url": "https://gitlab.freedesktop.org/xorg/app/iceauth/-/blob/master/COPYING", + "url": "https://gitlab.freedesktop.org/xorg/app/xauth/-/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:50Z", + "timestamp": "2024-08-19T17:44:55Z", "isWayBackLink": false, - "order": 0 + "order": 3 }, { "match": "false", - "url": "https://gitlab.freedesktop.org/xorg/app/xauth/-/blob/master/COPYING", + "url": "https://gitlab.freedesktop.org/xorg/app/iceauth/-/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:50Z", + "timestamp": "2024-08-19T17:44:59Z", "isWayBackLink": false, - "order": 3 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/MIT-testregex.json b/src/main/resources/license-list-data/json/details/MIT-testregex.json index 96ea96f280..53aa43aa41 100644 --- a/src/main/resources/license-list-data/json/details/MIT-testregex.json +++ b/src/main/resources/license-list-data/json/details/MIT-testregex.json @@ -10,7 +10,7 @@ "url": "https://github.com/dotnet/runtime/blob/55e1ac7c07df62c4108d4acedf78f77574470ce5/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/AttRegexTests.cs#L12-L28", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:24Z", + "timestamp": "2024-08-19T17:47:23Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MIT.json b/src/main/resources/license-list-data/json/details/MIT.json index 5ba15fce29..e848506a2c 100644 --- a/src/main/resources/license-list-data/json/details/MIT.json +++ b/src/main/resources/license-list-data/json/details/MIT.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/license/mit/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:38Z", + "timestamp": "2024-08-19T17:48:12Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MITNFA.json b/src/main/resources/license-list-data/json/details/MITNFA.json index 368f1556a2..bd58bfab0c 100644 --- a/src/main/resources/license-list-data/json/details/MITNFA.json +++ b/src/main/resources/license-list-data/json/details/MITNFA.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/MITNFA", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:27Z", + "timestamp": "2024-08-19T17:50:13Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MMIXware.json b/src/main/resources/license-list-data/json/details/MMIXware.json index faeb115300..86df449f66 100644 --- a/src/main/resources/license-list-data/json/details/MMIXware.json +++ b/src/main/resources/license-list-data/json/details/MMIXware.json @@ -10,7 +10,7 @@ "url": "https://gitlab.lrz.de/mmix/mmixware/-/blob/master/boilerplate.w", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:50Z", + "timestamp": "2024-08-19T17:42:27Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MPEG-SSG.json b/src/main/resources/license-list-data/json/details/MPEG-SSG.json index d03db633b6..170541310e 100644 --- a/src/main/resources/license-list-data/json/details/MPEG-SSG.json +++ b/src/main/resources/license-list-data/json/details/MPEG-SSG.json @@ -10,7 +10,7 @@ "url": "https://sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/ppm/ppmtompeg/jrevdct.c#l1189", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:08Z", + "timestamp": "2024-08-19T17:39:04Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MPL-1.0.json b/src/main/resources/license-list-data/json/details/MPL-1.0.json index c17c448bf3..b724e6be44 100644 --- a/src/main/resources/license-list-data/json/details/MPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/MPL-1.0.json @@ -13,7 +13,7 @@ "url": "https://opensource.org/licenses/MPL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:32Z", + "timestamp": "2024-08-19T17:37:31Z", "isWayBackLink": false, "order": 1 }, @@ -22,7 +22,7 @@ "url": "http://www.mozilla.org/MPL/MPL-1.0.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:33Z", + "timestamp": "2024-08-19T17:37:31Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MPL-1.1.json b/src/main/resources/license-list-data/json/details/MPL-1.1.json index 796135b7f7..5fce4826dd 100644 --- a/src/main/resources/license-list-data/json/details/MPL-1.1.json +++ b/src/main/resources/license-list-data/json/details/MPL-1.1.json @@ -2,30 +2,30 @@ "isDeprecatedLicenseId": false, "isFsfLibre": true, "licenseText": "Mozilla Public License Version 1.1\n\n1. Definitions.\n\n 1.0.1. \"Commercial Use\" means distribution or otherwise making the Covered Code available to a third party.\n\n 1.1. \"Contributor\" means each entity that creates or contributes to the creation of Modifications.\n\n 1.2. \"Contributor Version\" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.\n\n 1.3. \"Covered Code\" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.\n\n 1.4. \"Electronic Distribution Mechanism\" means a mechanism generally accepted in the software development community for the electronic transfer of data.\n\n 1.5. \"Executable\" means Covered Code in any form other than Source Code.\n\n 1.6. \"Initial Developer\" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.\n\n 1.7. \"Larger Work\" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.\n\n 1.8. \"License\" means this document.\n\n 1.8.1. \"Licensable\" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.\n\n 1.9. \"Modifications\" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:\nAny addition to or deletion from the contents of a file containing Original Code or previous Modifications.\nAny new file that contains any part of the Original Code or previous Modifications.\n\n 1.10. \"Original Code\" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.\n\n 1.10.1. \"Patent Claims\" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.\n\n 1.11. \"Source Code\" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor\u0027s choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.\n\n 1.12. \"You\" (or \"Your\") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, \"You\" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, \"control\" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.\n\n2. Source Code License.\n\n 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:\n\n a. under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and\n b. under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).\n c. the licenses granted in this Section 2.1 (a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.\n d. Notwithstanding Section 2.1 (b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices.\n\n 2.2. Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license\n\n a. under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and\n b. under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).\n c. the licenses granted in Sections 2.2 (a) and 2.2 (b) are effective on the date Contributor first makes Commercial Use of the Covered Code.\n d. Notwithstanding Section 2.2 (b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor.\n\n3. Distribution Obligations.\n\n 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients\u0027 rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.\n\n 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.\n\n 3.3. Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.\n\n 3.4. Intellectual Property Matters\n\n (a) Third Party Claims\n If Contributor has knowledge that a license under a third party\u0027s intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled \"LEGAL\" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.\n\n (b) Contributor APIs\n If Contributor\u0027s Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.\n\n (c) Representations.\n Contributor represents that, except as disclosed pursuant to Section 3.4 (a) above, Contributor believes that Contributor\u0027s Modifications are Contributor\u0027s original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.\n\n 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients\u0027 rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.\n\n 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Sections 3.1, 3.2, 3.3, 3.4 and 3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients\u0027 rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient\u0027s rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.\n\n 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.\n\n4. Inability to Comply Due to Statute or Regulation.\n\nIf it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.\n\n5. Application of this License.\nThis License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code.\n\n6. Versions of the License.\n\n 6.1. New Versions\n Netscape Communications Corporation (\"Netscape\") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.\n\n 6.2. Effect of New Versions\n Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License.\n\n 6.3. Derivative Works\n If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases \"Mozilla\", \"MOZILLAPL\", \"MOZPL\", \"Netscape\", \"MPL\", \"NPL\" or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)\n\n7. DISCLAIMER OF WARRANTY\nCOVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN \"AS IS\" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.\n\n8. Termination\n\n 8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.\n\n 8.2. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as \"Participant\") alleging that:\n\n a. such Participant\u0027s Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.\n b. any software, hardware, or device, other than such Participant\u0027s Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant.\n\n 8.3. If You assert a patent infringement claim against Participant alleging that such Participant\u0027s Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.\n\n 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.\n\n9. LIMITATION OF LIABILITY\nUNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY\u0027S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.\n\n10. U.S. government end users\nThe Covered Code is a \"commercial item,\" as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of \"commercial computer software\" and \"commercial computer software documentation,\" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.\n\n11. Miscellaneous\nThis License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys\u0027 fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.\n\n12. Responsibility for claims\nAs between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.\n\n13. Multiple-licensed code\nInitial Developer may designate portions of the Covered Code as \"Multiple-Licensed\". \"Multiple-Licensed\" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the MPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A.\n\nExhibit A - Mozilla Public License.\n\n\"The contents of this file are subject to the Mozilla Public License Version 1.1 (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.mozilla.org/MPL/\n\nSoftware distributed under the License is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.\n\nThe Original Code is ______________________________________.\n\nThe Initial Developer of the Original Code is ________________________.\nPortions created by ______________________ are Copyright (C) ______\n_______________________. All Rights Reserved.\n\nContributor(s): ______________________________________.\n\nAlternatively, the contents of this file may be used under the terms of the _____ license (the \"[___] License\"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the [___] License.\"\n\nNOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.\n", - "standardLicenseHeaderTemplate": "\u003c\u003cbeginOptional\u003e\u003e\"\u003c\u003cendOptional\u003e\u003eThe contents of this file are subject to the Mozilla Public License Version 1.1 (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.mozilla.org/MPL/\n\nSoftware distributed under the License is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.\n\nThe Original Code is \u003c\u003cvar;name\u003d\"code\";original\u003d\"______________________________________\";match\u003d\".+\"\u003e\u003e .\n\nThe Initial Developer of the Original Code is \u003c\u003cvar;name\u003d\"initialDeveloper\";original\u003d\"________________________\";match\u003d\".+\"\u003e\u003e .\n\nPortions created by \u003c\u003cvar;name\u003d\"creator\";original\u003d\"______________________\";match\u003d\".+\"\u003e\u003e are Copyright (C) \u003c\u003cvar;name\u003d\"copyright\";original\u003d\"______\";match\u003d\".+\"\u003e\u003e . All Rights Reserved.\n\nContributor(s): \u003c\u003cvar;name\u003d\"contributor\";original\u003d\"______________________________________\";match\u003d\".+\"\u003e\u003e .\n\nAlternatively, the contents of this file may be used under the terms of the \u003c\u003cvar;name\u003d\"altLicense\";original\u003d\"_____\";match\u003d\".+\"\u003e\u003e license (the \" \u003c\u003cvar;name\u003d\"altLicenseShort1\";original\u003d\"[___]\";match\u003d\".+\"\u003e\u003e License\"), in which case the provisions of \u003c\u003cvar;name\u003d\"altLicenseShort2\";original\u003d\"[______]\";match\u003d\".+\"\u003e\u003e License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the \u003c\u003cvar;name\u003d\"altLicenseShort3\";original\u003d\"[____]\";match\u003d\".+\"\u003e\u003e License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the \u003c\u003cvar;name\u003d\"altLicenseShort4\";original\u003d\"[___]\";match\u003d\".+\"\u003e\u003e License. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the \u003c\u003cvar;name\u003d\"altLicenseShort5\";original\u003d\"[___]\";match\u003d\".+\"\u003e\u003e License.\u003c\u003cbeginOptional\u003e\u003e\"\u003c\u003cendOptional\u003e\u003e\n\n", - "standardLicenseTemplate": "\u003c\u003cbeginOptional\u003e\u003eMozilla Public License Version 1.1\n\n\u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.\";match\u003d\".{0,20}\"\u003e\u003e Definitions.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.0.1.\";match\u003d\".{0,20}\"\u003e\u003e \"Commercial Use\" means distribution or otherwise making the Covered Code available to a third party.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.1.\";match\u003d\".{0,20}\"\u003e\u003e \"Contributor\" means each entity that creates or contributes to the creation of Modifications.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.2.\";match\u003d\".{0,20}\"\u003e\u003e \"Contributor Version\" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.3.\";match\u003d\".{0,20}\"\u003e\u003e \"Covered Code\" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.4.\";match\u003d\".{0,20}\"\u003e\u003e \"Electronic Distribution Mechanism\" means a mechanism generally accepted in the software development community for the electronic transfer of data.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.5.\";match\u003d\".{0,20}\"\u003e\u003e \"Executable\" means Covered Code in any form other than Source Code.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.6.\";match\u003d\".{0,20}\"\u003e\u003e \"Initial Developer\" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.7.\";match\u003d\".{0,20}\"\u003e\u003e \"Larger Work\" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.8.\";match\u003d\".{0,20}\"\u003e\u003e \"License\" means this document.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.8.1.\";match\u003d\".{0,20}\"\u003e\u003e \"Licensable\" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.9.\";match\u003d\".{0,20}\"\u003e\u003e \"Modifications\" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:\n\n Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.\n\n Any new file that contains any part of the Original Code or previous Modifications.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.10.\";match\u003d\".{0,20}\"\u003e\u003e \"Original Code\" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.10.1.\";match\u003d\".{0,20}\"\u003e\u003e \"Patent Claims\" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.11.\";match\u003d\".{0,20}\"\u003e\u003e \"Source Code\" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor\u0027s choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.12.\";match\u003d\".{0,20}\"\u003e\u003e \"You\" (or \"Your\") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, \"You\" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, \"control\" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.\";match\u003d\".{0,20}\"\u003e\u003e Source Code License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.1.\";match\u003d\".{0,20}\"\u003e\u003e The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"a.\";match\u003d\".{0,20}\"\u003e\u003e under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"b.\";match\u003d\".{0,20}\"\u003e\u003e under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"c.\";match\u003d\".{0,20}\"\u003e\u003e the licenses granted in this Section 2.1 (a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"d.\";match\u003d\".{0,20}\"\u003e\u003e Notwithstanding Section 2.1 (b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.2.\";match\u003d\".{0,20}\"\u003e\u003e Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"a.\";match\u003d\".{0,20}\"\u003e\u003e under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"b.\";match\u003d\".{0,20}\"\u003e\u003e under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"c.\";match\u003d\".{0,20}\"\u003e\u003e the licenses granted in Sections 2.2 (a) and 2.2 (b) are effective on the date Contributor first makes Commercial Use of the Covered Code.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"d.\";match\u003d\".{0,20}\"\u003e\u003e Notwithstanding Section 2.2 (b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.\";match\u003d\".{0,20}\"\u003e\u003e Distribution Obligations.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.1.\";match\u003d\".{0,20}\"\u003e\u003e Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients\u0027 rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.2.\";match\u003d\".{0,20}\"\u003e\u003e Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.3.\";match\u003d\".{0,20}\"\u003e\u003e Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.4.\";match\u003d\".{0,20}\"\u003e\u003e Intellectual Property Matters\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e Third Party Claims\n\n If Contributor has knowledge that a license under a third party\u0027s intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled \"LEGAL\" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e Contributor APIs\n\n If Contributor\u0027s Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(c)\";match\u003d\".{0,20}\"\u003e\u003e Representations.\n\n Contributor represents that, except as disclosed pursuant to Section 3.4 (a) above, Contributor believes that Contributor\u0027s Modifications are Contributor\u0027s original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.5.\";match\u003d\".{0,20}\"\u003e\u003e Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients\u0027 rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.6.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Sections 3.1, 3.2, 3.3, 3.4 and 3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients\u0027 rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient\u0027s rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.7.\";match\u003d\".{0,20}\"\u003e\u003e Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"4.\";match\u003d\".{0,20}\"\u003e\u003e Inability to Comply Due to Statute or Regulation.\n\n If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.\";match\u003d\".{0,20}\"\u003e\u003e Application of this License.\n\n This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"6.\";match\u003d\".{0,20}\"\u003e\u003e Versions of the License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"6.1.\";match\u003d\".{0,20}\"\u003e\u003e New Versions\n\n Netscape Communications Corporation (\"Netscape\") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"6.2.\";match\u003d\".{0,20}\"\u003e\u003e Effect of New Versions\n\n Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"6.3.\";match\u003d\".{0,20}\"\u003e\u003e Derivative Works\n\n If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases \"Mozilla\", \"MOZILLAPL\", \"MOZPL\", \"Netscape\", \"MPL\", \"NPL\" or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"7.\";match\u003d\".{0,20}\"\u003e\u003e DISCLAIMER OF WARRANTY\n\n COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN \"AS IS\" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.\";match\u003d\".{0,20}\"\u003e\u003e Termination\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.1.\";match\u003d\".{0,20}\"\u003e\u003e This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.2.\";match\u003d\".{0,20}\"\u003e\u003e If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as \"Participant\") alleging that:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"a.\";match\u003d\".{0,20}\"\u003e\u003e such Participant\u0027s Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"b.\";match\u003d\".{0,20}\"\u003e\u003e any software, hardware, or device, other than such Participant\u0027s Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.3.\";match\u003d\".{0,20}\"\u003e\u003e If You assert a patent infringement claim against Participant alleging that such Participant\u0027s Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.4.\";match\u003d\".{0,20}\"\u003e\u003e In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"9.\";match\u003d\".{0,20}\"\u003e\u003e LIMITATION OF LIABILITY\n\n UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY\u0027S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.\";match\u003d\".{0,20}\"\u003e\u003e U.S. government end users\n\n The Covered Code is a \"commercial item,\" as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of \"commercial computer software\" and \"commercial computer software documentation,\" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"11.\";match\u003d\".{0,20}\"\u003e\u003e Miscellaneous\n\n This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys\u0027 fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"12.\";match\u003d\".{0,20}\"\u003e\u003e Responsibility for claims\n\n As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"13.\";match\u003d\".{0,20}\"\u003e\u003e Multiple-licensed code\n\n Initial Developer may designate portions of the Covered Code as \"Multiple-Licensed\". \"Multiple-Licensed\" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the MPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A.\u003c\u003cbeginOptional\u003e\u003e Exhibit A - Mozilla Public License.\n\n\u003c\u003cbeginOptional\u003e\u003e\"\u003c\u003cendOptional\u003e\u003eThe contents of this file are subject to the Mozilla Public License Version 1.1 (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.mozilla.org/MPL/\n\nSoftware distributed under the License is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.\n\nThe Original Code is \u003c\u003cvar;name\u003d\"code\";original\u003d\"______________________________________\";match\u003d\".+\"\u003e\u003e .\n\nThe Initial Developer of the Original Code is \u003c\u003cvar;name\u003d\"initialDeveloper\";original\u003d\"________________________\";match\u003d\".+\"\u003e\u003e .\n\nPortions created by \u003c\u003cvar;name\u003d\"creator\";original\u003d\"______________________\";match\u003d\".+\"\u003e\u003e are Copyright (C) \u003c\u003cvar;name\u003d\"copyright\";original\u003d\"______\";match\u003d\".+\"\u003e\u003e . All Rights Reserved.\n\nContributor(s): \u003c\u003cvar;name\u003d\"contributor\";original\u003d\"______________________________________\";match\u003d\".+\"\u003e\u003e .\n\nAlternatively, the contents of this file may be used under the terms of the \u003c\u003cvar;name\u003d\"altLicense\";original\u003d\"_____\";match\u003d\".+\"\u003e\u003e license (the \" \u003c\u003cvar;name\u003d\"altLicenseShort1\";original\u003d\"[___]\";match\u003d\".+\"\u003e\u003e License\"), in which case the provisions of \u003c\u003cvar;name\u003d\"altLicenseShort2\";original\u003d\"[______]\";match\u003d\".+\"\u003e\u003e License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the \u003c\u003cvar;name\u003d\"altLicenseShort3\";original\u003d\"[____]\";match\u003d\".+\"\u003e\u003e License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the \u003c\u003cvar;name\u003d\"altLicenseShort4\";original\u003d\"[___]\";match\u003d\".+\"\u003e\u003e License. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the \u003c\u003cvar;name\u003d\"altLicenseShort5\";original\u003d\"[___]\";match\u003d\".+\"\u003e\u003e License.\u003c\u003cbeginOptional\u003e\u003e\"\u003c\u003cendOptional\u003e\u003e\n\nNOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.\n\n\u003c\u003cendOptional\u003e\u003e", + "standardLicenseHeaderTemplate": "\u003c\u003cbeginOptional\u003e\u003e\"\u003c\u003cendOptional\u003e\u003eThe contents of this file are subject to the Mozilla Public License Version 1.1 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.mozilla.org/MPL/\n\nSoftware distributed under the License is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.\n\nThe Original Code is \u003c\u003cvar;name\u003d\"code\";original\u003d\"______________________________________\";match\u003d\".+\"\u003e\u003e .\n\nThe Initial Developer of the Original Code is \u003c\u003cvar;name\u003d\"initialDeveloper\";original\u003d\"________________________\";match\u003d\".+\"\u003e\u003e .\n\nPortions created by \u003c\u003cvar;name\u003d\"creator\";original\u003d\"______________________\";match\u003d\".+\"\u003e\u003e are Copyright (C) \u003c\u003cvar;name\u003d\"copyright\";original\u003d\"______\";match\u003d\".+\"\u003e\u003e . All Rights Reserved.\n\nContributor(s): \u003c\u003cvar;name\u003d\"contributor\";original\u003d\"______________________________________\";match\u003d\".+\"\u003e\u003e .\n\nAlternatively, the contents of this file may be used under the terms of the \u003c\u003cvar;name\u003d\"altLicense\";original\u003d\"_____\";match\u003d\".+\"\u003e\u003e license (the \" \u003c\u003cvar;name\u003d\"altLicenseShort1\";original\u003d\"[___]\";match\u003d\".+\"\u003e\u003e License\"), in which case the provisions of \u003c\u003cvar;name\u003d\"altLicenseShort2\";original\u003d\"[______]\";match\u003d\".+\"\u003e\u003e License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the \u003c\u003cvar;name\u003d\"altLicenseShort3\";original\u003d\"[____]\";match\u003d\".+\"\u003e\u003e License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the \u003c\u003cvar;name\u003d\"altLicenseShort4\";original\u003d\"[___]\";match\u003d\".+\"\u003e\u003e License. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the \u003c\u003cvar;name\u003d\"altLicenseShort5\";original\u003d\"[___]\";match\u003d\".+\"\u003e\u003e License.\u003c\u003cbeginOptional\u003e\u003e\"\u003c\u003cendOptional\u003e\u003e\n\n", + "standardLicenseTemplate": "\u003c\u003cbeginOptional\u003e\u003eMozilla Public License Version 1.1\n\n\u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.\";match\u003d\".{0,20}\"\u003e\u003e Definitions.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.0.1.\";match\u003d\".{0,20}\"\u003e\u003e \"Commercial Use\" means distribution or otherwise making the Covered Code available to a third party.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.1.\";match\u003d\".{0,20}\"\u003e\u003e \"Contributor\" means each entity that creates or contributes to the creation of Modifications.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.2.\";match\u003d\".{0,20}\"\u003e\u003e \"Contributor Version\" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.3.\";match\u003d\".{0,20}\"\u003e\u003e \"Covered Code\" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.4.\";match\u003d\".{0,20}\"\u003e\u003e \"Electronic Distribution Mechanism\" means a mechanism generally accepted in the software development community for the electronic transfer of data.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.5.\";match\u003d\".{0,20}\"\u003e\u003e \"Executable\" means Covered Code in any form other than Source Code.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.6.\";match\u003d\".{0,20}\"\u003e\u003e \"Initial Developer\" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.7.\";match\u003d\".{0,20}\"\u003e\u003e \"Larger Work\" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.8.\";match\u003d\".{0,20}\"\u003e\u003e \"License\" means this document.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.8.1.\";match\u003d\".{0,20}\"\u003e\u003e \"Licensable\" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.9.\";match\u003d\".{0,20}\"\u003e\u003e \"Modifications\" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is:\n\n Any addition to or deletion from the contents of a file containing Original Code or previous Modifications.\n\n Any new file that contains any part of the Original Code or previous Modifications.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.10.\";match\u003d\".{0,20}\"\u003e\u003e \"Original Code\" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.10.1.\";match\u003d\".{0,20}\"\u003e\u003e \"Patent Claims\" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.11.\";match\u003d\".{0,20}\"\u003e\u003e \"Source Code\" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor\u0027s choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.12.\";match\u003d\".{0,20}\"\u003e\u003e \"You\" (or \"Your\") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, \"You\" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, \"control\" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.\";match\u003d\".{0,20}\"\u003e\u003e Source Code License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.1.\";match\u003d\".{0,20}\"\u003e\u003e The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"a.\";match\u003d\".{0,20}\"\u003e\u003e under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"b.\";match\u003d\".{0,20}\"\u003e\u003e under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"c.\";match\u003d\".{0,20}\"\u003e\u003e the licenses granted in this Section 2.1 (a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"d.\";match\u003d\".{0,20}\"\u003e\u003e Notwithstanding Section 2.1 (b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.2.\";match\u003d\".{0,20}\"\u003e\u003e Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"a.\";match\u003d\".{0,20}\"\u003e\u003e under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"b.\";match\u003d\".{0,20}\"\u003e\u003e under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"c.\";match\u003d\".{0,20}\"\u003e\u003e the licenses granted in Sections 2.2 (a) and 2.2 (b) are effective on the date Contributor first makes Commercial Use of the Covered Code.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"d.\";match\u003d\".{0,20}\"\u003e\u003e Notwithstanding Section 2.2 (b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.\";match\u003d\".{0,20}\"\u003e\u003e Distribution Obligations.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.1.\";match\u003d\".{0,20}\"\u003e\u003e Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients\u0027 rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.2.\";match\u003d\".{0,20}\"\u003e\u003e Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.3.\";match\u003d\".{0,20}\"\u003e\u003e Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.4.\";match\u003d\".{0,20}\"\u003e\u003e Intellectual Property Matters\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e Third Party Claims\n\n If Contributor has knowledge that a license under a third party\u0027s intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled \"LEGAL\" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e Contributor APIs\n\n If Contributor\u0027s Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(c)\";match\u003d\".{0,20}\"\u003e\u003e Representations.\n\n Contributor represents that, except as disclosed pursuant to Section 3.4 (a) above, Contributor believes that Contributor\u0027s Modifications are Contributor\u0027s original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.5.\";match\u003d\".{0,20}\"\u003e\u003e Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients\u0027 rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.6.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Sections 3.1, 3.2, 3.3, 3.4 and 3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients\u0027 rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient\u0027s rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.7.\";match\u003d\".{0,20}\"\u003e\u003e Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"4.\";match\u003d\".{0,20}\"\u003e\u003e Inability to Comply Due to Statute or Regulation.\n\n If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.\";match\u003d\".{0,20}\"\u003e\u003e Application of this License.\n\n This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"6.\";match\u003d\".{0,20}\"\u003e\u003e Versions of the License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"6.1.\";match\u003d\".{0,20}\"\u003e\u003e New Versions\n\n Netscape Communications Corporation (\"Netscape\") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"6.2.\";match\u003d\".{0,20}\"\u003e\u003e Effect of New Versions\n\n Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"6.3.\";match\u003d\".{0,20}\"\u003e\u003e Derivative Works\n\n If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases \"Mozilla\", \"MOZILLAPL\", \"MOZPL\", \"Netscape\", \"MPL\", \"NPL\" or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.)\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"7.\";match\u003d\".{0,20}\"\u003e\u003e DISCLAIMER OF WARRANTY\n\n COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN \"AS IS\" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.\";match\u003d\".{0,20}\"\u003e\u003e Termination\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.1.\";match\u003d\".{0,20}\"\u003e\u003e This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.2.\";match\u003d\".{0,20}\"\u003e\u003e If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as \"Participant\") alleging that:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"a.\";match\u003d\".{0,20}\"\u003e\u003e such Participant\u0027s Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"b.\";match\u003d\".{0,20}\"\u003e\u003e any software, hardware, or device, other than such Participant\u0027s Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.3.\";match\u003d\".{0,20}\"\u003e\u003e If You assert a patent infringement claim against Participant alleging that such Participant\u0027s Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.4.\";match\u003d\".{0,20}\"\u003e\u003e In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"9.\";match\u003d\".{0,20}\"\u003e\u003e LIMITATION OF LIABILITY\n\n UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY\u0027S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.\";match\u003d\".{0,20}\"\u003e\u003e U.S. government end users\n\n The Covered Code is a \"commercial item,\" as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of \"commercial computer software\" and \"commercial computer software documentation,\" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"11.\";match\u003d\".{0,20}\"\u003e\u003e Miscellaneous\n\n This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys\u0027 fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"12.\";match\u003d\".{0,20}\"\u003e\u003e Responsibility for claims\n\n As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"13.\";match\u003d\".{0,20}\"\u003e\u003e Multiple-licensed code\n\n Initial Developer may designate portions of the Covered Code as \"Multiple-Licensed\". \"Multiple-Licensed\" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the MPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A.\u003c\u003cbeginOptional\u003e\u003e Exhibit A - Mozilla Public License.\n\n\u003c\u003cbeginOptional\u003e\u003e\"\u003c\u003cendOptional\u003e\u003eThe contents of this file are subject to the Mozilla Public License Version 1.1 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.mozilla.org/MPL/\n\nSoftware distributed under the License is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.\n\nThe Original Code is \u003c\u003cvar;name\u003d\"code\";original\u003d\"______________________________________\";match\u003d\".+\"\u003e\u003e .\n\nThe Initial Developer of the Original Code is \u003c\u003cvar;name\u003d\"initialDeveloper\";original\u003d\"________________________\";match\u003d\".+\"\u003e\u003e .\n\nPortions created by \u003c\u003cvar;name\u003d\"creator\";original\u003d\"______________________\";match\u003d\".+\"\u003e\u003e are Copyright (C) \u003c\u003cvar;name\u003d\"copyright\";original\u003d\"______\";match\u003d\".+\"\u003e\u003e . All Rights Reserved.\n\nContributor(s): \u003c\u003cvar;name\u003d\"contributor\";original\u003d\"______________________________________\";match\u003d\".+\"\u003e\u003e .\n\nAlternatively, the contents of this file may be used under the terms of the \u003c\u003cvar;name\u003d\"altLicense\";original\u003d\"_____\";match\u003d\".+\"\u003e\u003e license (the \" \u003c\u003cvar;name\u003d\"altLicenseShort1\";original\u003d\"[___]\";match\u003d\".+\"\u003e\u003e License\"), in which case the provisions of \u003c\u003cvar;name\u003d\"altLicenseShort2\";original\u003d\"[______]\";match\u003d\".+\"\u003e\u003e License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the \u003c\u003cvar;name\u003d\"altLicenseShort3\";original\u003d\"[____]\";match\u003d\".+\"\u003e\u003e License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the \u003c\u003cvar;name\u003d\"altLicenseShort4\";original\u003d\"[___]\";match\u003d\".+\"\u003e\u003e License. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the \u003c\u003cvar;name\u003d\"altLicenseShort5\";original\u003d\"[___]\";match\u003d\".+\"\u003e\u003e License.\u003c\u003cbeginOptional\u003e\u003e\"\u003c\u003cendOptional\u003e\u003e\n\nNOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications.\n\n\u003c\u003cendOptional\u003e\u003e", "name": "Mozilla Public License 1.1", "licenseComments": "This license has been superseded by v2.0", "licenseId": "MPL-1.1", - "standardLicenseHeader": "\"The contents of this file are subject to the Mozilla Public License Version 1.1 (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.mozilla.org/MPL/\n\nSoftware distributed under the License is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.\n\nThe Original Code is ______________________________________ .\n\nThe Initial Developer of the Original Code is ________________________ .\n\nPortions created by ______________________ are Copyright (C) ______ . All Rights Reserved.\n\nContributor(s): ______________________________________ .\n\nAlternatively, the contents of this file may be used under the terms of the _____ license (the \" [___] License\"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the [___] License.\"\n\n", + "standardLicenseHeader": "\"The contents of this file are subject to the Mozilla Public License Version 1.1 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.mozilla.org/MPL/\n\nSoftware distributed under the License is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.\n\nThe Original Code is ______________________________________ .\n\nThe Initial Developer of the Original Code is ________________________ .\n\nPortions created by ______________________ are Copyright (C) ______ . All Rights Reserved.\n\nContributor(s): ______________________________________ .\n\nAlternatively, the contents of this file may be used under the terms of the _____ license (the \" [___] License\"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the [___] License.\"\n\n", "crossRef": [ { - "match": "N/A", - "url": "https://opensource.org/licenses/MPL-1.1", + "match": "true", + "url": "http://www.mozilla.org/MPL/MPL-1.1.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:58Z", + "timestamp": "2024-08-19T17:36:45Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { - "match": "true", - "url": "http://www.mozilla.org/MPL/MPL-1.1.html", + "match": "N/A", + "url": "https://opensource.org/licenses/MPL-1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:58Z", + "timestamp": "2024-08-19T17:36:46Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ @@ -33,6 +33,6 @@ "https://opensource.org/licenses/MPL-1.1" ], "isOsiApproved": true, - "licenseTextHtml": "\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eMozilla Public License Version 1.1\u003c/p\u003e\n\n \u003c/div\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.\u003c/var\u003e\n Definitions.\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.0.1.\u003c/var\u003e\n \u0026quot;Commercial Use\u0026quot; means distribution or otherwise making the Covered Code\n available to a third party.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.1.\u003c/var\u003e\n \u0026quot;Contributor\u0026quot; means each entity that creates or contributes to the creation of\n Modifications.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.2.\u003c/var\u003e\n \u0026quot;Contributor Version\u0026quot; means the combination of the Original Code, prior\n Modifications used by a Contributor, and the Modifications made by that particular\n Contributor.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.3.\u003c/var\u003e\n \u0026quot;Covered Code\u0026quot; means the Original Code or Modifications or the combination of the\n Original Code and Modifications, in each case including portions thereof.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.4.\u003c/var\u003e\n \u0026quot;Electronic Distribution Mechanism\u0026quot; means a mechanism generally accepted in the\n software development community for the electronic transfer of data.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.5.\u003c/var\u003e\n \u0026quot;Executable\u0026quot; means Covered Code in any form other than Source Code.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.6.\u003c/var\u003e\n \u0026quot;Initial Developer\u0026quot; means the individual or entity identified as the Initial\n Developer in the Source Code notice required by Exhibit A.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.7.\u003c/var\u003e\n \u0026quot;Larger Work\u0026quot; means a work which combines Covered Code or portions thereof with\n code not governed by the terms of this License.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.8.\u003c/var\u003e\n \u0026quot;License\u0026quot; means this document.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.8.1.\u003c/var\u003e\n \u0026quot;Licensable\u0026quot; means having the right to grant, to the maximum extent\n possible, whether at the time of the initial grant or subsequently acquired, any and all\n of the rights conveyed herein.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.9.\u003c/var\u003e\n \u0026quot;Modifications\u0026quot; means any addition to or deletion from the substance or structure\n of either the Original Code or any previous Modifications. When Covered Code is released\n as a series of files, a Modification is:\n \u003cbr /\u003e\n\n Any addition to or deletion from the contents of a file containing Original Code or\n previous Modifications.\n \u003cbr /\u003e\n\n Any new file that contains any part of the Original Code or previous Modifications.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.10.\u003c/var\u003e\n \u0026quot;Original Code\u0026quot; means Source Code of computer software code which is described in\n the Source Code notice required by Exhibit A as Original Code, and which, at the time of\n its release under this License is not already Covered Code governed by this License.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.10.1.\u003c/var\u003e\n \u0026quot;Patent Claims\u0026quot; means any patent claim(s), now owned or hereafter acquired,\n including without limitation, method, process, and apparatus claims, in any patent\n Licensable by grantor.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.11.\u003c/var\u003e\n \u0026quot;Source Code\u0026quot; means the preferred form of the Covered Code for making modifications\n to it, including all modules it contains, plus any associated interface definition files,\n scripts used to control compilation and installation of an Executable, or source code\n differential comparisons against either the Original Code or another well known, available\n Covered Code of the Contributor\u0026apos;s choice. The Source Code can be in a compressed or\n archival form, provided the appropriate decompression or de-archiving software is widely\n available for no charge.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.12.\u003c/var\u003e\n \u0026quot;You\u0026quot; (or \u0026quot;Your\u0026quot;) means an individual or a legal entity exercising rights\n under, and complying with all of the terms of, this License or a future version of this\n License issued under Section 6.1. For legal entities, \u0026quot;You\u0026quot; includes any entity\n which controls, is controlled by, or is under common control with You. For purposes of\n this definition, \u0026quot;control\u0026quot; means (a) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or otherwise, or (b) ownership\n of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such\n entity.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.\u003c/var\u003e\n Source Code License.\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.1.\u003c/var\u003e\n The Initial Developer Grant. The Initial Developer hereby grants You a world-wide,\n royalty-free, non-exclusive license, subject to third party intellectual property\n claims:\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e a.\u003c/var\u003e\n under intellectual property rights (other than patent or trademark) Licensable by Initial\n Developer to use, reproduce, modify, display, perform, sublicense and distribute the\n Original Code (or portions thereof) with or without Modifications, and/or as part of a\n Larger Work; and\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e b.\u003c/var\u003e\n under Patents Claims infringed by the making, using or selling of Original Code, to make,\n have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the\n Original Code (or portions thereof).\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e c.\u003c/var\u003e\n the licenses granted in this Section 2.1 (a) and (b) are effective on the date Initial\n Developer first distributes Original Code under the terms of this License.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e d.\u003c/var\u003e\n Notwithstanding Section 2.1 (b) above, no patent license is granted: 1) for code that You\n delete from the Original Code; 2) separate from the Original Code; or 3) for\n infringements caused by: i) the modification of the Original Code or ii) the\n combination of the Original Code with other software or devices.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.2.\u003c/var\u003e\n Contributor Grant. Subject to third party intellectual property claims, each Contributor\n hereby grants You a world-wide, royalty-free, non-exclusive license\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e a.\u003c/var\u003e\n under intellectual property rights (other than patent or trademark) Licensable by\n Contributor, to use, reproduce, modify, display, perform, sublicense and distribute\n the Modifications created by such Contributor (or portions thereof) either on an\n unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger\n Work; and\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e b.\u003c/var\u003e\n under Patent Claims infringed by the making, using, or selling of Modifications made by\n that Contributor either alone and/or in combination with its Contributor Version (or\n portions of such combination), to make, use, sell, offer for sale, have made, and/or\n otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof);\n and 2) the combination of Modifications made by that Contributor with its Contributor\n Version (or portions of such combination).\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e c.\u003c/var\u003e\n the licenses granted in Sections 2.2 (a) and 2.2 (b) are effective on the date\n Contributor first makes Commercial Use of the Covered Code.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e d.\u003c/var\u003e\n Notwithstanding Section 2.2 (b) above, no patent license is granted: 1) for any code that\n Contributor has deleted from the Contributor Version; 2) separate from the Contributor\n Version; 3) for infringements caused by: i) third party modifications of Contributor\n Version or ii) the combination of Modifications made by that Contributor with other\n software (except as part of the Contributor Version) or other devices; or 4) under\n Patent Claims infringed by Covered Code in the absence of Modifications made by that\n Contributor.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.\u003c/var\u003e\n Distribution Obligations.\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.1.\u003c/var\u003e\n Application of License. The Modifications which You create or to which You contribute are\n governed by the terms of this License, including without limitation Section 2.2. The\n Source Code version of Covered Code may be distributed only under the terms of this\n License or a future version of this License released under Section 6.1, and You must\n include a copy of this License with every copy of the Source Code You distribute. You may\n not offer or impose any terms on any Source Code version that alters or restricts the\n applicable version of this License or the recipients\u0026apos; rights hereunder. However, You\n may include an additional document offering the additional rights described in Section\n 3.5.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.2.\u003c/var\u003e\n Availability of Source Code. Any Modification which You create or to which You contribute\n must be made available in Source Code form under the terms of this License either on the\n same media as an Executable version or via an accepted Electronic Distribution Mechanism\n to anyone to whom you made an Executable version available; and if made available via\n Electronic Distribution Mechanism, must remain available for at least twelve (12) months\n after the date it initially became available, or at least six (6) months after a\n subsequent version of that particular Modification has been made available to such\n recipients. You are responsible for ensuring that the Source Code version remains\n available even if the Electronic Distribution Mechanism is maintained by a third\n party.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.3.\u003c/var\u003e\n Description of Modifications. You must cause all Covered Code to which You contribute to\n contain a file documenting the changes You made to create that Covered Code and the date\n of any change. You must include a prominent statement that the Modification is derived,\n directly or indirectly, from Original Code provided by the Initial Developer and including\n the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an\n Executable version or related documentation in which You describe the origin or ownership\n of the Covered Code.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.4.\u003c/var\u003e\n Intellectual Property Matters\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n Third Party Claims\n \u003cbr /\u003e\n\n If Contributor has knowledge that a license under a third party\u0026apos;s intellectual\n property rights is required to exercise the rights granted by such Contributor\n under Sections 2.1 or 2.2, Contributor must include a text file with the\n Source Code distribution titled \u0026quot;LEGAL\u0026quot; which describes the claim\n and the party making the claim in sufficient detail that a recipient will know\n whom to contact. If Contributor obtains such knowledge after the Modification\n is made available as described in Section 3.2, Contributor shall promptly\n modify the LEGAL file in all copies Contributor makes available thereafter and\n shall take other steps (such as notifying appropriate mailing lists or\n newsgroups) reasonably calculated to inform those who received the Covered\n Code that new knowledge has been obtained.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n Contributor APIs\n \u003cbr /\u003e\n\n If Contributor\u0026apos;s Modifications include an application programming interface\n and Contributor has knowledge of patent licenses which are reasonably\n necessary to implement that API, Contributor must also include this\n information in the LEGAL file.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (c)\u003c/var\u003e\n Representations.\n \u003cbr /\u003e\n\n Contributor represents that, except as disclosed pursuant to Section 3.4 (a) above,\n Contributor believes that Contributor\u0026apos;s Modifications are\n Contributor\u0026apos;s original creation(s) and/or Contributor has sufficient\n rights to grant the rights conveyed by this License.\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.5.\u003c/var\u003e\n Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code.\n If it is not possible to put such notice in a particular Source Code file due to its\n structure, then You must include such notice in a location (such as a relevant directory)\n where a user would be likely to look for such a notice. If You created one or more\n Modification(s) You may add your name as a Contributor to the notice described in Exhibit\n A. You must also duplicate this License in any documentation for the Source Code where You\n describe recipients\u0026apos; rights or ownership rights relating to Covered Code. You may\n choose to offer, and to charge a fee for, warranty, support, indemnity or liability\n obligations to one or more recipients of Covered Code. However, You may do so only on Your\n own behalf, and not on behalf of the Initial Developer or any Contributor. You must make\n it absolutely clear than any such warranty, support, indemnity or liability obligation is\n offered by You alone, and You hereby agree to indemnify the Initial Developer and every\n Contributor for any liability incurred by the Initial Developer or such Contributor as a\n result of warranty, support, indemnity or liability terms You offer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.6.\u003c/var\u003e\n Distribution of Executable Versions. You may distribute Covered Code in Executable form only\n if the requirements of Sections 3.1, 3.2, 3.3, 3.4 and 3.5 have been met for that Covered\n Code, and if You include a notice stating that the Source Code version of the Covered Code\n is available under the terms of this License, including a description of how and where You\n have fulfilled the obligations of Section 3.2. The notice must be conspicuously included\n in any notice in an Executable version, related documentation or collateral in which You\n describe recipients\u0026apos; rights relating to the Covered Code. You may distribute the\n Executable version of Covered Code or ownership rights under a license of Your choice,\n which may contain terms different from this License, provided that You are in compliance\n with the terms of this License and that the license for the Executable version does not\n attempt to limit or alter the recipient\u0026apos;s rights in the Source Code version from the\n rights set forth in this License. If You distribute the Executable version under a\n different license You must make it absolutely clear that any terms which differ from this\n License are offered by You alone, not by the Initial Developer or any Contributor. You\n hereby agree to indemnify the Initial Developer and every Contributor for any liability\n incurred by the Initial Developer or such Contributor as a result of any such terms You\n offer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.7.\u003c/var\u003e\n Larger Works. You may create a Larger Work by combining Covered Code with other code not\n governed by the terms of this License and distribute the Larger Work as a single product.\n In such a case, You must make sure the requirements of this License are fulfilled for the\n Covered Code.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 4.\u003c/var\u003e\n Inability to Comply Due to Statute or Regulation.\n \u003cp\u003eIf it is impossible for You to comply with any of the terms of this License with respect to some\n or all of the Covered Code due to statute, judicial order, or regulation then You must: (a)\n comply with the terms of this License to the maximum extent possible; and (b) describe the\n limitations and the code they affect. Such description must be included in the LEGAL file\n described in Section 3.4 and must be included with all distributions of the Source Code.\n Except to the extent prohibited by statute or regulation, such description must be\n sufficiently detailed for a recipient of ordinary skill to be able to understand it.\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.\u003c/var\u003e\n Application of this License.\n \u003cbr /\u003e\n\n This License applies to code to which the Initial Developer has attached the notice in\n Exhibit A and to related Covered Code.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 6.\u003c/var\u003e\n Versions of the License.\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 6.1.\u003c/var\u003e\n New Versions\n \u003cbr /\u003e\n\n Netscape Communications Corporation (\u0026quot;Netscape\u0026quot;) may publish revised and/or\n new versions of the License from time to time. Each version will be given a\n distinguishing version number.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 6.2.\u003c/var\u003e\n Effect of New Versions\n \u003cbr /\u003e\n\n Once Covered Code has been published under a particular version of the License, You may\n always continue to use it under the terms of that version. You may also choose to\n use such Covered Code under the terms of any subsequent version of the License\n published by Netscape. No one other than Netscape has the right to modify the\n terms applicable to Covered Code created under this License.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 6.3.\u003c/var\u003e\n Derivative Works\n \u003cbr /\u003e\n\n If You create or use a modified version of this License (which you may only do in order\n to apply it to code which is not already Covered Code governed by this License),\n You must (a) rename Your license so that the phrases \u0026quot;Mozilla\u0026quot;,\n \u0026quot;MOZILLAPL\u0026quot;, \u0026quot;MOZPL\u0026quot;, \u0026quot;Netscape\u0026quot;, \u0026quot;MPL\u0026quot;,\n \u0026quot;NPL\u0026quot; or any confusingly similar phrase do not appear in your license\n (except to note that your license differs from this License) and (b) otherwise\n make it clear that Your version of the license contains terms which differ from\n the Mozilla Public License and Netscape Public License. (Filling in the name of\n the Initial Developer, Original Code or Contributor in the notice described in\n Exhibit A shall not of themselves be deemed to be modifications of this License.)\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 7.\u003c/var\u003e\n DISCLAIMER OF WARRANTY\n \u003cbr /\u003e\n\n COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN \u0026quot;AS IS\u0026quot; BASIS, WITHOUT WARRANTY\n OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES\n THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE\n OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED\n CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE\n INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY\n SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL\n PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER\n THIS DISCLAIMER.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 8.\u003c/var\u003e\n Termination\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 8.1.\u003c/var\u003e\n This License and the rights granted hereunder will terminate automatically if You fail to\n comply with terms herein and fail to cure such breach within 30 days of becoming aware of\n the breach. All sublicenses to the Covered Code which are properly granted shall survive\n any termination of this License. Provisions which, by their nature, must remain in effect\n beyond the termination of this License shall survive.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 8.2.\u003c/var\u003e\n If You initiate litigation by asserting a patent infringement claim (excluding declatory\n judgment actions) against Initial Developer or a Contributor (the Initial Developer or\n Contributor against whom You file such action is referred to as \u0026quot;Participant\u0026quot;)\n alleging that:\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e a.\u003c/var\u003e\n such Participant\u0026apos;s Contributor Version directly or indirectly infringes any patent,\n then any and all rights granted by such Participant to You under Sections 2.1 and/or\n 2.2 of this License shall, upon 60 days notice from Participant terminate\n prospectively, unless if within 60 days after receipt of notice You either: (i) agree\n in writing to pay Participant a mutually agreeable reasonable royalty for Your past\n and future use of Modifications made by such Participant, or (ii) withdraw Your\n litigation claim with respect to the Contributor Version against such Participant. If\n within 60 days of notice, a reasonable royalty and payment arrangement are not\n mutually agreed upon in writing by the parties or the litigation claim is not\n withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2\n automatically terminate at the expiration of the 60 day notice period specified\n above.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e b.\u003c/var\u003e\n any software, hardware, or device, other than such Participant\u0026apos;s Contributor\n Version, directly or indirectly infringes any patent, then any rights granted to You\n by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the\n date You first made, used, sold, distributed, or had made, Modifications made by that\n Participant.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 8.3.\u003c/var\u003e\n If You assert a patent infringement claim against Participant alleging that such\n Participant\u0026apos;s Contributor Version directly or indirectly infringes any patent where\n such claim is resolved (such as by license or settlement) prior to the initiation of\n patent infringement litigation, then the reasonable value of the licenses granted by such\n Participant under Sections 2.1 or 2.2 shall be taken into account in determining the\n amount or value of any payment or license.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 8.4.\u003c/var\u003e\n In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements\n (excluding distributors and resellers) which have been validly granted by You or any\n distributor hereunder prior to termination shall survive termination.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 9.\u003c/var\u003e\n LIMITATION OF LIABILITY\n \u003cbr /\u003e\n\n UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE),\n CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR\n ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO\n ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY\n CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE,\n COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES,\n EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS\n LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY\n RESULTING FROM SUCH PARTY\u0026apos;S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS\n SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF\n INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO\n YOU.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.\u003c/var\u003e\n U.S. government end users\n \u003cbr /\u003e\n\n The Covered Code is a \u0026quot;commercial item,\u0026quot; as that term is defined in 48 C.F.R.\n 2.101 (Oct. 1995), consisting of \u0026quot;commercial computer software\u0026quot; and\n \u0026quot;commercial computer software documentation,\u0026quot; as such terms are used in 48\n C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1\n through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code\n with only those rights set forth herein.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 11.\u003c/var\u003e\n Miscellaneous\n \u003cbr /\u003e\n\n This License represents the complete agreement concerning subject matter hereof. If any\n provision of this License is held to be unenforceable, such provision shall be\n reformed only to the extent necessary to make it enforceable. This License shall be\n governed by California law provisions (except to the extent applicable law, if any,\n provides otherwise), excluding its conflict-of-law provisions. With respect to\n disputes in which at least one party is a citizen of, or an entity chartered or\n registered to do business in the United States of America, any litigation relating to\n this License shall be subject to the jurisdiction of the Federal Courts of the\n Northern District of California, with venue lying in Santa Clara County, California,\n with the losing party responsible for costs, including without limitation, court costs\n and reasonable attorneys\u0026apos; fees and expenses. The application of the United\n Nations Convention on Contracts for the International Sale of Goods is expressly\n excluded. Any law or regulation which provides that the language of a contract shall\n be construed against the drafter shall not apply to this License.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 12.\u003c/var\u003e\n Responsibility for claims\n \u003cbr /\u003e\n\n As between Initial Developer and the Contributors, each party is responsible for claims and\n damages arising, directly or indirectly, out of its utilization of rights under this\n License and You agree to work with Initial Developer and Contributors to distribute\n such responsibility on an equitable basis. Nothing herein is intended or shall be\n deemed to constitute any admission of liability.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 13.\u003c/var\u003e\n Multiple-licensed code\n \u003cbr /\u003e\n\n Initial Developer may designate portions of the Covered Code as \u0026quot;Multiple-Licensed\u0026quot;.\n \u0026quot;Multiple-Licensed\u0026quot; means that the Initial Developer permits you to utilize portions of the\n Covered Code under Your choice of the MPL or the alternative licenses, if any, specified by the\n Initial Developer in the file described in Exhibit A.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eExhibit A - Mozilla Public License.\u003c/p\u003e\n\n \u003cp\u003e\u003cvar class\u003d\"optional-license-text\"\u003e\u0026quot;\u003c/var\u003eThe contents of this file are subject to the Mozilla Public License Version 1.1 (the\n \u0026quot;License\u0026quot;); you may not use this file except in compliance with the License. You may obtain\n a copy of the License at http://www.mozilla.org/MPL/\u003c/p\u003e\n\n \u003cp\u003eSoftware distributed under the License is distributed on an \u0026quot;AS IS\u0026quot; basis, WITHOUT WARRANTY OF\n ANY KIND, either express or implied. See the License for the specific language governing rights and\n limitations under the License.\u003c/p\u003e\n\n \u003cp\u003eThe Original Code is \u003cvar class\u003d\"replaceable-license-text\"\u003e ______________________________________\u003c/var\u003e.\u003c/p\u003e\n\n \u003cp\u003eThe Initial Developer of the Original Code is \u003cvar class\u003d\"replaceable-license-text\"\u003e ________________________\u003c/var\u003e.\n \u003cbr /\u003e\n\nPortions created by \u003cvar class\u003d\"replaceable-license-text\"\u003e ______________________\u003c/var\u003e are Copyright (C) \u003cvar class\u003d\"replaceable-license-text\"\u003e ______\u003c/var\u003e. All Rights Reserved.\n \u003c/p\u003e\n\n \u003cp\u003eContributor(s): \u003cvar class\u003d\"replaceable-license-text\"\u003e ______________________________________\u003c/var\u003e.\u003c/p\u003e\n\n \u003cp\u003eAlternatively, the contents of this file may be used under the terms of the \u003cvar class\u003d\"replaceable-license-text\"\u003e _____\u003c/var\u003e license (the\n \u0026quot;\u003cvar class\u003d\"replaceable-license-text\"\u003e [___]\u003c/var\u003e License\u0026quot;), in which case the provisions of \u003cvar class\u003d\"replaceable-license-text\"\u003e [______]\u003c/var\u003e License are applicable instead of\n those above. If you wish to allow use of your version of this file only under the terms of the \u003cvar class\u003d\"replaceable-license-text\"\u003e [____]\u003c/var\u003e\n License and not to allow others to use your version of this file under the MPL, indicate your decision\n by deleting the provisions above and replace them with the notice and other provisions required by the\n \u003cvar class\u003d\"replaceable-license-text\"\u003e [___]\u003c/var\u003e License. If you do not delete the provisions above, a recipient may use your version of this\n file under either the MPL or the \u003cvar class\u003d\"replaceable-license-text\"\u003e [___]\u003c/var\u003e License.\u003cvar class\u003d\"optional-license-text\"\u003e\u0026quot;\u003c/var\u003e\u003c/p\u003e\n\n \u003cp\u003eNOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code\n files of the Original Code. You should use the text of this Exhibit A rather than the text found in\n the Original Code Source Code for Your Modifications.\u003c/p\u003e\n\n \u003c/div\u003e\n ", - "standardLicenseHeaderHtml": "\n \u003cp\u003e\u003cvar class\u003d\"optional-license-text\"\u003e\u0026quot;\u003c/var\u003eThe contents of this file are subject to the Mozilla Public License Version 1.1 (the\n \u0026quot;License\u0026quot;); you may not use this file except in compliance with the License. You may obtain\n a copy of the License at http://www.mozilla.org/MPL/\u003c/p\u003e\n\n \u003cp\u003eSoftware distributed under the License is distributed on an \u0026quot;AS IS\u0026quot; basis, WITHOUT WARRANTY OF\n ANY KIND, either express or implied. See the License for the specific language governing rights and\n limitations under the License.\u003c/p\u003e\n\n \u003cp\u003eThe Original Code is \u003cvar class\u003d\"replaceable-license-text\"\u003e ______________________________________\u003c/var\u003e.\u003c/p\u003e\n\n \u003cp\u003eThe Initial Developer of the Original Code is \u003cvar class\u003d\"replaceable-license-text\"\u003e ________________________\u003c/var\u003e.\n \u003cbr /\u003e\n\nPortions created by \u003cvar class\u003d\"replaceable-license-text\"\u003e ______________________\u003c/var\u003e are Copyright (C) \u003cvar class\u003d\"replaceable-license-text\"\u003e ______\u003c/var\u003e. All Rights Reserved.\n \u003c/p\u003e\n\n \u003cp\u003eContributor(s): \u003cvar class\u003d\"replaceable-license-text\"\u003e ______________________________________\u003c/var\u003e.\u003c/p\u003e\n\n \u003cp\u003eAlternatively, the contents of this file may be used under the terms of the \u003cvar class\u003d\"replaceable-license-text\"\u003e _____\u003c/var\u003e license (the\n \u0026quot;\u003cvar class\u003d\"replaceable-license-text\"\u003e [___]\u003c/var\u003e License\u0026quot;), in which case the provisions of \u003cvar class\u003d\"replaceable-license-text\"\u003e [______]\u003c/var\u003e License are applicable instead of\n those above. If you wish to allow use of your version of this file only under the terms of the \u003cvar class\u003d\"replaceable-license-text\"\u003e [____]\u003c/var\u003e\n License and not to allow others to use your version of this file under the MPL, indicate your decision\n by deleting the provisions above and replace them with the notice and other provisions required by the\n \u003cvar class\u003d\"replaceable-license-text\"\u003e [___]\u003c/var\u003e License. If you do not delete the provisions above, a recipient may use your version of this\n file under either the MPL or the \u003cvar class\u003d\"replaceable-license-text\"\u003e [___]\u003c/var\u003e License.\u003cvar class\u003d\"optional-license-text\"\u003e\u0026quot;\u003c/var\u003e\u003c/p\u003e\n\n " + "licenseTextHtml": "\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eMozilla Public License Version 1.1\u003c/p\u003e\n\n \u003c/div\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.\u003c/var\u003e\n Definitions.\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.0.1.\u003c/var\u003e\n \u0026quot;Commercial Use\u0026quot; means distribution or otherwise making the Covered Code\n available to a third party.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.1.\u003c/var\u003e\n \u0026quot;Contributor\u0026quot; means each entity that creates or contributes to the creation of\n Modifications.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.2.\u003c/var\u003e\n \u0026quot;Contributor Version\u0026quot; means the combination of the Original Code, prior\n Modifications used by a Contributor, and the Modifications made by that particular\n Contributor.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.3.\u003c/var\u003e\n \u0026quot;Covered Code\u0026quot; means the Original Code or Modifications or the combination of the\n Original Code and Modifications, in each case including portions thereof.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.4.\u003c/var\u003e\n \u0026quot;Electronic Distribution Mechanism\u0026quot; means a mechanism generally accepted in the\n software development community for the electronic transfer of data.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.5.\u003c/var\u003e\n \u0026quot;Executable\u0026quot; means Covered Code in any form other than Source Code.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.6.\u003c/var\u003e\n \u0026quot;Initial Developer\u0026quot; means the individual or entity identified as the Initial\n Developer in the Source Code notice required by Exhibit A.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.7.\u003c/var\u003e\n \u0026quot;Larger Work\u0026quot; means a work which combines Covered Code or portions thereof with\n code not governed by the terms of this License.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.8.\u003c/var\u003e\n \u0026quot;License\u0026quot; means this document.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.8.1.\u003c/var\u003e\n \u0026quot;Licensable\u0026quot; means having the right to grant, to the maximum extent\n possible, whether at the time of the initial grant or subsequently acquired, any and all\n of the rights conveyed herein.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.9.\u003c/var\u003e\n \u0026quot;Modifications\u0026quot; means any addition to or deletion from the substance or structure\n of either the Original Code or any previous Modifications. When Covered Code is released\n as a series of files, a Modification is:\n \u003cbr /\u003e\n\n Any addition to or deletion from the contents of a file containing Original Code or\n previous Modifications.\n \u003cbr /\u003e\n\n Any new file that contains any part of the Original Code or previous Modifications.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.10.\u003c/var\u003e\n \u0026quot;Original Code\u0026quot; means Source Code of computer software code which is described in\n the Source Code notice required by Exhibit A as Original Code, and which, at the time of\n its release under this License is not already Covered Code governed by this License.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.10.1.\u003c/var\u003e\n \u0026quot;Patent Claims\u0026quot; means any patent claim(s), now owned or hereafter acquired,\n including without limitation, method, process, and apparatus claims, in any patent\n Licensable by grantor.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.11.\u003c/var\u003e\n \u0026quot;Source Code\u0026quot; means the preferred form of the Covered Code for making modifications\n to it, including all modules it contains, plus any associated interface definition files,\n scripts used to control compilation and installation of an Executable, or source code\n differential comparisons against either the Original Code or another well known, available\n Covered Code of the Contributor\u0026apos;s choice. The Source Code can be in a compressed or\n archival form, provided the appropriate decompression or de-archiving software is widely\n available for no charge.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.12.\u003c/var\u003e\n \u0026quot;You\u0026quot; (or \u0026quot;Your\u0026quot;) means an individual or a legal entity exercising rights\n under, and complying with all of the terms of, this License or a future version of this\n License issued under Section 6.1. For legal entities, \u0026quot;You\u0026quot; includes any entity\n which controls, is controlled by, or is under common control with You. For purposes of\n this definition, \u0026quot;control\u0026quot; means (a) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or otherwise, or (b) ownership\n of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such\n entity.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.\u003c/var\u003e\n Source Code License.\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.1.\u003c/var\u003e\n The Initial Developer Grant. The Initial Developer hereby grants You a world-wide,\n royalty-free, non-exclusive license, subject to third party intellectual property\n claims:\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e a.\u003c/var\u003e\n under intellectual property rights (other than patent or trademark) Licensable by Initial\n Developer to use, reproduce, modify, display, perform, sublicense and distribute the\n Original Code (or portions thereof) with or without Modifications, and/or as part of a\n Larger Work; and\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e b.\u003c/var\u003e\n under Patents Claims infringed by the making, using or selling of Original Code, to make,\n have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the\n Original Code (or portions thereof).\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e c.\u003c/var\u003e\n the licenses granted in this Section 2.1 (a) and (b) are effective on the date Initial\n Developer first distributes Original Code under the terms of this License.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e d.\u003c/var\u003e\n Notwithstanding Section 2.1 (b) above, no patent license is granted: 1) for code that You\n delete from the Original Code; 2) separate from the Original Code; or 3) for\n infringements caused by: i) the modification of the Original Code or ii) the\n combination of the Original Code with other software or devices.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.2.\u003c/var\u003e\n Contributor Grant. Subject to third party intellectual property claims, each Contributor\n hereby grants You a world-wide, royalty-free, non-exclusive license\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e a.\u003c/var\u003e\n under intellectual property rights (other than patent or trademark) Licensable by\n Contributor, to use, reproduce, modify, display, perform, sublicense and distribute\n the Modifications created by such Contributor (or portions thereof) either on an\n unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger\n Work; and\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e b.\u003c/var\u003e\n under Patent Claims infringed by the making, using, or selling of Modifications made by\n that Contributor either alone and/or in combination with its Contributor Version (or\n portions of such combination), to make, use, sell, offer for sale, have made, and/or\n otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof);\n and 2) the combination of Modifications made by that Contributor with its Contributor\n Version (or portions of such combination).\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e c.\u003c/var\u003e\n the licenses granted in Sections 2.2 (a) and 2.2 (b) are effective on the date\n Contributor first makes Commercial Use of the Covered Code.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e d.\u003c/var\u003e\n Notwithstanding Section 2.2 (b) above, no patent license is granted: 1) for any code that\n Contributor has deleted from the Contributor Version; 2) separate from the Contributor\n Version; 3) for infringements caused by: i) third party modifications of Contributor\n Version or ii) the combination of Modifications made by that Contributor with other\n software (except as part of the Contributor Version) or other devices; or 4) under\n Patent Claims infringed by Covered Code in the absence of Modifications made by that\n Contributor.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.\u003c/var\u003e\n Distribution Obligations.\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.1.\u003c/var\u003e\n Application of License. The Modifications which You create or to which You contribute are\n governed by the terms of this License, including without limitation Section 2.2. The\n Source Code version of Covered Code may be distributed only under the terms of this\n License or a future version of this License released under Section 6.1, and You must\n include a copy of this License with every copy of the Source Code You distribute. You may\n not offer or impose any terms on any Source Code version that alters or restricts the\n applicable version of this License or the recipients\u0026apos; rights hereunder. However, You\n may include an additional document offering the additional rights described in Section\n 3.5.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.2.\u003c/var\u003e\n Availability of Source Code. Any Modification which You create or to which You contribute\n must be made available in Source Code form under the terms of this License either on the\n same media as an Executable version or via an accepted Electronic Distribution Mechanism\n to anyone to whom you made an Executable version available; and if made available via\n Electronic Distribution Mechanism, must remain available for at least twelve (12) months\n after the date it initially became available, or at least six (6) months after a\n subsequent version of that particular Modification has been made available to such\n recipients. You are responsible for ensuring that the Source Code version remains\n available even if the Electronic Distribution Mechanism is maintained by a third\n party.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.3.\u003c/var\u003e\n Description of Modifications. You must cause all Covered Code to which You contribute to\n contain a file documenting the changes You made to create that Covered Code and the date\n of any change. You must include a prominent statement that the Modification is derived,\n directly or indirectly, from Original Code provided by the Initial Developer and including\n the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an\n Executable version or related documentation in which You describe the origin or ownership\n of the Covered Code.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.4.\u003c/var\u003e\n Intellectual Property Matters\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n Third Party Claims\n \u003cbr /\u003e\n\n If Contributor has knowledge that a license under a third party\u0026apos;s intellectual\n property rights is required to exercise the rights granted by such Contributor\n under Sections 2.1 or 2.2, Contributor must include a text file with the\n Source Code distribution titled \u0026quot;LEGAL\u0026quot; which describes the claim\n and the party making the claim in sufficient detail that a recipient will know\n whom to contact. If Contributor obtains such knowledge after the Modification\n is made available as described in Section 3.2, Contributor shall promptly\n modify the LEGAL file in all copies Contributor makes available thereafter and\n shall take other steps (such as notifying appropriate mailing lists or\n newsgroups) reasonably calculated to inform those who received the Covered\n Code that new knowledge has been obtained.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n Contributor APIs\n \u003cbr /\u003e\n\n If Contributor\u0026apos;s Modifications include an application programming interface\n and Contributor has knowledge of patent licenses which are reasonably\n necessary to implement that API, Contributor must also include this\n information in the LEGAL file.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (c)\u003c/var\u003e\n Representations.\n \u003cbr /\u003e\n\n Contributor represents that, except as disclosed pursuant to Section 3.4 (a) above,\n Contributor believes that Contributor\u0026apos;s Modifications are\n Contributor\u0026apos;s original creation(s) and/or Contributor has sufficient\n rights to grant the rights conveyed by this License.\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.5.\u003c/var\u003e\n Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code.\n If it is not possible to put such notice in a particular Source Code file due to its\n structure, then You must include such notice in a location (such as a relevant directory)\n where a user would be likely to look for such a notice. If You created one or more\n Modification(s) You may add your name as a Contributor to the notice described in Exhibit\n A. You must also duplicate this License in any documentation for the Source Code where You\n describe recipients\u0026apos; rights or ownership rights relating to Covered Code. You may\n choose to offer, and to charge a fee for, warranty, support, indemnity or liability\n obligations to one or more recipients of Covered Code. However, You may do so only on Your\n own behalf, and not on behalf of the Initial Developer or any Contributor. You must make\n it absolutely clear than any such warranty, support, indemnity or liability obligation is\n offered by You alone, and You hereby agree to indemnify the Initial Developer and every\n Contributor for any liability incurred by the Initial Developer or such Contributor as a\n result of warranty, support, indemnity or liability terms You offer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.6.\u003c/var\u003e\n Distribution of Executable Versions. You may distribute Covered Code in Executable form only\n if the requirements of Sections 3.1, 3.2, 3.3, 3.4 and 3.5 have been met for that Covered\n Code, and if You include a notice stating that the Source Code version of the Covered Code\n is available under the terms of this License, including a description of how and where You\n have fulfilled the obligations of Section 3.2. The notice must be conspicuously included\n in any notice in an Executable version, related documentation or collateral in which You\n describe recipients\u0026apos; rights relating to the Covered Code. You may distribute the\n Executable version of Covered Code or ownership rights under a license of Your choice,\n which may contain terms different from this License, provided that You are in compliance\n with the terms of this License and that the license for the Executable version does not\n attempt to limit or alter the recipient\u0026apos;s rights in the Source Code version from the\n rights set forth in this License. If You distribute the Executable version under a\n different license You must make it absolutely clear that any terms which differ from this\n License are offered by You alone, not by the Initial Developer or any Contributor. You\n hereby agree to indemnify the Initial Developer and every Contributor for any liability\n incurred by the Initial Developer or such Contributor as a result of any such terms You\n offer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.7.\u003c/var\u003e\n Larger Works. You may create a Larger Work by combining Covered Code with other code not\n governed by the terms of this License and distribute the Larger Work as a single product.\n In such a case, You must make sure the requirements of this License are fulfilled for the\n Covered Code.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 4.\u003c/var\u003e\n Inability to Comply Due to Statute or Regulation.\n \u003cp\u003eIf it is impossible for You to comply with any of the terms of this License with respect to some\n or all of the Covered Code due to statute, judicial order, or regulation then You must: (a)\n comply with the terms of this License to the maximum extent possible; and (b) describe the\n limitations and the code they affect. Such description must be included in the LEGAL file\n described in Section 3.4 and must be included with all distributions of the Source Code.\n Except to the extent prohibited by statute or regulation, such description must be\n sufficiently detailed for a recipient of ordinary skill to be able to understand it.\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.\u003c/var\u003e\n Application of this License.\n \u003cbr /\u003e\n\n This License applies to code to which the Initial Developer has attached the notice in\n Exhibit A and to related Covered Code.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 6.\u003c/var\u003e\n Versions of the License.\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 6.1.\u003c/var\u003e\n New Versions\n \u003cbr /\u003e\n\n Netscape Communications Corporation (\u0026quot;Netscape\u0026quot;) may publish revised and/or\n new versions of the License from time to time. Each version will be given a\n distinguishing version number.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 6.2.\u003c/var\u003e\n Effect of New Versions\n \u003cbr /\u003e\n\n Once Covered Code has been published under a particular version of the License, You may\n always continue to use it under the terms of that version. You may also choose to\n use such Covered Code under the terms of any subsequent version of the License\n published by Netscape. No one other than Netscape has the right to modify the\n terms applicable to Covered Code created under this License.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 6.3.\u003c/var\u003e\n Derivative Works\n \u003cbr /\u003e\n\n If You create or use a modified version of this License (which you may only do in order\n to apply it to code which is not already Covered Code governed by this License),\n You must (a) rename Your license so that the phrases \u0026quot;Mozilla\u0026quot;,\n \u0026quot;MOZILLAPL\u0026quot;, \u0026quot;MOZPL\u0026quot;, \u0026quot;Netscape\u0026quot;, \u0026quot;MPL\u0026quot;,\n \u0026quot;NPL\u0026quot; or any confusingly similar phrase do not appear in your license\n (except to note that your license differs from this License) and (b) otherwise\n make it clear that Your version of the license contains terms which differ from\n the Mozilla Public License and Netscape Public License. (Filling in the name of\n the Initial Developer, Original Code or Contributor in the notice described in\n Exhibit A shall not of themselves be deemed to be modifications of this License.)\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 7.\u003c/var\u003e\n DISCLAIMER OF WARRANTY\n \u003cbr /\u003e\n\n COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN \u0026quot;AS IS\u0026quot; BASIS, WITHOUT WARRANTY\n OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES\n THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE\n OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED\n CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE\n INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY\n SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL\n PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER\n THIS DISCLAIMER.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 8.\u003c/var\u003e\n Termination\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 8.1.\u003c/var\u003e\n This License and the rights granted hereunder will terminate automatically if You fail to\n comply with terms herein and fail to cure such breach within 30 days of becoming aware of\n the breach. All sublicenses to the Covered Code which are properly granted shall survive\n any termination of this License. Provisions which, by their nature, must remain in effect\n beyond the termination of this License shall survive.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 8.2.\u003c/var\u003e\n If You initiate litigation by asserting a patent infringement claim (excluding declatory\n judgment actions) against Initial Developer or a Contributor (the Initial Developer or\n Contributor against whom You file such action is referred to as \u0026quot;Participant\u0026quot;)\n alleging that:\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e a.\u003c/var\u003e\n such Participant\u0026apos;s Contributor Version directly or indirectly infringes any patent,\n then any and all rights granted by such Participant to You under Sections 2.1 and/or\n 2.2 of this License shall, upon 60 days notice from Participant terminate\n prospectively, unless if within 60 days after receipt of notice You either: (i) agree\n in writing to pay Participant a mutually agreeable reasonable royalty for Your past\n and future use of Modifications made by such Participant, or (ii) withdraw Your\n litigation claim with respect to the Contributor Version against such Participant. If\n within 60 days of notice, a reasonable royalty and payment arrangement are not\n mutually agreed upon in writing by the parties or the litigation claim is not\n withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2\n automatically terminate at the expiration of the 60 day notice period specified\n above.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e b.\u003c/var\u003e\n any software, hardware, or device, other than such Participant\u0026apos;s Contributor\n Version, directly or indirectly infringes any patent, then any rights granted to You\n by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the\n date You first made, used, sold, distributed, or had made, Modifications made by that\n Participant.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 8.3.\u003c/var\u003e\n If You assert a patent infringement claim against Participant alleging that such\n Participant\u0026apos;s Contributor Version directly or indirectly infringes any patent where\n such claim is resolved (such as by license or settlement) prior to the initiation of\n patent infringement litigation, then the reasonable value of the licenses granted by such\n Participant under Sections 2.1 or 2.2 shall be taken into account in determining the\n amount or value of any payment or license.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 8.4.\u003c/var\u003e\n In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements\n (excluding distributors and resellers) which have been validly granted by You or any\n distributor hereunder prior to termination shall survive termination.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 9.\u003c/var\u003e\n LIMITATION OF LIABILITY\n \u003cbr /\u003e\n\n UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE),\n CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR\n ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO\n ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY\n CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE,\n COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES,\n EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS\n LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY\n RESULTING FROM SUCH PARTY\u0026apos;S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS\n SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF\n INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO\n YOU.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.\u003c/var\u003e\n U.S. government end users\n \u003cbr /\u003e\n\n The Covered Code is a \u0026quot;commercial item,\u0026quot; as that term is defined in 48 C.F.R.\n 2.101 (Oct. 1995), consisting of \u0026quot;commercial computer software\u0026quot; and\n \u0026quot;commercial computer software documentation,\u0026quot; as such terms are used in 48\n C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1\n through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code\n with only those rights set forth herein.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 11.\u003c/var\u003e\n Miscellaneous\n \u003cbr /\u003e\n\n This License represents the complete agreement concerning subject matter hereof. If any\n provision of this License is held to be unenforceable, such provision shall be\n reformed only to the extent necessary to make it enforceable. This License shall be\n governed by California law provisions (except to the extent applicable law, if any,\n provides otherwise), excluding its conflict-of-law provisions. With respect to\n disputes in which at least one party is a citizen of, or an entity chartered or\n registered to do business in the United States of America, any litigation relating to\n this License shall be subject to the jurisdiction of the Federal Courts of the\n Northern District of California, with venue lying in Santa Clara County, California,\n with the losing party responsible for costs, including without limitation, court costs\n and reasonable attorneys\u0026apos; fees and expenses. The application of the United\n Nations Convention on Contracts for the International Sale of Goods is expressly\n excluded. Any law or regulation which provides that the language of a contract shall\n be construed against the drafter shall not apply to this License.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 12.\u003c/var\u003e\n Responsibility for claims\n \u003cbr /\u003e\n\n As between Initial Developer and the Contributors, each party is responsible for claims and\n damages arising, directly or indirectly, out of its utilization of rights under this\n License and You agree to work with Initial Developer and Contributors to distribute\n such responsibility on an equitable basis. Nothing herein is intended or shall be\n deemed to constitute any admission of liability.\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 13.\u003c/var\u003e\n Multiple-licensed code\n \u003cbr /\u003e\n\n Initial Developer may designate portions of the Covered Code as \u0026quot;Multiple-Licensed\u0026quot;.\n \u0026quot;Multiple-Licensed\u0026quot; means that the Initial Developer permits you to utilize portions of the\n Covered Code under Your choice of the MPL or the alternative licenses, if any, specified by the\n Initial Developer in the file described in Exhibit A.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eExhibit A - Mozilla Public License.\u003c/p\u003e\n\n \u003cp\u003e\u003cvar class\u003d\"optional-license-text\"\u003e\u0026quot;\u003c/var\u003eThe contents of this file are subject to the Mozilla Public License Version 1.1 (the\n \u0026quot;License\u0026quot;); you may not use this file except in compliance with the License. You may obtain\n a copy of the License at https://www.mozilla.org/MPL/\u003c/p\u003e\n\n \u003cp\u003eSoftware distributed under the License is distributed on an \u0026quot;AS IS\u0026quot; basis, WITHOUT WARRANTY OF\n ANY KIND, either express or implied. See the License for the specific language governing rights and\n limitations under the License.\u003c/p\u003e\n\n \u003cp\u003eThe Original Code is \u003cvar class\u003d\"replaceable-license-text\"\u003e ______________________________________\u003c/var\u003e.\u003c/p\u003e\n\n \u003cp\u003eThe Initial Developer of the Original Code is \u003cvar class\u003d\"replaceable-license-text\"\u003e ________________________\u003c/var\u003e.\n \u003cbr /\u003e\n\nPortions created by \u003cvar class\u003d\"replaceable-license-text\"\u003e ______________________\u003c/var\u003e are Copyright (C) \u003cvar class\u003d\"replaceable-license-text\"\u003e ______\u003c/var\u003e. All Rights Reserved.\n \u003c/p\u003e\n\n \u003cp\u003eContributor(s): \u003cvar class\u003d\"replaceable-license-text\"\u003e ______________________________________\u003c/var\u003e.\u003c/p\u003e\n\n \u003cp\u003eAlternatively, the contents of this file may be used under the terms of the \u003cvar class\u003d\"replaceable-license-text\"\u003e _____\u003c/var\u003e license (the\n \u0026quot;\u003cvar class\u003d\"replaceable-license-text\"\u003e [___]\u003c/var\u003e License\u0026quot;), in which case the provisions of \u003cvar class\u003d\"replaceable-license-text\"\u003e [______]\u003c/var\u003e License are applicable instead of\n those above. If you wish to allow use of your version of this file only under the terms of the \u003cvar class\u003d\"replaceable-license-text\"\u003e [____]\u003c/var\u003e\n License and not to allow others to use your version of this file under the MPL, indicate your decision\n by deleting the provisions above and replace them with the notice and other provisions required by the\n \u003cvar class\u003d\"replaceable-license-text\"\u003e [___]\u003c/var\u003e License. If you do not delete the provisions above, a recipient may use your version of this\n file under either the MPL or the \u003cvar class\u003d\"replaceable-license-text\"\u003e [___]\u003c/var\u003e License.\u003cvar class\u003d\"optional-license-text\"\u003e\u0026quot;\u003c/var\u003e\u003c/p\u003e\n\n \u003cp\u003eNOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code\n files of the Original Code. You should use the text of this Exhibit A rather than the text found in\n the Original Code Source Code for Your Modifications.\u003c/p\u003e\n\n \u003c/div\u003e\n ", + "standardLicenseHeaderHtml": "\n \u003cp\u003e\u003cvar class\u003d\"optional-license-text\"\u003e\u0026quot;\u003c/var\u003eThe contents of this file are subject to the Mozilla Public License Version 1.1 (the\n \u0026quot;License\u0026quot;); you may not use this file except in compliance with the License. You may obtain\n a copy of the License at https://www.mozilla.org/MPL/\u003c/p\u003e\n\n \u003cp\u003eSoftware distributed under the License is distributed on an \u0026quot;AS IS\u0026quot; basis, WITHOUT WARRANTY OF\n ANY KIND, either express or implied. See the License for the specific language governing rights and\n limitations under the License.\u003c/p\u003e\n\n \u003cp\u003eThe Original Code is \u003cvar class\u003d\"replaceable-license-text\"\u003e ______________________________________\u003c/var\u003e.\u003c/p\u003e\n\n \u003cp\u003eThe Initial Developer of the Original Code is \u003cvar class\u003d\"replaceable-license-text\"\u003e ________________________\u003c/var\u003e.\n \u003cbr /\u003e\n\nPortions created by \u003cvar class\u003d\"replaceable-license-text\"\u003e ______________________\u003c/var\u003e are Copyright (C) \u003cvar class\u003d\"replaceable-license-text\"\u003e ______\u003c/var\u003e. All Rights Reserved.\n \u003c/p\u003e\n\n \u003cp\u003eContributor(s): \u003cvar class\u003d\"replaceable-license-text\"\u003e ______________________________________\u003c/var\u003e.\u003c/p\u003e\n\n \u003cp\u003eAlternatively, the contents of this file may be used under the terms of the \u003cvar class\u003d\"replaceable-license-text\"\u003e _____\u003c/var\u003e license (the\n \u0026quot;\u003cvar class\u003d\"replaceable-license-text\"\u003e [___]\u003c/var\u003e License\u0026quot;), in which case the provisions of \u003cvar class\u003d\"replaceable-license-text\"\u003e [______]\u003c/var\u003e License are applicable instead of\n those above. If you wish to allow use of your version of this file only under the terms of the \u003cvar class\u003d\"replaceable-license-text\"\u003e [____]\u003c/var\u003e\n License and not to allow others to use your version of this file under the MPL, indicate your decision\n by deleting the provisions above and replace them with the notice and other provisions required by the\n \u003cvar class\u003d\"replaceable-license-text\"\u003e [___]\u003c/var\u003e License. If you do not delete the provisions above, a recipient may use your version of this\n file under either the MPL or the \u003cvar class\u003d\"replaceable-license-text\"\u003e [___]\u003c/var\u003e License.\u003cvar class\u003d\"optional-license-text\"\u003e\u0026quot;\u003c/var\u003e\u003c/p\u003e\n\n " } \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/MPL-2.0-no-copyleft-exception.json b/src/main/resources/license-list-data/json/details/MPL-2.0-no-copyleft-exception.json index 825fbdfb7d..84c14b3817 100644 --- a/src/main/resources/license-list-data/json/details/MPL-2.0-no-copyleft-exception.json +++ b/src/main/resources/license-list-data/json/details/MPL-2.0-no-copyleft-exception.json @@ -1,19 +1,19 @@ { "isDeprecatedLicenseId": false, "licenseText": "Mozilla Public License Version 2.0\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n means the combination of the Contributions of others (if any) used\n by a Contributor and that particular Contributor\u0027s Contribution.\n\n1.3. \"Contribution\"\n means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n means Source Code Form to which the initial Contributor has attached\n the notice in Exhibit A, the Executable Form of such Source Code\n Form, and Modifications of such Source Code Form, in each case\n including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n means\n\n (a) that the initial Contributor has attached the notice described\n in Exhibit B to the Covered Software; or\n\n (b) that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the\n terms of a Secondary License.\n\n1.6. \"Executable Form\"\n means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n means a work that combines Covered Software with other material, in \n a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n means this document.\n\n1.9. \"Licensable\"\n means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and\n all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n means any of the following:\n\n (a) any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered\n Software; or\n\n (b) any new file in Source Code Form that contains any Covered\n Software.\n\n1.11. \"Patent Claims\" of a Contributor\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the\n License, by the making, using, selling, offering for sale, having\n made, import, or transfer of either its Contributions or its\n Contributor Version.\n\n1.12. \"Secondary License\"\n means either the GNU General Public License, Version 2.0, the GNU\n Lesser General Public License, Version 2.1, the GNU Affero General\n Public License, Version 3.0, or any later versions of those\n licenses.\n\n1.13. \"Source Code Form\"\n means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that\n controls, is controlled by, or is under common control with You. For\n purposes of this definition, \"control\" means (a) the power, direct\n or indirect, to cause the direction or management of such entity,\n whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial\n ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n for sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n or\n\n(b) for infringements caused by: (i) Your and any other third party\u0027s\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients\u0027 rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n Form, as described in Section 3.1, and You must inform recipients of\n the Executable Form how they can obtain a copy of such Source Code\n Form by reasonable means in a timely manner, at a charge no more\n than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n License, or sublicense it under different terms, provided that the\n license for the Executable Form does not attempt to limit or alter\n the recipients\u0027 rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n* *\n* 6. Disclaimer of Warranty *\n* ------------------------- *\n* *\n* Covered Software is provided under this License on an \"as is\" *\n* basis, without warranty of any kind, either expressed, implied, or *\n* statutory, including, without limitation, warranties that the *\n* Covered Software is free of defects, merchantable, fit for a *\n* particular purpose or non-infringing. The entire risk as to the *\n* quality and performance of the Covered Software is with You. *\n* Should any Covered Software prove defective in any respect, You *\n* (not any Contributor) assume the cost of any necessary servicing, *\n* repair, or correction. This disclaimer of warranty constitutes an *\n* essential part of this License. No use of any Covered Software is *\n* authorized under this License except under this disclaimer. *\n* *\n************************************************************************\n\n************************************************************************\n* *\n* 7. Limitation of Liability *\n* -------------------------- *\n* *\n* Under no circumstances and under no legal theory, whether tort *\n* (including negligence), contract, or otherwise, shall any *\n* Contributor, or anyone who distributes Covered Software as *\n* permitted above, be liable to You for any direct, indirect, *\n* special, incidental, or consequential damages of any character *\n* including, without limitation, damages for lost profits, loss of *\n* goodwill, work stoppage, computer failure or malfunction, or any *\n* and all other commercial damages or losses, even if such party *\n* shall have been informed of the possibility of such damages. This *\n* limitation of liability shall not apply to liability for death or *\n* personal injury resulting from such party\u0027s negligence to the *\n* extent applicable law prohibits such limitation. Some *\n* jurisdictions do not allow the exclusion or limitation of *\n* incidental or consequential damages, so this exclusion and *\n* limitation may not apply to You. *\n* *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party\u0027s ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL was not distributed with this\n file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n This Source Code Form is \"Incompatible With Secondary Licenses\", as\n defined by the Mozilla Public License, v. 2.0.\n", - "standardLicenseHeaderTemplate": "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n\nThis Source Code Form is \"Incompatible With Secondary Licenses\", as defined by the Mozilla Public License, v. 2.0.\n\n", - "standardLicenseTemplate": "\u003c\u003cbeginOptional\u003e\u003eMozilla Public License Version 2.0\n\n\u003c\u003cbeginOptional\u003e\u003e\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\n\u003c\u003cendOptional\u003e\u003e\u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.\";match\u003d\".{0,20}\"\u003e\u003e Definitions\n\n \u003c\u003cbeginOptional\u003e\u003e--------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.1.\";match\u003d\".{0,20}\"\u003e\u003e \"Contributor\" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.2.\";match\u003d\".{0,20}\"\u003e\u003e \"Contributor Version\" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor\u0027s Contribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.3.\";match\u003d\".{0,20}\"\u003e\u003e \"Contribution\" means Covered Software of a particular Contributor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.4.\";match\u003d\".{0,20}\"\u003e\u003e \"Covered Software\" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.5.\";match\u003d\".{0,20}\"\u003e\u003e \"Incompatible With Secondary Licenses\" means\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.6.\";match\u003d\".{0,20}\"\u003e\u003e \"Executable Form\" means any form of the work other than Source Code Form.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.7.\";match\u003d\".{0,20}\"\u003e\u003e \"Larger Work\" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.8.\";match\u003d\".{0,20}\"\u003e\u003e \"License\" means this document.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.9.\";match\u003d\".{0,20}\"\u003e\u003e \"Licensable\" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.10.\";match\u003d\".{0,20}\"\u003e\u003e \"Modifications\" means any of the following:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e any new file in Source Code Form that contains any Covered Software.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.11.\";match\u003d\".{0,20}\"\u003e\u003e \"Patent Claims\" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.12.\";match\u003d\".{0,20}\"\u003e\u003e \"Secondary License\" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.13.\";match\u003d\".{0,20}\"\u003e\u003e \"Source Code Form\" means the form of the work preferred for making modifications.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.14.\";match\u003d\".{0,20}\"\u003e\u003e \"You\" (or \"Your\") means an individual or a legal entity exercising rights under this License. For legal entities, \"You\" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, \"control\" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.\";match\u003d\".{0,20}\"\u003e\u003e License Grants and Conditions\n\n \u003c\u003cbeginOptional\u003e\u003e--------------------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.1.\";match\u003d\".{0,20}\"\u003e\u003e Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.2.\";match\u003d\".{0,20}\"\u003e\u003e Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.3.\";match\u003d\".{0,20}\"\u003e\u003e Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e for any code that a Contributor has removed from Covered Software; or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e for infringements caused by: (i) Your and any other third party\u0027s modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(c)\";match\u003d\".{0,20}\"\u003e\u003e under Patent Claims infringed by Covered Software in the absence of its Contributions.\n\n This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.4.\";match\u003d\".{0,20}\"\u003e\u003e Subsequent Licenses\n\n No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.5.\";match\u003d\".{0,20}\"\u003e\u003e Representation\n\n Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.6.\";match\u003d\".{0,20}\"\u003e\u003e Fair Use\n\n This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.7.\";match\u003d\".{0,20}\"\u003e\u003e Conditions\n\n Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.\";match\u003d\".{0,20}\"\u003e\u003e Responsibilities\n\n \u003c\u003cbeginOptional\u003e\u003e-------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.1.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of Source Form\n\n All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients\u0027 rights in the Source Code Form.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.2.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of Executable Form\n\n If You distribute Covered Software in Executable Form then:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients\u0027 rights in the Source Code Form under this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.3.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of a Larger Work\n\n You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.4.\";match\u003d\".{0,20}\"\u003e\u003e Notices\n\n You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.5.\";match\u003d\".{0,20}\"\u003e\u003e Application of Additional Terms\n\n You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"4.\";match\u003d\".{0,20}\"\u003e\u003e Inability to Comply Due to Statute or Regulation\n\n \u003c\u003cbeginOptional\u003e\u003e---------------------------------------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.\";match\u003d\".{0,20}\"\u003e\u003e Termination\n\n \u003c\u003cbeginOptional\u003e\u003e--------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.1.\";match\u003d\".{0,20}\"\u003e\u003e The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.2.\";match\u003d\".{0,20}\"\u003e\u003e If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.3.\";match\u003d\".{0,20}\"\u003e\u003e In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination.\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"6.\";match\u003d\".{0,20}\"\u003e\u003e Disclaimer of Warranty\n\n \u003c\u003cbeginOptional\u003e\u003e* ------------------------- *\n\n \u003c\u003cendOptional\u003e\u003e\n\n Covered Software is provided under this License on an \"as is\" basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer.\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"7.\";match\u003d\".{0,20}\"\u003e\u003e Limitation of Liability\n\n \u003c\u003cbeginOptional\u003e\u003e* -------------------------- *\n\n \u003c\u003cendOptional\u003e\u003e\n\n Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party\u0027s negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.\";match\u003d\".{0,20}\"\u003e\u003e Litigation\n\n \u003c\u003cbeginOptional\u003e\u003e-------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party\u0027s ability to bring cross-claims or counter-claims.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"9.\";match\u003d\".{0,20}\"\u003e\u003e Miscellaneous\n\n \u003c\u003cbeginOptional\u003e\u003e----------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.\";match\u003d\".{0,20}\"\u003e\u003e Versions of the License\n\n \u003c\u003cbeginOptional\u003e\u003e---------------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.1.\";match\u003d\".{0,20}\"\u003e\u003e New Versions\n\n Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.2.\";match\u003d\".{0,20}\"\u003e\u003e Effect of New Versions\n\n You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.3.\";match\u003d\".{0,20}\"\u003e\u003e Modified Versions\n\n If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.4.\";match\u003d\".{0,20}\"\u003e\u003e Distributing Source Code Form that is Incompatible With Secondary Licenses\n\n If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached.\n\n \u003c\u003cbeginOptional\u003e\u003eExhibit A - Source Code Form License Notice\n\n\u003c\u003cbeginOptional\u003e\u003e-------------------------------------------\n\n\u003c\u003cendOptional\u003e\u003e\n\nThis Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf 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.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n\n\u003c\u003cbeginOptional\u003e\u003e---------------------------------------------------------\n\n\u003c\u003cendOptional\u003e\u003e\n\nThis Source Code Form is \"Incompatible With Secondary Licenses\", as defined by the Mozilla Public License, v. 2.0.\n\n\u003c\u003cendOptional\u003e\u003e", + "standardLicenseHeaderTemplate": "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\n\nThis Source Code Form is \"Incompatible With Secondary Licenses\", as defined by the Mozilla Public License, v. 2.0.\n\n", + "standardLicenseTemplate": "\u003c\u003cbeginOptional\u003e\u003eMozilla Public License Version 2.0\n\n\u003c\u003cbeginOptional\u003e\u003e\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\n\u003c\u003cendOptional\u003e\u003e\u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.\";match\u003d\".{0,20}\"\u003e\u003e Definitions\n\n \u003c\u003cbeginOptional\u003e\u003e--------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.1.\";match\u003d\".{0,20}\"\u003e\u003e \"Contributor\" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.2.\";match\u003d\".{0,20}\"\u003e\u003e \"Contributor Version\" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor\u0027s Contribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.3.\";match\u003d\".{0,20}\"\u003e\u003e \"Contribution\" means Covered Software of a particular Contributor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.4.\";match\u003d\".{0,20}\"\u003e\u003e \"Covered Software\" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.5.\";match\u003d\".{0,20}\"\u003e\u003e \"Incompatible With Secondary Licenses\" means\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.6.\";match\u003d\".{0,20}\"\u003e\u003e \"Executable Form\" means any form of the work other than Source Code Form.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.7.\";match\u003d\".{0,20}\"\u003e\u003e \"Larger Work\" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.8.\";match\u003d\".{0,20}\"\u003e\u003e \"License\" means this document.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.9.\";match\u003d\".{0,20}\"\u003e\u003e \"Licensable\" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.10.\";match\u003d\".{0,20}\"\u003e\u003e \"Modifications\" means any of the following:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e any new file in Source Code Form that contains any Covered Software.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.11.\";match\u003d\".{0,20}\"\u003e\u003e \"Patent Claims\" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.12.\";match\u003d\".{0,20}\"\u003e\u003e \"Secondary License\" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.13.\";match\u003d\".{0,20}\"\u003e\u003e \"Source Code Form\" means the form of the work preferred for making modifications.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.14.\";match\u003d\".{0,20}\"\u003e\u003e \"You\" (or \"Your\") means an individual or a legal entity exercising rights under this License. For legal entities, \"You\" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, \"control\" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.\";match\u003d\".{0,20}\"\u003e\u003e License Grants and Conditions\n\n \u003c\u003cbeginOptional\u003e\u003e--------------------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.1.\";match\u003d\".{0,20}\"\u003e\u003e Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.2.\";match\u003d\".{0,20}\"\u003e\u003e Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.3.\";match\u003d\".{0,20}\"\u003e\u003e Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e for any code that a Contributor has removed from Covered Software; or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e for infringements caused by: (i) Your and any other third party\u0027s modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(c)\";match\u003d\".{0,20}\"\u003e\u003e under Patent Claims infringed by Covered Software in the absence of its Contributions.\n\n This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.4.\";match\u003d\".{0,20}\"\u003e\u003e Subsequent Licenses\n\n No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.5.\";match\u003d\".{0,20}\"\u003e\u003e Representation\n\n Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.6.\";match\u003d\".{0,20}\"\u003e\u003e Fair Use\n\n This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.7.\";match\u003d\".{0,20}\"\u003e\u003e Conditions\n\n Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.\";match\u003d\".{0,20}\"\u003e\u003e Responsibilities\n\n \u003c\u003cbeginOptional\u003e\u003e-------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.1.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of Source Form\n\n All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients\u0027 rights in the Source Code Form.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.2.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of Executable Form\n\n If You distribute Covered Software in Executable Form then:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients\u0027 rights in the Source Code Form under this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.3.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of a Larger Work\n\n You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.4.\";match\u003d\".{0,20}\"\u003e\u003e Notices\n\n You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.5.\";match\u003d\".{0,20}\"\u003e\u003e Application of Additional Terms\n\n You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"4.\";match\u003d\".{0,20}\"\u003e\u003e Inability to Comply Due to Statute or Regulation\n\n \u003c\u003cbeginOptional\u003e\u003e---------------------------------------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.\";match\u003d\".{0,20}\"\u003e\u003e Termination\n\n \u003c\u003cbeginOptional\u003e\u003e--------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.1.\";match\u003d\".{0,20}\"\u003e\u003e The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.2.\";match\u003d\".{0,20}\"\u003e\u003e If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.3.\";match\u003d\".{0,20}\"\u003e\u003e In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination.\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"6.\";match\u003d\".{0,20}\"\u003e\u003e Disclaimer of Warranty\n\n \u003c\u003cbeginOptional\u003e\u003e* ------------------------- *\n\n \u003c\u003cendOptional\u003e\u003e\n\n Covered Software is provided under this License on an \"as is\" basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer.\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"7.\";match\u003d\".{0,20}\"\u003e\u003e Limitation of Liability\n\n \u003c\u003cbeginOptional\u003e\u003e* -------------------------- *\n\n \u003c\u003cendOptional\u003e\u003e\n\n Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party\u0027s negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.\";match\u003d\".{0,20}\"\u003e\u003e Litigation\n\n \u003c\u003cbeginOptional\u003e\u003e-------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party\u0027s ability to bring cross-claims or counter-claims.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"9.\";match\u003d\".{0,20}\"\u003e\u003e Miscellaneous\n\n \u003c\u003cbeginOptional\u003e\u003e----------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.\";match\u003d\".{0,20}\"\u003e\u003e Versions of the License\n\n \u003c\u003cbeginOptional\u003e\u003e---------------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.1.\";match\u003d\".{0,20}\"\u003e\u003e New Versions\n\n Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.2.\";match\u003d\".{0,20}\"\u003e\u003e Effect of New Versions\n\n You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.3.\";match\u003d\".{0,20}\"\u003e\u003e Modified Versions\n\n If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.4.\";match\u003d\".{0,20}\"\u003e\u003e Distributing Source Code Form that is Incompatible With Secondary Licenses\n\n If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached.\n\n \u003c\u003cbeginOptional\u003e\u003eExhibit A - Source Code Form License Notice\n\n\u003c\u003cbeginOptional\u003e\u003e-------------------------------------------\n\n\u003c\u003cendOptional\u003e\u003e\n\nThis Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\nIf 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.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n\n\u003c\u003cbeginOptional\u003e\u003e---------------------------------------------------------\n\n\u003c\u003cendOptional\u003e\u003e\n\nThis Source Code Form is \"Incompatible With Secondary Licenses\", as defined by the Mozilla Public License, v. 2.0.\n\n\u003c\u003cendOptional\u003e\u003e", "name": "Mozilla Public License 2.0 (no copyleft exception)", "licenseComments": "This license was released in January 2012. This license list entry is for use when the MPL\u0027s Exhibit B is used, which effectively negates the copyleft compatibility clause in section 3.3.", "licenseId": "MPL-2.0-no-copyleft-exception", - "standardLicenseHeader": "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n\nThis Source Code Form is \"Incompatible With Secondary Licenses\", as defined by the Mozilla Public License, v. 2.0.\n\n", + "standardLicenseHeader": "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\n\nThis Source Code Form is \"Incompatible With Secondary Licenses\", as defined by the Mozilla Public License, v. 2.0.\n\n", "crossRef": [ { "match": "N/A", "url": "https://opensource.org/licenses/MPL-2.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:26:06Z", + "timestamp": "2024-08-19T17:45:28Z", "isWayBackLink": false, "order": 1 }, @@ -22,7 +22,7 @@ "url": "https://www.mozilla.org/MPL/2.0/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:06Z", + "timestamp": "2024-08-19T17:45:28Z", "isWayBackLink": false, "order": 0 } @@ -32,6 +32,6 @@ "https://opensource.org/licenses/MPL-2.0" ], "isOsiApproved": true, - "licenseTextHtml": "\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eMozilla Public License Version 2.0\u003c/p\u003e\n\n\t \u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003c/p\u003e\n\u003c/div\u003e\n \u003c/div\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \t\t\u003cp\u003e\n\t\t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 1.\u003c/var\u003e\n\t\t\tDefinitions\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e--------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.1.\u003c/var\u003e\n \u0026quot;Contributor\u0026quot; means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.2.\u003c/var\u003e\n \u0026quot;Contributor Version\u0026quot; means the combination of the Contributions of others (if any)\n used by a Contributor and that particular Contributor\u0026apos;s Contribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.3.\u003c/var\u003e\n \u0026quot;Contribution\u0026quot; means Covered Software of a particular Contributor.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.4.\u003c/var\u003e\n \u0026quot;Covered Software\u0026quot; means Source Code Form to which the initial Contributor has\n attached the notice in Exhibit A, the Executable Form of such Source Code Form, and\n Modifications of such Source Code Form, in each case including portions thereof.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.5.\u003c/var\u003e\n \u0026quot;Incompatible With Secondary Licenses\u0026quot; means\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n that the initial Contributor has attached the notice described in Exhibit B to the\n Covered Software; or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n that the Covered Software was made available under the terms of version 1.1 or earlier of\n the License, but not also under the terms of a Secondary License.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.6.\u003c/var\u003e\n \u0026quot;Executable Form\u0026quot; means any form of the work other than Source Code Form.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.7.\u003c/var\u003e\n \u0026quot;Larger Work\u0026quot; means a work that combines Covered Software with other material, in a\n separate file or files, that is not Covered Software.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.8.\u003c/var\u003e\n \u0026quot;License\u0026quot; means this document.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.9.\u003c/var\u003e\n \u0026quot;Licensable\u0026quot; means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and all of the rights\n conveyed by this License.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.10.\u003c/var\u003e\n \u0026quot;Modifications\u0026quot; means any of the following:\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n any file in Source Code Form that results from an addition to, deletion from, or\n modification of the contents of Covered Software; or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n any new file in Source Code Form that contains any Covered Software.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.11.\u003c/var\u003e\n \u0026quot;Patent Claims\u0026quot; of a Contributor means any patent claim(s), including without\n limitation, method, process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the License, by the making,\n using, selling, offering for sale, having made, import, or transfer of either its\n Contributions or its Contributor Version.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.12.\u003c/var\u003e\n \u0026quot;Secondary License\u0026quot; means either the GNU General Public License, Version 2.0, the\n GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License,\n Version 3.0, or any later versions of those licenses.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.13.\u003c/var\u003e\n \u0026quot;Source Code Form\u0026quot; means the form of the work preferred for making modifications.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.14.\u003c/var\u003e\n \u0026quot;You\u0026quot; (or \u0026quot;Your\u0026quot;) means an individual or a legal entity exercising rights\n under this License. For legal entities, \u0026quot;You\u0026quot; includes any entity that controls,\n is controlled by, or is under common control with You. For purposes of this definition,\n \u0026quot;control\u0026quot; means (a) the power, direct or indirect, to cause the direction or\n management of such entity, whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 2.\u003c/var\u003e\n\t\t\tLicense Grants and Conditions\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e--------------------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.1.\u003c/var\u003e\n Grants\n \u003cp\u003eEach Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:\n \u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n under intellectual property rights (other than patent or trademark) Licensable by such\n Contributor to use, reproduce, make available, modify, display, perform, distribute,\n and otherwise exploit its Contributions, either on an unmodified basis, with\n Modifications, or as part of a Larger Work; and\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n under Patent Claims of such Contributor to make, use, sell, offer for sale, have made,\n import, and otherwise transfer either its Contributions or its Contributor\n Version.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.2.\u003c/var\u003e\n Effective Date\n \u003cp\u003eThe licenses granted in Section 2.1 with respect to any Contribution become effective\n for each Contribution on the date the Contributor first distributes such\n Contribution.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.3.\u003c/var\u003e\n Limitations on Grant Scope\n \u003cp\u003eThe licenses granted in this Section 2 are the only rights granted under this License.\n No additional rights or licenses will be implied from the distribution or\n licensing of Covered Software under this License. Notwithstanding Section 2.1(b)\n above, no patent license is granted by a Contributor:\n \u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n for any code that a Contributor has removed from Covered Software; or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n for infringements caused by: (i) Your and any other third party\u0026apos;s modifications of\n Covered Software, or (ii) the combination of its Contributions with other software\n (except as part of its Contributor Version); or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (c)\u003c/var\u003e\n under Patent Claims infringed by Covered Software in the absence of its Contributions.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003eThis License does not grant any rights in the trademarks, service marks, or logos of any\n Contributor (except as may be necessary to comply with the notice requirements in\n Section 3.4).\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.4.\u003c/var\u003e\n Subsequent Licenses\n \u003cp\u003eNo Contributor makes additional grants as a result of Your choice to distribute the\n Covered Software under a subsequent version of this License (see Section 10.2) or\n under the terms of a Secondary License (if permitted under the terms of Section\n 3.3).\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.5.\u003c/var\u003e\n Representation\n \u003cp\u003eEach Contributor represents that the Contributor believes its Contributions are its\n original creation(s) or it has sufficient rights to grant the rights to its\n Contributions conveyed by this License.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.6.\u003c/var\u003e\n Fair Use\n \u003cp\u003eThis License is not intended to limit any rights You have under applicable copyright\n doctrines of fair use, fair dealing, or other equivalents.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.7.\u003c/var\u003e\n Conditions\n \u003cp\u003eSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1.\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 3.\u003c/var\u003e\n\t\t Responsibilities\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e-------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.1.\u003c/var\u003e\n Distribution of Source Form\n \u003cp\u003eAll distribution of Covered Software in Source Code Form, including any Modifications\n that You create or to which You contribute, must be under the terms of this\n License. You must inform recipients that the Source Code Form of the Covered\n Software is governed by the terms of this License, and how they can obtain a copy\n of this License. You may not attempt to alter or restrict the recipients\u0026apos;\n rights in the Source Code Form.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.2.\u003c/var\u003e\n Distribution of Executable Form\n \u003cp\u003eIf You distribute Covered Software in Executable Form then:\u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n such Covered Software must also be made available in Source Code Form, as described in\n Section 3.1, and You must inform recipients of the Executable Form how they can obtain\n a copy of such Source Code Form by reasonable means in a timely manner, at a charge no\n more than the cost of distribution to the recipient; and\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n You may distribute such Executable Form under the terms of this License, or sublicense it\n under different terms, provided that the license for the Executable Form does not\n attempt to limit or alter the recipients\u0026apos; rights in the Source Code Form under\n this License.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.3.\u003c/var\u003e\n Distribution of a Larger Work\n \u003cp\u003eYou may create and distribute a Larger Work under terms of Your choice, provided that\n You also comply with the requirements of this License for the Covered Software. If\n the Larger Work is a combination of Covered Software with a work governed by one\n or more Secondary Licenses, and the Covered Software is not Incompatible With\n Secondary Licenses, this License permits You to additionally distribute such\n Covered Software under the terms of such Secondary License(s), so that the\n recipient of the Larger Work may, at their option, further distribute the Covered\n Software under the terms of either this License or such Secondary License(s).\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.4.\u003c/var\u003e\n Notices\n \u003cp\u003eYou may not remove or alter the substance of any license notices (including copyright\n notices, patent notices, disclaimers of warranty, or limitations of liability)\n contained within the Source Code Form of the Covered Software, except that You may\n alter any license notices to the extent required to remedy known factual\n inaccuracies.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.5.\u003c/var\u003e\n Application of Additional Terms\n \u003cp\u003eYou may choose to offer, and to charge a fee for, warranty, support, indemnity or\n liability obligations to one or more recipients of Covered Software. However, You\n may do so only on Your own behalf, and not on behalf of any Contributor. You must\n make it absolutely clear that any such warranty, support, indemnity, or liability\n obligation is offered by You alone, and You hereby agree to indemnify every\n Contributor for any liability incurred by such Contributor as a result of\n warranty, support, indemnity or liability terms You offer. You may include\n additional disclaimers of warranty and limitations of liability specific to any\n jurisdiction.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 4.\u003c/var\u003e\n \t\tInability to Comply Due to Statute or Regulation\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e---------------------------------------------------\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eIf it is impossible for You to comply with any of the terms of this License with respect to\n some or all of the Covered Software due to statute, judicial order, or regulation then\n You must: (a) comply with the terms of this License to the maximum extent possible;\n and (b) describe the limitations and the code they affect. Such description must be\n placed in a text file included with all distributions of the Covered Software under\n this License. Except to the extent prohibited by statute or regulation, such\n description must be sufficiently detailed for a recipient of ordinary skill to be able\n to understand it.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n\t\t\u003cp\u003e\n\t\t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 5.\u003c/var\u003e\n\t\t Termination\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e--------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.1.\u003c/var\u003e\n The rights granted under this License will terminate automatically if You fail to comply with\n any of its terms. However, if You become compliant, then the rights granted under this\n License from a particular Contributor are reinstated (a) provisionally, unless and until\n such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing\n basis, if such Contributor fails to notify You of the non-compliance by some reasonable\n means prior to 60 days after You have come back into compliance. Moreover, Your grants\n from a particular Contributor are reinstated on an ongoing basis if such Contributor\n notifies You of the non-compliance by some reasonable means, this is the first time You\n have received notice of non-compliance with this License from such Contributor, and You\n become compliant prior to 30 days after Your receipt of the notice.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.2.\u003c/var\u003e\n If You initiate litigation against any entity by asserting a patent infringement claim\n (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a\n Contributor Version directly or indirectly infringes any patent, then the rights granted\n to You by any and all Contributors for the Covered Software under Section 2.1 of this\n License shall terminate.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.3.\u003c/var\u003e\n In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements\n (excluding distributors and resellers) which have been validly granted by You or Your\n distributors under this License prior to termination shall survive termination.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n\t\t\u003cp\u003e\n\t \t\u003cvar class\u003d\"replaceable-license-text\"\u003e 6.\u003c/var\u003e\n\t \tDisclaimer of Warranty\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e* ------------------------- *\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eCovered Software is provided under this License on an \u0026quot;as is\u0026quot; basis, without\n warranty of any kind, either expressed, implied, or statutory, including, without\n limitation, warranties that the Covered Software is free of defects, merchantable, fit\n for a particular purpose or non-infringing. The entire risk as to the quality and\n performance of the Covered Software is with You. Should any Covered Software prove\n defective in any respect, You (not any Contributor) assume the cost of any necessary\n servicing, repair, or correction. This disclaimer of warranty constitutes an essential\n part of this License. No use of any Covered Software is authorized under this License\n except under this disclaimer.\n \u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 7.\u003c/var\u003e\n \t\tLimitation of Liability\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e* -------------------------- *\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eUnder no circumstances and under no legal theory, whether tort (including negligence),\n contract, or otherwise, shall any Contributor, or anyone who distributes Covered\n Software as permitted above, be liable to You for any direct, indirect, special,\n incidental, or consequential damages of any character including, without limitation,\n damages for lost profits, loss of goodwill, work stoppage, computer failure or\n malfunction, or any and all other commercial damages or losses, even if such party\n shall have been informed of the possibility of such damages. This limitation of\n liability shall not apply to liability for death or personal injury resulting from\n such party\u0026apos;s negligence to the extent applicable law prohibits such limitation.\n Some jurisdictions do not allow the exclusion or limitation of incidental or\n consequential damages, so this exclusion and limitation may not apply to You.\n \u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 8.\u003c/var\u003e\n \t\tLitigation\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e-------------\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eAny litigation relating to this License may be brought only in the courts of a jurisdiction\n where the defendant maintains its principal place of business and such litigation\n shall be governed by laws of that jurisdiction, without reference to its\n conflict-of-law provisions. Nothing in this Section shall prevent a party\u0026apos;s\n ability to bring cross-claims or counter-claims.\n \u003c/p\u003e\n\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 9.\u003c/var\u003e\n \t\tMiscellaneous\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e----------------\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eThis License represents the complete agreement concerning the subject matter hereof. If any\n provision of this License is held to be unenforceable, such provision shall be\n reformed only to the extent necessary to make it enforceable. Any law or regulation\n which provides that the language of a contract shall be construed against the drafter\n shall not be used to construe this License against a Contributor.\n \u003c/p\u003e\n\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 10.\u003c/var\u003e\n \t\tVersions of the License\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e---------------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.1.\u003c/var\u003e\n New Versions\n \u003cp\u003eMozilla Foundation is the license steward. Except as provided in Section 10.3, no one\n other than the license steward has the right to modify or publish new versions of\n this License. Each version will be given a distinguishing version number.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.2.\u003c/var\u003e\n Effect of New Versions\n \u003cp\u003eYou may distribute the Covered Software under the terms of the version of the License\n under which You originally received the Covered Software, or under the terms of\n any subsequent version published by the license steward.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.3.\u003c/var\u003e\n Modified Versions\n \u003cp\u003eIf you create software not governed by this License, and you want to create a new\n license for such software, you may create and use a modified version of this\n License if you rename the license and remove any references to the name of the\n license steward (except to note that such modified license differs from this\n License).\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.4.\u003c/var\u003e\n Distributing Source Code Form that is Incompatible With Secondary Licenses\n \u003cp\u003eIf You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms\n of this version of the License, the notice described in Exhibit B of this License must be\n attached.\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003c/ul\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eExhibit A - Source Code Form License Notice\u003c/p\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e-------------------------------------------\u003c/p\u003e\n\u003c/div\u003e\n\t \n \u003cp\u003eThis Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL\n was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.\u003c/p\u003e\n\n \u003cp\u003eIf it is not possible or desirable to put the notice in a particular file, then You may include the\n notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be\n likely to look for such a notice.\u003c/p\u003e\n\n \u003cp\u003eYou may add additional accurate notices of copyright ownership.\u003c/p\u003e\n\n \u003cp\u003eExhibit B - \u0026quot;Incompatible With Secondary Licenses\u0026quot; Notice\u003c/p\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e---------------------------------------------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\t \u003cp\u003eThis Source Code Form is \u0026quot;Incompatible With Secondary Licenses\u0026quot;, as defined by the Mozilla\n Public License, v. 2.0.\u003c/p\u003e\n\n \u003c/div\u003e\n ", - "standardLicenseHeaderHtml": "\n \u003cp\u003eThis Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL\n was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.\u003c/p\u003e\n\n \u003cbr /\u003e\n\n\t \u003cp\u003eThis Source Code Form is \u0026quot;Incompatible With Secondary Licenses\u0026quot;, as defined by the Mozilla\n Public License, v. 2.0.\u003c/p\u003e\n\n " + "licenseTextHtml": "\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eMozilla Public License Version 2.0\u003c/p\u003e\n\n\t \u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003c/p\u003e\n\u003c/div\u003e\n \u003c/div\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \t\t\u003cp\u003e\n\t\t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 1.\u003c/var\u003e\n\t\t\tDefinitions\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e--------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.1.\u003c/var\u003e\n \u0026quot;Contributor\u0026quot; means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.2.\u003c/var\u003e\n \u0026quot;Contributor Version\u0026quot; means the combination of the Contributions of others (if any)\n used by a Contributor and that particular Contributor\u0026apos;s Contribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.3.\u003c/var\u003e\n \u0026quot;Contribution\u0026quot; means Covered Software of a particular Contributor.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.4.\u003c/var\u003e\n \u0026quot;Covered Software\u0026quot; means Source Code Form to which the initial Contributor has\n attached the notice in Exhibit A, the Executable Form of such Source Code Form, and\n Modifications of such Source Code Form, in each case including portions thereof.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.5.\u003c/var\u003e\n \u0026quot;Incompatible With Secondary Licenses\u0026quot; means\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n that the initial Contributor has attached the notice described in Exhibit B to the\n Covered Software; or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n that the Covered Software was made available under the terms of version 1.1 or earlier of\n the License, but not also under the terms of a Secondary License.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.6.\u003c/var\u003e\n \u0026quot;Executable Form\u0026quot; means any form of the work other than Source Code Form.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.7.\u003c/var\u003e\n \u0026quot;Larger Work\u0026quot; means a work that combines Covered Software with other material, in a\n separate file or files, that is not Covered Software.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.8.\u003c/var\u003e\n \u0026quot;License\u0026quot; means this document.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.9.\u003c/var\u003e\n \u0026quot;Licensable\u0026quot; means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and all of the rights\n conveyed by this License.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.10.\u003c/var\u003e\n \u0026quot;Modifications\u0026quot; means any of the following:\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n any file in Source Code Form that results from an addition to, deletion from, or\n modification of the contents of Covered Software; or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n any new file in Source Code Form that contains any Covered Software.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.11.\u003c/var\u003e\n \u0026quot;Patent Claims\u0026quot; of a Contributor means any patent claim(s), including without\n limitation, method, process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the License, by the making,\n using, selling, offering for sale, having made, import, or transfer of either its\n Contributions or its Contributor Version.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.12.\u003c/var\u003e\n \u0026quot;Secondary License\u0026quot; means either the GNU General Public License, Version 2.0, the\n GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License,\n Version 3.0, or any later versions of those licenses.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.13.\u003c/var\u003e\n \u0026quot;Source Code Form\u0026quot; means the form of the work preferred for making modifications.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.14.\u003c/var\u003e\n \u0026quot;You\u0026quot; (or \u0026quot;Your\u0026quot;) means an individual or a legal entity exercising rights\n under this License. For legal entities, \u0026quot;You\u0026quot; includes any entity that controls,\n is controlled by, or is under common control with You. For purposes of this definition,\n \u0026quot;control\u0026quot; means (a) the power, direct or indirect, to cause the direction or\n management of such entity, whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 2.\u003c/var\u003e\n\t\t\tLicense Grants and Conditions\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e--------------------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.1.\u003c/var\u003e\n Grants\n \u003cp\u003eEach Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:\n \u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n under intellectual property rights (other than patent or trademark) Licensable by such\n Contributor to use, reproduce, make available, modify, display, perform, distribute,\n and otherwise exploit its Contributions, either on an unmodified basis, with\n Modifications, or as part of a Larger Work; and\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n under Patent Claims of such Contributor to make, use, sell, offer for sale, have made,\n import, and otherwise transfer either its Contributions or its Contributor\n Version.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.2.\u003c/var\u003e\n Effective Date\n \u003cp\u003eThe licenses granted in Section 2.1 with respect to any Contribution become effective\n for each Contribution on the date the Contributor first distributes such\n Contribution.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.3.\u003c/var\u003e\n Limitations on Grant Scope\n \u003cp\u003eThe licenses granted in this Section 2 are the only rights granted under this License.\n No additional rights or licenses will be implied from the distribution or\n licensing of Covered Software under this License. Notwithstanding Section 2.1(b)\n above, no patent license is granted by a Contributor:\n \u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n for any code that a Contributor has removed from Covered Software; or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n for infringements caused by: (i) Your and any other third party\u0026apos;s modifications of\n Covered Software, or (ii) the combination of its Contributions with other software\n (except as part of its Contributor Version); or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (c)\u003c/var\u003e\n under Patent Claims infringed by Covered Software in the absence of its Contributions.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003eThis License does not grant any rights in the trademarks, service marks, or logos of any\n Contributor (except as may be necessary to comply with the notice requirements in\n Section 3.4).\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.4.\u003c/var\u003e\n Subsequent Licenses\n \u003cp\u003eNo Contributor makes additional grants as a result of Your choice to distribute the\n Covered Software under a subsequent version of this License (see Section 10.2) or\n under the terms of a Secondary License (if permitted under the terms of Section\n 3.3).\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.5.\u003c/var\u003e\n Representation\n \u003cp\u003eEach Contributor represents that the Contributor believes its Contributions are its\n original creation(s) or it has sufficient rights to grant the rights to its\n Contributions conveyed by this License.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.6.\u003c/var\u003e\n Fair Use\n \u003cp\u003eThis License is not intended to limit any rights You have under applicable copyright\n doctrines of fair use, fair dealing, or other equivalents.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.7.\u003c/var\u003e\n Conditions\n \u003cp\u003eSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1.\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 3.\u003c/var\u003e\n\t\t Responsibilities\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e-------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.1.\u003c/var\u003e\n Distribution of Source Form\n \u003cp\u003eAll distribution of Covered Software in Source Code Form, including any Modifications\n that You create or to which You contribute, must be under the terms of this\n License. You must inform recipients that the Source Code Form of the Covered\n Software is governed by the terms of this License, and how they can obtain a copy\n of this License. You may not attempt to alter or restrict the recipients\u0026apos;\n rights in the Source Code Form.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.2.\u003c/var\u003e\n Distribution of Executable Form\n \u003cp\u003eIf You distribute Covered Software in Executable Form then:\u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n such Covered Software must also be made available in Source Code Form, as described in\n Section 3.1, and You must inform recipients of the Executable Form how they can obtain\n a copy of such Source Code Form by reasonable means in a timely manner, at a charge no\n more than the cost of distribution to the recipient; and\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n You may distribute such Executable Form under the terms of this License, or sublicense it\n under different terms, provided that the license for the Executable Form does not\n attempt to limit or alter the recipients\u0026apos; rights in the Source Code Form under\n this License.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.3.\u003c/var\u003e\n Distribution of a Larger Work\n \u003cp\u003eYou may create and distribute a Larger Work under terms of Your choice, provided that\n You also comply with the requirements of this License for the Covered Software. If\n the Larger Work is a combination of Covered Software with a work governed by one\n or more Secondary Licenses, and the Covered Software is not Incompatible With\n Secondary Licenses, this License permits You to additionally distribute such\n Covered Software under the terms of such Secondary License(s), so that the\n recipient of the Larger Work may, at their option, further distribute the Covered\n Software under the terms of either this License or such Secondary License(s).\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.4.\u003c/var\u003e\n Notices\n \u003cp\u003eYou may not remove or alter the substance of any license notices (including copyright\n notices, patent notices, disclaimers of warranty, or limitations of liability)\n contained within the Source Code Form of the Covered Software, except that You may\n alter any license notices to the extent required to remedy known factual\n inaccuracies.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.5.\u003c/var\u003e\n Application of Additional Terms\n \u003cp\u003eYou may choose to offer, and to charge a fee for, warranty, support, indemnity or\n liability obligations to one or more recipients of Covered Software. However, You\n may do so only on Your own behalf, and not on behalf of any Contributor. You must\n make it absolutely clear that any such warranty, support, indemnity, or liability\n obligation is offered by You alone, and You hereby agree to indemnify every\n Contributor for any liability incurred by such Contributor as a result of\n warranty, support, indemnity or liability terms You offer. You may include\n additional disclaimers of warranty and limitations of liability specific to any\n jurisdiction.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 4.\u003c/var\u003e\n \t\tInability to Comply Due to Statute or Regulation\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e---------------------------------------------------\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eIf it is impossible for You to comply with any of the terms of this License with respect to\n some or all of the Covered Software due to statute, judicial order, or regulation then\n You must: (a) comply with the terms of this License to the maximum extent possible;\n and (b) describe the limitations and the code they affect. Such description must be\n placed in a text file included with all distributions of the Covered Software under\n this License. Except to the extent prohibited by statute or regulation, such\n description must be sufficiently detailed for a recipient of ordinary skill to be able\n to understand it.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n\t\t\u003cp\u003e\n\t\t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 5.\u003c/var\u003e\n\t\t Termination\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e--------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.1.\u003c/var\u003e\n The rights granted under this License will terminate automatically if You fail to comply with\n any of its terms. However, if You become compliant, then the rights granted under this\n License from a particular Contributor are reinstated (a) provisionally, unless and until\n such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing\n basis, if such Contributor fails to notify You of the non-compliance by some reasonable\n means prior to 60 days after You have come back into compliance. Moreover, Your grants\n from a particular Contributor are reinstated on an ongoing basis if such Contributor\n notifies You of the non-compliance by some reasonable means, this is the first time You\n have received notice of non-compliance with this License from such Contributor, and You\n become compliant prior to 30 days after Your receipt of the notice.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.2.\u003c/var\u003e\n If You initiate litigation against any entity by asserting a patent infringement claim\n (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a\n Contributor Version directly or indirectly infringes any patent, then the rights granted\n to You by any and all Contributors for the Covered Software under Section 2.1 of this\n License shall terminate.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.3.\u003c/var\u003e\n In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements\n (excluding distributors and resellers) which have been validly granted by You or Your\n distributors under this License prior to termination shall survive termination.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n\t\t\u003cp\u003e\n\t \t\u003cvar class\u003d\"replaceable-license-text\"\u003e 6.\u003c/var\u003e\n\t \tDisclaimer of Warranty\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e* ------------------------- *\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eCovered Software is provided under this License on an \u0026quot;as is\u0026quot; basis, without\n warranty of any kind, either expressed, implied, or statutory, including, without\n limitation, warranties that the Covered Software is free of defects, merchantable, fit\n for a particular purpose or non-infringing. The entire risk as to the quality and\n performance of the Covered Software is with You. Should any Covered Software prove\n defective in any respect, You (not any Contributor) assume the cost of any necessary\n servicing, repair, or correction. This disclaimer of warranty constitutes an essential\n part of this License. No use of any Covered Software is authorized under this License\n except under this disclaimer.\n \u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 7.\u003c/var\u003e\n \t\tLimitation of Liability\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e* -------------------------- *\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eUnder no circumstances and under no legal theory, whether tort (including negligence),\n contract, or otherwise, shall any Contributor, or anyone who distributes Covered\n Software as permitted above, be liable to You for any direct, indirect, special,\n incidental, or consequential damages of any character including, without limitation,\n damages for lost profits, loss of goodwill, work stoppage, computer failure or\n malfunction, or any and all other commercial damages or losses, even if such party\n shall have been informed of the possibility of such damages. This limitation of\n liability shall not apply to liability for death or personal injury resulting from\n such party\u0026apos;s negligence to the extent applicable law prohibits such limitation.\n Some jurisdictions do not allow the exclusion or limitation of incidental or\n consequential damages, so this exclusion and limitation may not apply to You.\n \u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 8.\u003c/var\u003e\n \t\tLitigation\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e-------------\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eAny litigation relating to this License may be brought only in the courts of a jurisdiction\n where the defendant maintains its principal place of business and such litigation\n shall be governed by laws of that jurisdiction, without reference to its\n conflict-of-law provisions. Nothing in this Section shall prevent a party\u0026apos;s\n ability to bring cross-claims or counter-claims.\n \u003c/p\u003e\n\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 9.\u003c/var\u003e\n \t\tMiscellaneous\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e----------------\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eThis License represents the complete agreement concerning the subject matter hereof. If any\n provision of this License is held to be unenforceable, such provision shall be\n reformed only to the extent necessary to make it enforceable. Any law or regulation\n which provides that the language of a contract shall be construed against the drafter\n shall not be used to construe this License against a Contributor.\n \u003c/p\u003e\n\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 10.\u003c/var\u003e\n \t\tVersions of the License\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e---------------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.1.\u003c/var\u003e\n New Versions\n \u003cp\u003eMozilla Foundation is the license steward. Except as provided in Section 10.3, no one\n other than the license steward has the right to modify or publish new versions of\n this License. Each version will be given a distinguishing version number.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.2.\u003c/var\u003e\n Effect of New Versions\n \u003cp\u003eYou may distribute the Covered Software under the terms of the version of the License\n under which You originally received the Covered Software, or under the terms of\n any subsequent version published by the license steward.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.3.\u003c/var\u003e\n Modified Versions\n \u003cp\u003eIf you create software not governed by this License, and you want to create a new\n license for such software, you may create and use a modified version of this\n License if you rename the license and remove any references to the name of the\n license steward (except to note that such modified license differs from this\n License).\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.4.\u003c/var\u003e\n Distributing Source Code Form that is Incompatible With Secondary Licenses\n \u003cp\u003eIf You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms\n of this version of the License, the notice described in Exhibit B of this License must be\n attached.\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003c/ul\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eExhibit A - Source Code Form License Notice\u003c/p\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e-------------------------------------------\u003c/p\u003e\n\u003c/div\u003e\n\t \n \u003cp\u003eThis Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL\n was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.\u003c/p\u003e\n\n \u003cp\u003eIf it is not possible or desirable to put the notice in a particular file, then You may include the\n notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be\n likely to look for such a notice.\u003c/p\u003e\n\n \u003cp\u003eYou may add additional accurate notices of copyright ownership.\u003c/p\u003e\n\n \u003cp\u003eExhibit B - \u0026quot;Incompatible With Secondary Licenses\u0026quot; Notice\u003c/p\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e---------------------------------------------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\t \u003cp\u003eThis Source Code Form is \u0026quot;Incompatible With Secondary Licenses\u0026quot;, as defined by the Mozilla\n Public License, v. 2.0.\u003c/p\u003e\n\n \u003c/div\u003e\n ", + "standardLicenseHeaderHtml": "\n \u003cp\u003eThis Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL\n was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.\u003c/p\u003e\n\n \u003cbr /\u003e\n\n\t \u003cp\u003eThis Source Code Form is \u0026quot;Incompatible With Secondary Licenses\u0026quot;, as defined by the Mozilla\n Public License, v. 2.0.\u003c/p\u003e\n\n " } \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/MPL-2.0.json b/src/main/resources/license-list-data/json/details/MPL-2.0.json index 5bc1aa82ad..6a3eac8c2d 100644 --- a/src/main/resources/license-list-data/json/details/MPL-2.0.json +++ b/src/main/resources/license-list-data/json/details/MPL-2.0.json @@ -2,30 +2,30 @@ "isDeprecatedLicenseId": false, "isFsfLibre": true, "licenseText": "Mozilla Public License Version 2.0\n\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n means the combination of the Contributions of others (if any) used\n by a Contributor and that particular Contributor\u0027s Contribution.\n\n1.3. \"Contribution\"\n means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n means Source Code Form to which the initial Contributor has attached\n the notice in Exhibit A, the Executable Form of such Source Code\n Form, and Modifications of such Source Code Form, in each case\n including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n means\n\n (a) that the initial Contributor has attached the notice described\n in Exhibit B to the Covered Software; or\n\n (b) that the Covered Software was made available under the terms of\n version 1.1 or earlier of the License, but not also under the\n terms of a Secondary License.\n\n1.6. \"Executable Form\"\n means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n means a work that combines Covered Software with other material, in \n a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n means this document.\n\n1.9. \"Licensable\"\n means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and\n all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n means any of the following:\n\n (a) any file in Source Code Form that results from an addition to,\n deletion from, or modification of the contents of Covered\n Software; or\n\n (b) any new file in Source Code Form that contains any Covered\n Software.\n\n1.11. \"Patent Claims\" of a Contributor\n means any patent claim(s), including without limitation, method,\n process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the\n License, by the making, using, selling, offering for sale, having\n made, import, or transfer of either its Contributions or its\n Contributor Version.\n\n1.12. \"Secondary License\"\n means either the GNU General Public License, Version 2.0, the GNU\n Lesser General Public License, Version 2.1, the GNU Affero General\n Public License, Version 3.0, or any later versions of those\n licenses.\n\n1.13. \"Source Code Form\"\n means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n means an individual or a legal entity exercising rights under this\n License. For legal entities, \"You\" includes any entity that\n controls, is controlled by, or is under common control with You. For\n purposes of this definition, \"control\" means (a) the power, direct\n or indirect, to cause the direction or management of such entity,\n whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial\n ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or\n as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n for sale, have made, import, and otherwise transfer either its\n Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n or\n\n(b) for infringements caused by: (i) Your and any other third party\u0027s\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients\u0027 rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n Form, as described in Section 3.1, and You must inform recipients of\n the Executable Form how they can obtain a copy of such Source Code\n Form by reasonable means in a timely manner, at a charge no more\n than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n License, or sublicense it under different terms, provided that the\n license for the Executable Form does not attempt to limit or alter\n the recipients\u0027 rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n* *\n* 6. Disclaimer of Warranty *\n* ------------------------- *\n* *\n* Covered Software is provided under this License on an \"as is\" *\n* basis, without warranty of any kind, either expressed, implied, or *\n* statutory, including, without limitation, warranties that the *\n* Covered Software is free of defects, merchantable, fit for a *\n* particular purpose or non-infringing. The entire risk as to the *\n* quality and performance of the Covered Software is with You. *\n* Should any Covered Software prove defective in any respect, You *\n* (not any Contributor) assume the cost of any necessary servicing, *\n* repair, or correction. This disclaimer of warranty constitutes an *\n* essential part of this License. No use of any Covered Software is *\n* authorized under this License except under this disclaimer. *\n* *\n************************************************************************\n\n************************************************************************\n* *\n* 7. Limitation of Liability *\n* -------------------------- *\n* *\n* Under no circumstances and under no legal theory, whether tort *\n* (including negligence), contract, or otherwise, shall any *\n* Contributor, or anyone who distributes Covered Software as *\n* permitted above, be liable to You for any direct, indirect, *\n* special, incidental, or consequential damages of any character *\n* including, without limitation, damages for lost profits, loss of *\n* goodwill, work stoppage, computer failure or malfunction, or any *\n* and all other commercial damages or losses, even if such party *\n* shall have been informed of the possibility of such damages. This *\n* limitation of liability shall not apply to liability for death or *\n* personal injury resulting from such party\u0027s negligence to the *\n* extent applicable law prohibits such limitation. Some *\n* jurisdictions do not allow the exclusion or limitation of *\n* incidental or consequential damages, so this exclusion and *\n* limitation may not apply to You. *\n* *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party\u0027s ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n This Source Code Form is subject to the terms of the Mozilla Public\n License, v. 2.0. If a copy of the MPL was not distributed with this\n file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n This Source Code Form is \"Incompatible With Secondary Licenses\", as\n defined by the Mozilla Public License, v. 2.0.\n", - "standardLicenseHeaderTemplate": "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n", - "standardLicenseTemplate": "\u003c\u003cbeginOptional\u003e\u003eMozilla Public License Version 2.0\n\n\u003c\u003cbeginOptional\u003e\u003e\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\n\u003c\u003cendOptional\u003e\u003e\u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.\";match\u003d\".{0,20}\"\u003e\u003e Definitions\n\n \u003c\u003cbeginOptional\u003e\u003e--------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.1.\";match\u003d\".{0,20}\"\u003e\u003e \"Contributor\" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.2.\";match\u003d\".{0,20}\"\u003e\u003e \"Contributor Version\" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor\u0027s Contribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.3.\";match\u003d\".{0,20}\"\u003e\u003e \"Contribution\" means Covered Software of a particular Contributor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.4.\";match\u003d\".{0,20}\"\u003e\u003e \"Covered Software\" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.5.\";match\u003d\".{0,20}\"\u003e\u003e \"Incompatible With Secondary Licenses\" means\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.6.\";match\u003d\".{0,20}\"\u003e\u003e \"Executable Form\" means any form of the work other than Source Code Form.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.7.\";match\u003d\".{0,20}\"\u003e\u003e \"Larger Work\" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.8.\";match\u003d\".{0,20}\"\u003e\u003e \"License\" means this document.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.9.\";match\u003d\".{0,20}\"\u003e\u003e \"Licensable\" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.10.\";match\u003d\".{0,20}\"\u003e\u003e \"Modifications\" means any of the following:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e any new file in Source Code Form that contains any Covered Software.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.11.\";match\u003d\".{0,20}\"\u003e\u003e \"Patent Claims\" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.12.\";match\u003d\".{0,20}\"\u003e\u003e \"Secondary License\" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.13.\";match\u003d\".{0,20}\"\u003e\u003e \"Source Code Form\" means the form of the work preferred for making modifications.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.14.\";match\u003d\".{0,20}\"\u003e\u003e \"You\" (or \"Your\") means an individual or a legal entity exercising rights under this License. For legal entities, \"You\" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, \"control\" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.\";match\u003d\".{0,20}\"\u003e\u003e License Grants and Conditions\n\n \u003c\u003cbeginOptional\u003e\u003e--------------------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.1.\";match\u003d\".{0,20}\"\u003e\u003e Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.2.\";match\u003d\".{0,20}\"\u003e\u003e Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.3.\";match\u003d\".{0,20}\"\u003e\u003e Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e for any code that a Contributor has removed from Covered Software; or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e for infringements caused by: (i) Your and any other third party\u0027s modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(c)\";match\u003d\".{0,20}\"\u003e\u003e under Patent Claims infringed by Covered Software in the absence of its Contributions.\n\n This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.4.\";match\u003d\".{0,20}\"\u003e\u003e Subsequent Licenses\n\n No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.5.\";match\u003d\".{0,20}\"\u003e\u003e Representation\n\n Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.6.\";match\u003d\".{0,20}\"\u003e\u003e Fair Use\n\n This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.7.\";match\u003d\".{0,20}\"\u003e\u003e Conditions\n\n Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.\";match\u003d\".{0,20}\"\u003e\u003e Responsibilities\n\n \u003c\u003cbeginOptional\u003e\u003e-------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.1.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of Source Form\n\n All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients\u0027 rights in the Source Code Form.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.2.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of Executable Form\n\n If You distribute Covered Software in Executable Form then:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients\u0027 rights in the Source Code Form under this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.3.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of a Larger Work\n\n You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.4.\";match\u003d\".{0,20}\"\u003e\u003e Notices\n\n You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.5.\";match\u003d\".{0,20}\"\u003e\u003e Application of Additional Terms\n\n You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"4.\";match\u003d\".{0,20}\"\u003e\u003e Inability to Comply Due to Statute or Regulation\n\n \u003c\u003cbeginOptional\u003e\u003e---------------------------------------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.\";match\u003d\".{0,20}\"\u003e\u003e Termination\n\n \u003c\u003cbeginOptional\u003e\u003e--------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.1.\";match\u003d\".{0,20}\"\u003e\u003e The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.2.\";match\u003d\".{0,20}\"\u003e\u003e If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.3.\";match\u003d\".{0,20}\"\u003e\u003e In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination.\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"6.\";match\u003d\".{0,20}\"\u003e\u003e Disclaimer of Warranty\n\n \u003c\u003cbeginOptional\u003e\u003e* ------------------------- *\n\n \u003c\u003cendOptional\u003e\u003e\n\n Covered Software is provided under this License on an \"as is\" basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer.\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"7.\";match\u003d\".{0,20}\"\u003e\u003e Limitation of Liability\n\n \u003c\u003cbeginOptional\u003e\u003e* -------------------------- *\n\n \u003c\u003cendOptional\u003e\u003e\n\n Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party\u0027s negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.\";match\u003d\".{0,20}\"\u003e\u003e Litigation\n\n \u003c\u003cbeginOptional\u003e\u003e-------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party\u0027s ability to bring cross-claims or counter-claims.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"9.\";match\u003d\".{0,20}\"\u003e\u003e Miscellaneous\n\n \u003c\u003cbeginOptional\u003e\u003e----------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.\";match\u003d\".{0,20}\"\u003e\u003e Versions of the License\n\n \u003c\u003cbeginOptional\u003e\u003e---------------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.1.\";match\u003d\".{0,20}\"\u003e\u003e New Versions\n\n Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.2.\";match\u003d\".{0,20}\"\u003e\u003e Effect of New Versions\n\n You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.3.\";match\u003d\".{0,20}\"\u003e\u003e Modified Versions\n\n If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.4.\";match\u003d\".{0,20}\"\u003e\u003e Distributing Source Code Form that is Incompatible With Secondary Licenses\n\n If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached.\n\n \u003c\u003cbeginOptional\u003e\u003eExhibit A - Source Code Form License Notice\n\n\u003c\u003cbeginOptional\u003e\u003e-------------------------------------------\n\n\u003c\u003cendOptional\u003e\u003e\n\nThis Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf 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.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n\n\u003c\u003cbeginOptional\u003e\u003e---------------------------------------------------------\n\n\u003c\u003cendOptional\u003e\u003e\n\nThis Source Code Form is \"Incompatible With Secondary Licenses\", as defined by the Mozilla Public License, v. 2.0.\n\n\u003c\u003cendOptional\u003e\u003e", + "standardLicenseHeaderTemplate": "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\n", + "standardLicenseTemplate": "\u003c\u003cbeginOptional\u003e\u003eMozilla Public License Version 2.0\n\n\u003c\u003cbeginOptional\u003e\u003e\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\n\u003c\u003cendOptional\u003e\u003e\u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.\";match\u003d\".{0,20}\"\u003e\u003e Definitions\n\n \u003c\u003cbeginOptional\u003e\u003e--------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.1.\";match\u003d\".{0,20}\"\u003e\u003e \"Contributor\" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.2.\";match\u003d\".{0,20}\"\u003e\u003e \"Contributor Version\" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor\u0027s Contribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.3.\";match\u003d\".{0,20}\"\u003e\u003e \"Contribution\" means Covered Software of a particular Contributor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.4.\";match\u003d\".{0,20}\"\u003e\u003e \"Covered Software\" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.5.\";match\u003d\".{0,20}\"\u003e\u003e \"Incompatible With Secondary Licenses\" means\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.6.\";match\u003d\".{0,20}\"\u003e\u003e \"Executable Form\" means any form of the work other than Source Code Form.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.7.\";match\u003d\".{0,20}\"\u003e\u003e \"Larger Work\" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.8.\";match\u003d\".{0,20}\"\u003e\u003e \"License\" means this document.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.9.\";match\u003d\".{0,20}\"\u003e\u003e \"Licensable\" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.10.\";match\u003d\".{0,20}\"\u003e\u003e \"Modifications\" means any of the following:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e any new file in Source Code Form that contains any Covered Software.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.11.\";match\u003d\".{0,20}\"\u003e\u003e \"Patent Claims\" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.12.\";match\u003d\".{0,20}\"\u003e\u003e \"Secondary License\" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.13.\";match\u003d\".{0,20}\"\u003e\u003e \"Source Code Form\" means the form of the work preferred for making modifications.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.14.\";match\u003d\".{0,20}\"\u003e\u003e \"You\" (or \"Your\") means an individual or a legal entity exercising rights under this License. For legal entities, \"You\" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, \"control\" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.\";match\u003d\".{0,20}\"\u003e\u003e License Grants and Conditions\n\n \u003c\u003cbeginOptional\u003e\u003e--------------------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.1.\";match\u003d\".{0,20}\"\u003e\u003e Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.2.\";match\u003d\".{0,20}\"\u003e\u003e Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.3.\";match\u003d\".{0,20}\"\u003e\u003e Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e for any code that a Contributor has removed from Covered Software; or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e for infringements caused by: (i) Your and any other third party\u0027s modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(c)\";match\u003d\".{0,20}\"\u003e\u003e under Patent Claims infringed by Covered Software in the absence of its Contributions.\n\n This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.4.\";match\u003d\".{0,20}\"\u003e\u003e Subsequent Licenses\n\n No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.5.\";match\u003d\".{0,20}\"\u003e\u003e Representation\n\n Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.6.\";match\u003d\".{0,20}\"\u003e\u003e Fair Use\n\n This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.7.\";match\u003d\".{0,20}\"\u003e\u003e Conditions\n\n Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.\";match\u003d\".{0,20}\"\u003e\u003e Responsibilities\n\n \u003c\u003cbeginOptional\u003e\u003e-------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.1.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of Source Form\n\n All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients\u0027 rights in the Source Code Form.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.2.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of Executable Form\n\n If You distribute Covered Software in Executable Form then:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(a)\";match\u003d\".{0,20}\"\u003e\u003e such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"(b)\";match\u003d\".{0,20}\"\u003e\u003e You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients\u0027 rights in the Source Code Form under this License.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.3.\";match\u003d\".{0,20}\"\u003e\u003e Distribution of a Larger Work\n\n You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.4.\";match\u003d\".{0,20}\"\u003e\u003e Notices\n\n You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.5.\";match\u003d\".{0,20}\"\u003e\u003e Application of Additional Terms\n\n You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"4.\";match\u003d\".{0,20}\"\u003e\u003e Inability to Comply Due to Statute or Regulation\n\n \u003c\u003cbeginOptional\u003e\u003e---------------------------------------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.\";match\u003d\".{0,20}\"\u003e\u003e Termination\n\n \u003c\u003cbeginOptional\u003e\u003e--------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.1.\";match\u003d\".{0,20}\"\u003e\u003e The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.2.\";match\u003d\".{0,20}\"\u003e\u003e If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"5.3.\";match\u003d\".{0,20}\"\u003e\u003e In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination.\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"6.\";match\u003d\".{0,20}\"\u003e\u003e Disclaimer of Warranty\n\n \u003c\u003cbeginOptional\u003e\u003e* ------------------------- *\n\n \u003c\u003cendOptional\u003e\u003e\n\n Covered Software is provided under this License on an \"as is\" basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer.\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"7.\";match\u003d\".{0,20}\"\u003e\u003e Limitation of Liability\n\n \u003c\u003cbeginOptional\u003e\u003e* -------------------------- *\n\n \u003c\u003cendOptional\u003e\u003e\n\n Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party\u0027s negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.\n\n \u003c\u003cbeginOptional\u003e\u003e************************************************************************\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"8.\";match\u003d\".{0,20}\"\u003e\u003e Litigation\n\n \u003c\u003cbeginOptional\u003e\u003e-------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party\u0027s ability to bring cross-claims or counter-claims.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"9.\";match\u003d\".{0,20}\"\u003e\u003e Miscellaneous\n\n \u003c\u003cbeginOptional\u003e\u003e----------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.\";match\u003d\".{0,20}\"\u003e\u003e Versions of the License\n\n \u003c\u003cbeginOptional\u003e\u003e---------------------------\n\n \u003c\u003cendOptional\u003e\u003e\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.1.\";match\u003d\".{0,20}\"\u003e\u003e New Versions\n\n Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.2.\";match\u003d\".{0,20}\"\u003e\u003e Effect of New Versions\n\n You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.3.\";match\u003d\".{0,20}\"\u003e\u003e Modified Versions\n\n If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License).\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"10.4.\";match\u003d\".{0,20}\"\u003e\u003e Distributing Source Code Form that is Incompatible With Secondary Licenses\n\n If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached.\n\n \u003c\u003cbeginOptional\u003e\u003eExhibit A - Source Code Form License Notice\n\n\u003c\u003cbeginOptional\u003e\u003e-------------------------------------------\n\n\u003c\u003cendOptional\u003e\u003e\n\nThis Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\nIf 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.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n\n\u003c\u003cbeginOptional\u003e\u003e---------------------------------------------------------\n\n\u003c\u003cendOptional\u003e\u003e\n\nThis Source Code Form is \"Incompatible With Secondary Licenses\", as defined by the Mozilla Public License, v. 2.0.\n\n\u003c\u003cendOptional\u003e\u003e", "name": "Mozilla Public License 2.0", "licenseComments": "This license was released in January 2012. This license list entry is for use when the standard MPL 2.0 is used, as indicated by the standard header (Exhibit A but no Exhibit B).", "licenseId": "MPL-2.0", - "standardLicenseHeader": "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n", + "standardLicenseHeader": "This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\n", "crossRef": [ { - "match": "N/A", - "url": "https://opensource.org/licenses/MPL-2.0", + "match": "true", + "url": "https://www.mozilla.org/MPL/2.0/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:16Z", + "timestamp": "2024-08-19T17:36:03Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { - "match": "true", - "url": "https://www.mozilla.org/MPL/2.0/", + "match": "N/A", + "url": "https://opensource.org/licenses/MPL-2.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:16Z", + "timestamp": "2024-08-19T17:36:03Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ @@ -33,6 +33,6 @@ "https://opensource.org/licenses/MPL-2.0" ], "isOsiApproved": true, - "licenseTextHtml": "\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eMozilla Public License Version 2.0\u003c/p\u003e\n\n\t \u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003c/p\u003e\n\u003c/div\u003e\n \u003c/div\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \t\t\u003cp\u003e\n\t\t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 1.\u003c/var\u003e\n\t\t\tDefinitions\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e--------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.1.\u003c/var\u003e\n \u0026quot;Contributor\u0026quot; means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.2.\u003c/var\u003e\n \u0026quot;Contributor Version\u0026quot; means the combination of the Contributions of others (if any)\n used by a Contributor and that particular Contributor\u0026apos;s Contribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.3.\u003c/var\u003e\n \u0026quot;Contribution\u0026quot; means Covered Software of a particular Contributor.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.4.\u003c/var\u003e\n \u0026quot;Covered Software\u0026quot; means Source Code Form to which the initial Contributor has\n attached the notice in Exhibit A, the Executable Form of such Source Code Form, and\n Modifications of such Source Code Form, in each case including portions thereof.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.5.\u003c/var\u003e\n \u0026quot;Incompatible With Secondary Licenses\u0026quot; means\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n that the initial Contributor has attached the notice described in Exhibit B to the\n Covered Software; or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n that the Covered Software was made available under the terms of version 1.1 or earlier of\n the License, but not also under the terms of a Secondary License.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.6.\u003c/var\u003e\n \u0026quot;Executable Form\u0026quot; means any form of the work other than Source Code Form.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.7.\u003c/var\u003e\n \u0026quot;Larger Work\u0026quot; means a work that combines Covered Software with other material, in a\n separate file or files, that is not Covered Software.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.8.\u003c/var\u003e\n \u0026quot;License\u0026quot; means this document.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.9.\u003c/var\u003e\n \u0026quot;Licensable\u0026quot; means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and all of the rights\n conveyed by this License.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.10.\u003c/var\u003e\n \u0026quot;Modifications\u0026quot; means any of the following:\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n any file in Source Code Form that results from an addition to, deletion from, or\n modification of the contents of Covered Software; or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n any new file in Source Code Form that contains any Covered Software.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.11.\u003c/var\u003e\n \u0026quot;Patent Claims\u0026quot; of a Contributor means any patent claim(s), including without\n limitation, method, process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the License, by the making,\n using, selling, offering for sale, having made, import, or transfer of either its\n Contributions or its Contributor Version.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.12.\u003c/var\u003e\n \u0026quot;Secondary License\u0026quot; means either the GNU General Public License, Version 2.0, the\n GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License,\n Version 3.0, or any later versions of those licenses.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.13.\u003c/var\u003e\n \u0026quot;Source Code Form\u0026quot; means the form of the work preferred for making modifications.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.14.\u003c/var\u003e\n \u0026quot;You\u0026quot; (or \u0026quot;Your\u0026quot;) means an individual or a legal entity exercising rights\n under this License. For legal entities, \u0026quot;You\u0026quot; includes any entity that controls,\n is controlled by, or is under common control with You. For purposes of this definition,\n \u0026quot;control\u0026quot; means (a) the power, direct or indirect, to cause the direction or\n management of such entity, whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 2.\u003c/var\u003e\n\t\t\tLicense Grants and Conditions\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e--------------------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.1.\u003c/var\u003e\n Grants\n \u003cp\u003eEach Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:\n \u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n under intellectual property rights (other than patent or trademark) Licensable by such\n Contributor to use, reproduce, make available, modify, display, perform, distribute,\n and otherwise exploit its Contributions, either on an unmodified basis, with\n Modifications, or as part of a Larger Work; and\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n under Patent Claims of such Contributor to make, use, sell, offer for sale, have made,\n import, and otherwise transfer either its Contributions or its Contributor\n Version.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.2.\u003c/var\u003e\n Effective Date\n \u003cp\u003eThe licenses granted in Section 2.1 with respect to any Contribution become effective\n for each Contribution on the date the Contributor first distributes such\n Contribution.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.3.\u003c/var\u003e\n Limitations on Grant Scope\n \u003cp\u003eThe licenses granted in this Section 2 are the only rights granted under this License.\n No additional rights or licenses will be implied from the distribution or\n licensing of Covered Software under this License. Notwithstanding Section 2.1(b)\n above, no patent license is granted by a Contributor:\n \u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n for any code that a Contributor has removed from Covered Software; or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n for infringements caused by: (i) Your and any other third party\u0026apos;s modifications of\n Covered Software, or (ii) the combination of its Contributions with other software\n (except as part of its Contributor Version); or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (c)\u003c/var\u003e\n under Patent Claims infringed by Covered Software in the absence of its Contributions.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003eThis License does not grant any rights in the trademarks, service marks, or logos of any\n Contributor (except as may be necessary to comply with the notice requirements in\n Section 3.4).\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.4.\u003c/var\u003e\n Subsequent Licenses\n \u003cp\u003eNo Contributor makes additional grants as a result of Your choice to distribute the\n Covered Software under a subsequent version of this License (see Section 10.2) or\n under the terms of a Secondary License (if permitted under the terms of Section\n 3.3).\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.5.\u003c/var\u003e\n Representation\n \u003cp\u003eEach Contributor represents that the Contributor believes its Contributions are its\n original creation(s) or it has sufficient rights to grant the rights to its\n Contributions conveyed by this License.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.6.\u003c/var\u003e\n Fair Use\n \u003cp\u003eThis License is not intended to limit any rights You have under applicable copyright\n doctrines of fair use, fair dealing, or other equivalents.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.7.\u003c/var\u003e\n Conditions\n \u003cp\u003eSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1.\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 3.\u003c/var\u003e\n\t\t Responsibilities\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e-------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.1.\u003c/var\u003e\n Distribution of Source Form\n \u003cp\u003eAll distribution of Covered Software in Source Code Form, including any Modifications\n that You create or to which You contribute, must be under the terms of this\n License. You must inform recipients that the Source Code Form of the Covered\n Software is governed by the terms of this License, and how they can obtain a copy\n of this License. You may not attempt to alter or restrict the recipients\u0026apos;\n rights in the Source Code Form.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.2.\u003c/var\u003e\n Distribution of Executable Form\n \u003cp\u003eIf You distribute Covered Software in Executable Form then:\u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n such Covered Software must also be made available in Source Code Form, as described in\n Section 3.1, and You must inform recipients of the Executable Form how they can obtain\n a copy of such Source Code Form by reasonable means in a timely manner, at a charge no\n more than the cost of distribution to the recipient; and\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n You may distribute such Executable Form under the terms of this License, or sublicense it\n under different terms, provided that the license for the Executable Form does not\n attempt to limit or alter the recipients\u0026apos; rights in the Source Code Form under\n this License.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.3.\u003c/var\u003e\n Distribution of a Larger Work\n \u003cp\u003eYou may create and distribute a Larger Work under terms of Your choice, provided that\n You also comply with the requirements of this License for the Covered Software. If\n the Larger Work is a combination of Covered Software with a work governed by one\n or more Secondary Licenses, and the Covered Software is not Incompatible With\n Secondary Licenses, this License permits You to additionally distribute such\n Covered Software under the terms of such Secondary License(s), so that the\n recipient of the Larger Work may, at their option, further distribute the Covered\n Software under the terms of either this License or such Secondary License(s).\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.4.\u003c/var\u003e\n Notices\n \u003cp\u003eYou may not remove or alter the substance of any license notices (including copyright\n notices, patent notices, disclaimers of warranty, or limitations of liability)\n contained within the Source Code Form of the Covered Software, except that You may\n alter any license notices to the extent required to remedy known factual\n inaccuracies.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.5.\u003c/var\u003e\n Application of Additional Terms\n \u003cp\u003eYou may choose to offer, and to charge a fee for, warranty, support, indemnity or\n liability obligations to one or more recipients of Covered Software. However, You\n may do so only on Your own behalf, and not on behalf of any Contributor. You must\n make it absolutely clear that any such warranty, support, indemnity, or liability\n obligation is offered by You alone, and You hereby agree to indemnify every\n Contributor for any liability incurred by such Contributor as a result of\n warranty, support, indemnity or liability terms You offer. You may include\n additional disclaimers of warranty and limitations of liability specific to any\n jurisdiction.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 4.\u003c/var\u003e\n \t\tInability to Comply Due to Statute or Regulation\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e---------------------------------------------------\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eIf it is impossible for You to comply with any of the terms of this License with respect to\n some or all of the Covered Software due to statute, judicial order, or regulation then\n You must: (a) comply with the terms of this License to the maximum extent possible;\n and (b) describe the limitations and the code they affect. Such description must be\n placed in a text file included with all distributions of the Covered Software under\n this License. Except to the extent prohibited by statute or regulation, such\n description must be sufficiently detailed for a recipient of ordinary skill to be able\n to understand it.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n\t\t\u003cp\u003e\n\t\t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 5.\u003c/var\u003e\n\t\t Termination\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e--------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.1.\u003c/var\u003e\n The rights granted under this License will terminate automatically if You fail to comply with\n any of its terms. However, if You become compliant, then the rights granted under this\n License from a particular Contributor are reinstated (a) provisionally, unless and until\n such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing\n basis, if such Contributor fails to notify You of the non-compliance by some reasonable\n means prior to 60 days after You have come back into compliance. Moreover, Your grants\n from a particular Contributor are reinstated on an ongoing basis if such Contributor\n notifies You of the non-compliance by some reasonable means, this is the first time You\n have received notice of non-compliance with this License from such Contributor, and You\n become compliant prior to 30 days after Your receipt of the notice.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.2.\u003c/var\u003e\n If You initiate litigation against any entity by asserting a patent infringement claim\n (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a\n Contributor Version directly or indirectly infringes any patent, then the rights granted\n to You by any and all Contributors for the Covered Software under Section 2.1 of this\n License shall terminate.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.3.\u003c/var\u003e\n In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements\n (excluding distributors and resellers) which have been validly granted by You or Your\n distributors under this License prior to termination shall survive termination.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n\t\t\u003cp\u003e\n\t \t\u003cvar class\u003d\"replaceable-license-text\"\u003e 6.\u003c/var\u003e\n\t \tDisclaimer of Warranty\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e* ------------------------- *\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eCovered Software is provided under this License on an \u0026quot;as is\u0026quot; basis, without\n warranty of any kind, either expressed, implied, or statutory, including, without\n limitation, warranties that the Covered Software is free of defects, merchantable, fit\n for a particular purpose or non-infringing. The entire risk as to the quality and\n performance of the Covered Software is with You. Should any Covered Software prove\n defective in any respect, You (not any Contributor) assume the cost of any necessary\n servicing, repair, or correction. This disclaimer of warranty constitutes an essential\n part of this License. No use of any Covered Software is authorized under this License\n except under this disclaimer.\n \u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 7.\u003c/var\u003e\n \t\tLimitation of Liability\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e* -------------------------- *\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eUnder no circumstances and under no legal theory, whether tort (including negligence),\n contract, or otherwise, shall any Contributor, or anyone who distributes Covered\n Software as permitted above, be liable to You for any direct, indirect, special,\n incidental, or consequential damages of any character including, without limitation,\n damages for lost profits, loss of goodwill, work stoppage, computer failure or\n malfunction, or any and all other commercial damages or losses, even if such party\n shall have been informed of the possibility of such damages. This limitation of\n liability shall not apply to liability for death or personal injury resulting from\n such party\u0026apos;s negligence to the extent applicable law prohibits such limitation.\n Some jurisdictions do not allow the exclusion or limitation of incidental or\n consequential damages, so this exclusion and limitation may not apply to You.\n \u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 8.\u003c/var\u003e\n \t\tLitigation\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e-------------\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eAny litigation relating to this License may be brought only in the courts of a jurisdiction\n where the defendant maintains its principal place of business and such litigation\n shall be governed by laws of that jurisdiction, without reference to its\n conflict-of-law provisions. Nothing in this Section shall prevent a party\u0026apos;s\n ability to bring cross-claims or counter-claims.\n \u003c/p\u003e\n\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 9.\u003c/var\u003e\n \t\tMiscellaneous\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e----------------\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eThis License represents the complete agreement concerning the subject matter hereof. If any\n provision of this License is held to be unenforceable, such provision shall be\n reformed only to the extent necessary to make it enforceable. Any law or regulation\n which provides that the language of a contract shall be construed against the drafter\n shall not be used to construe this License against a Contributor.\n \u003c/p\u003e\n\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 10.\u003c/var\u003e\n \t\tVersions of the License\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e---------------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.1.\u003c/var\u003e\n New Versions\n \u003cp\u003eMozilla Foundation is the license steward. Except as provided in Section 10.3, no one\n other than the license steward has the right to modify or publish new versions of\n this License. Each version will be given a distinguishing version number.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.2.\u003c/var\u003e\n Effect of New Versions\n \u003cp\u003eYou may distribute the Covered Software under the terms of the version of the License\n under which You originally received the Covered Software, or under the terms of\n any subsequent version published by the license steward.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.3.\u003c/var\u003e\n Modified Versions\n \u003cp\u003eIf you create software not governed by this License, and you want to create a new\n license for such software, you may create and use a modified version of this\n License if you rename the license and remove any references to the name of the\n license steward (except to note that such modified license differs from this\n License).\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.4.\u003c/var\u003e\n Distributing Source Code Form that is Incompatible With Secondary Licenses\n \u003cp\u003eIf You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms\n of this version of the License, the notice described in Exhibit B of this License must be\n attached.\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003c/ul\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eExhibit A - Source Code Form License Notice\u003c/p\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e-------------------------------------------\u003c/p\u003e\n\u003c/div\u003e\n\t \n \u003cp\u003eThis Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL\n was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.\u003c/p\u003e\n\n \u003cp\u003eIf it is not possible or desirable to put the notice in a particular file, then You may include the\n notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be\n likely to look for such a notice.\u003c/p\u003e\n\n \u003cp\u003eYou may add additional accurate notices of copyright ownership.\u003c/p\u003e\n\n \u003cp\u003eExhibit B - \u0026quot;Incompatible With Secondary Licenses\u0026quot; Notice\u003c/p\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e---------------------------------------------------------\u003c/p\u003e\n\u003c/div\u003e\n\t \u003cp\u003eThis Source Code Form is \u0026quot;Incompatible With Secondary Licenses\u0026quot;, as defined by the Mozilla\n Public License, v. 2.0.\u003c/p\u003e\n\n \u003c/div\u003e\n ", - "standardLicenseHeaderHtml": "\n \u003cp\u003eThis Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL\n was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.\u003c/p\u003e\n\n " + "licenseTextHtml": "\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eMozilla Public License Version 2.0\u003c/p\u003e\n\n\t \u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003c/p\u003e\n\u003c/div\u003e\n \u003c/div\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \t\t\u003cp\u003e\n\t\t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 1.\u003c/var\u003e\n\t\t\tDefinitions\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e--------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.1.\u003c/var\u003e\n \u0026quot;Contributor\u0026quot; means each individual or legal entity that creates, contributes to\n the creation of, or owns Covered Software.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.2.\u003c/var\u003e\n \u0026quot;Contributor Version\u0026quot; means the combination of the Contributions of others (if any)\n used by a Contributor and that particular Contributor\u0026apos;s Contribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.3.\u003c/var\u003e\n \u0026quot;Contribution\u0026quot; means Covered Software of a particular Contributor.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.4.\u003c/var\u003e\n \u0026quot;Covered Software\u0026quot; means Source Code Form to which the initial Contributor has\n attached the notice in Exhibit A, the Executable Form of such Source Code Form, and\n Modifications of such Source Code Form, in each case including portions thereof.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.5.\u003c/var\u003e\n \u0026quot;Incompatible With Secondary Licenses\u0026quot; means\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n that the initial Contributor has attached the notice described in Exhibit B to the\n Covered Software; or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n that the Covered Software was made available under the terms of version 1.1 or earlier of\n the License, but not also under the terms of a Secondary License.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.6.\u003c/var\u003e\n \u0026quot;Executable Form\u0026quot; means any form of the work other than Source Code Form.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.7.\u003c/var\u003e\n \u0026quot;Larger Work\u0026quot; means a work that combines Covered Software with other material, in a\n separate file or files, that is not Covered Software.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.8.\u003c/var\u003e\n \u0026quot;License\u0026quot; means this document.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.9.\u003c/var\u003e\n \u0026quot;Licensable\u0026quot; means having the right to grant, to the maximum extent possible,\n whether at the time of the initial grant or subsequently, any and all of the rights\n conveyed by this License.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.10.\u003c/var\u003e\n \u0026quot;Modifications\u0026quot; means any of the following:\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n any file in Source Code Form that results from an addition to, deletion from, or\n modification of the contents of Covered Software; or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n any new file in Source Code Form that contains any Covered Software.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.11.\u003c/var\u003e\n \u0026quot;Patent Claims\u0026quot; of a Contributor means any patent claim(s), including without\n limitation, method, process, and apparatus claims, in any patent Licensable by such\n Contributor that would be infringed, but for the grant of the License, by the making,\n using, selling, offering for sale, having made, import, or transfer of either its\n Contributions or its Contributor Version.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.12.\u003c/var\u003e\n \u0026quot;Secondary License\u0026quot; means either the GNU General Public License, Version 2.0, the\n GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License,\n Version 3.0, or any later versions of those licenses.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.13.\u003c/var\u003e\n \u0026quot;Source Code Form\u0026quot; means the form of the work preferred for making modifications.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.14.\u003c/var\u003e\n \u0026quot;You\u0026quot; (or \u0026quot;Your\u0026quot;) means an individual or a legal entity exercising rights\n under this License. For legal entities, \u0026quot;You\u0026quot; includes any entity that controls,\n is controlled by, or is under common control with You. For purposes of this definition,\n \u0026quot;control\u0026quot; means (a) the power, direct or indirect, to cause the direction or\n management of such entity, whether by contract or otherwise, or (b) ownership of more than\n fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 2.\u003c/var\u003e\n\t\t\tLicense Grants and Conditions\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e--------------------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.1.\u003c/var\u003e\n Grants\n \u003cp\u003eEach Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:\n \u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n under intellectual property rights (other than patent or trademark) Licensable by such\n Contributor to use, reproduce, make available, modify, display, perform, distribute,\n and otherwise exploit its Contributions, either on an unmodified basis, with\n Modifications, or as part of a Larger Work; and\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n under Patent Claims of such Contributor to make, use, sell, offer for sale, have made,\n import, and otherwise transfer either its Contributions or its Contributor\n Version.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.2.\u003c/var\u003e\n Effective Date\n \u003cp\u003eThe licenses granted in Section 2.1 with respect to any Contribution become effective\n for each Contribution on the date the Contributor first distributes such\n Contribution.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.3.\u003c/var\u003e\n Limitations on Grant Scope\n \u003cp\u003eThe licenses granted in this Section 2 are the only rights granted under this License.\n No additional rights or licenses will be implied from the distribution or\n licensing of Covered Software under this License. Notwithstanding Section 2.1(b)\n above, no patent license is granted by a Contributor:\n \u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n for any code that a Contributor has removed from Covered Software; or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n for infringements caused by: (i) Your and any other third party\u0026apos;s modifications of\n Covered Software, or (ii) the combination of its Contributions with other software\n (except as part of its Contributor Version); or\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (c)\u003c/var\u003e\n under Patent Claims infringed by Covered Software in the absence of its Contributions.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003eThis License does not grant any rights in the trademarks, service marks, or logos of any\n Contributor (except as may be necessary to comply with the notice requirements in\n Section 3.4).\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.4.\u003c/var\u003e\n Subsequent Licenses\n \u003cp\u003eNo Contributor makes additional grants as a result of Your choice to distribute the\n Covered Software under a subsequent version of this License (see Section 10.2) or\n under the terms of a Secondary License (if permitted under the terms of Section\n 3.3).\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.5.\u003c/var\u003e\n Representation\n \u003cp\u003eEach Contributor represents that the Contributor believes its Contributions are its\n original creation(s) or it has sufficient rights to grant the rights to its\n Contributions conveyed by this License.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.6.\u003c/var\u003e\n Fair Use\n \u003cp\u003eThis License is not intended to limit any rights You have under applicable copyright\n doctrines of fair use, fair dealing, or other equivalents.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.7.\u003c/var\u003e\n Conditions\n \u003cp\u003eSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1.\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 3.\u003c/var\u003e\n\t\t Responsibilities\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e-------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.1.\u003c/var\u003e\n Distribution of Source Form\n \u003cp\u003eAll distribution of Covered Software in Source Code Form, including any Modifications\n that You create or to which You contribute, must be under the terms of this\n License. You must inform recipients that the Source Code Form of the Covered\n Software is governed by the terms of this License, and how they can obtain a copy\n of this License. You may not attempt to alter or restrict the recipients\u0026apos;\n rights in the Source Code Form.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.2.\u003c/var\u003e\n Distribution of Executable Form\n \u003cp\u003eIf You distribute Covered Software in Executable Form then:\u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (a)\u003c/var\u003e\n such Covered Software must also be made available in Source Code Form, as described in\n Section 3.1, and You must inform recipients of the Executable Form how they can obtain\n a copy of such Source Code Form by reasonable means in a timely manner, at a charge no\n more than the cost of distribution to the recipient; and\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e (b)\u003c/var\u003e\n You may distribute such Executable Form under the terms of this License, or sublicense it\n under different terms, provided that the license for the Executable Form does not\n attempt to limit or alter the recipients\u0026apos; rights in the Source Code Form under\n this License.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.3.\u003c/var\u003e\n Distribution of a Larger Work\n \u003cp\u003eYou may create and distribute a Larger Work under terms of Your choice, provided that\n You also comply with the requirements of this License for the Covered Software. If\n the Larger Work is a combination of Covered Software with a work governed by one\n or more Secondary Licenses, and the Covered Software is not Incompatible With\n Secondary Licenses, this License permits You to additionally distribute such\n Covered Software under the terms of such Secondary License(s), so that the\n recipient of the Larger Work may, at their option, further distribute the Covered\n Software under the terms of either this License or such Secondary License(s).\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.4.\u003c/var\u003e\n Notices\n \u003cp\u003eYou may not remove or alter the substance of any license notices (including copyright\n notices, patent notices, disclaimers of warranty, or limitations of liability)\n contained within the Source Code Form of the Covered Software, except that You may\n alter any license notices to the extent required to remedy known factual\n inaccuracies.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.5.\u003c/var\u003e\n Application of Additional Terms\n \u003cp\u003eYou may choose to offer, and to charge a fee for, warranty, support, indemnity or\n liability obligations to one or more recipients of Covered Software. However, You\n may do so only on Your own behalf, and not on behalf of any Contributor. You must\n make it absolutely clear that any such warranty, support, indemnity, or liability\n obligation is offered by You alone, and You hereby agree to indemnify every\n Contributor for any liability incurred by such Contributor as a result of\n warranty, support, indemnity or liability terms You offer. You may include\n additional disclaimers of warranty and limitations of liability specific to any\n jurisdiction.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 4.\u003c/var\u003e\n \t\tInability to Comply Due to Statute or Regulation\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e---------------------------------------------------\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eIf it is impossible for You to comply with any of the terms of this License with respect to\n some or all of the Covered Software due to statute, judicial order, or regulation then\n You must: (a) comply with the terms of this License to the maximum extent possible;\n and (b) describe the limitations and the code they affect. Such description must be\n placed in a text file included with all distributions of the Covered Software under\n this License. Except to the extent prohibited by statute or regulation, such\n description must be sufficiently detailed for a recipient of ordinary skill to be able\n to understand it.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n\t\t\u003cp\u003e\n\t\t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 5.\u003c/var\u003e\n\t\t Termination\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e--------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.1.\u003c/var\u003e\n The rights granted under this License will terminate automatically if You fail to comply with\n any of its terms. However, if You become compliant, then the rights granted under this\n License from a particular Contributor are reinstated (a) provisionally, unless and until\n such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing\n basis, if such Contributor fails to notify You of the non-compliance by some reasonable\n means prior to 60 days after You have come back into compliance. Moreover, Your grants\n from a particular Contributor are reinstated on an ongoing basis if such Contributor\n notifies You of the non-compliance by some reasonable means, this is the first time You\n have received notice of non-compliance with this License from such Contributor, and You\n become compliant prior to 30 days after Your receipt of the notice.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.2.\u003c/var\u003e\n If You initiate litigation against any entity by asserting a patent infringement claim\n (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a\n Contributor Version directly or indirectly infringes any patent, then the rights granted\n to You by any and all Contributors for the Covered Software under Section 2.1 of this\n License shall terminate.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 5.3.\u003c/var\u003e\n In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements\n (excluding distributors and resellers) which have been validly granted by You or Your\n distributors under this License prior to termination shall survive termination.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n\t\t\u003cp\u003e\n\t \t\u003cvar class\u003d\"replaceable-license-text\"\u003e 6.\u003c/var\u003e\n\t \tDisclaimer of Warranty\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e* ------------------------- *\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eCovered Software is provided under this License on an \u0026quot;as is\u0026quot; basis, without\n warranty of any kind, either expressed, implied, or statutory, including, without\n limitation, warranties that the Covered Software is free of defects, merchantable, fit\n for a particular purpose or non-infringing. The entire risk as to the quality and\n performance of the Covered Software is with You. Should any Covered Software prove\n defective in any respect, You (not any Contributor) assume the cost of any necessary\n servicing, repair, or correction. This disclaimer of warranty constitutes an essential\n part of this License. No use of any Covered Software is authorized under this License\n except under this disclaimer.\n \u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n \u003c/li\u003e\n \n\u003cli\u003e\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 7.\u003c/var\u003e\n \t\tLimitation of Liability\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e* -------------------------- *\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eUnder no circumstances and under no legal theory, whether tort (including negligence),\n contract, or otherwise, shall any Contributor, or anyone who distributes Covered\n Software as permitted above, be liable to You for any direct, indirect, special,\n incidental, or consequential damages of any character including, without limitation,\n damages for lost profits, loss of goodwill, work stoppage, computer failure or\n malfunction, or any and all other commercial damages or losses, even if such party\n shall have been informed of the possibility of such damages. This limitation of\n liability shall not apply to liability for death or personal injury resulting from\n such party\u0026apos;s negligence to the extent applicable law prohibits such limitation.\n Some jurisdictions do not allow the exclusion or limitation of incidental or\n consequential damages, so this exclusion and limitation may not apply to You.\n \u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e************************************************************************\u003c/p\u003e\n\u003c/div\u003e\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 8.\u003c/var\u003e\n \t\tLitigation\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e-------------\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eAny litigation relating to this License may be brought only in the courts of a jurisdiction\n where the defendant maintains its principal place of business and such litigation\n shall be governed by laws of that jurisdiction, without reference to its\n conflict-of-law provisions. Nothing in this Section shall prevent a party\u0026apos;s\n ability to bring cross-claims or counter-claims.\n \u003c/p\u003e\n\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 9.\u003c/var\u003e\n \t\tMiscellaneous\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e----------------\u003c/p\u003e\n\u003c/div\u003e\n \u003cp\u003eThis License represents the complete agreement concerning the subject matter hereof. If any\n provision of this License is held to be unenforceable, such provision shall be\n reformed only to the extent necessary to make it enforceable. Any law or regulation\n which provides that the language of a contract shall be construed against the drafter\n shall not be used to construe this License against a Contributor.\n \u003c/p\u003e\n\n \u003c/li\u003e\n\t\n\u003cli\u003e\n\t\t\u003cp\u003e\n \t\t\u003cvar class\u003d\"replaceable-license-text\"\u003e 10.\u003c/var\u003e\n \t\tVersions of the License\n\t\t\u003c/p\u003e\n\n\t\t\u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e---------------------------\u003c/p\u003e\n\u003c/div\u003e\n \n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.1.\u003c/var\u003e\n New Versions\n \u003cp\u003eMozilla Foundation is the license steward. Except as provided in Section 10.3, no one\n other than the license steward has the right to modify or publish new versions of\n this License. Each version will be given a distinguishing version number.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.2.\u003c/var\u003e\n Effect of New Versions\n \u003cp\u003eYou may distribute the Covered Software under the terms of the version of the License\n under which You originally received the Covered Software, or under the terms of\n any subsequent version published by the license steward.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.3.\u003c/var\u003e\n Modified Versions\n \u003cp\u003eIf you create software not governed by this License, and you want to create a new\n license for such software, you may create and use a modified version of this\n License if you rename the license and remove any references to the name of the\n license steward (except to note that such modified license differs from this\n License).\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 10.4.\u003c/var\u003e\n Distributing Source Code Form that is Incompatible With Secondary Licenses\n \u003cp\u003eIf You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms\n of this version of the License, the notice described in Exhibit B of this License must be\n attached.\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003c/li\u003e\n \n\u003c/ul\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eExhibit A - Source Code Form License Notice\u003c/p\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e-------------------------------------------\u003c/p\u003e\n\u003c/div\u003e\n\t \n \u003cp\u003eThis Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL\n was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.\u003c/p\u003e\n\n \u003cp\u003eIf it is not possible or desirable to put the notice in a particular file, then You may include the\n notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be\n likely to look for such a notice.\u003c/p\u003e\n\n \u003cp\u003eYou may add additional accurate notices of copyright ownership.\u003c/p\u003e\n\n \u003cp\u003eExhibit B - \u0026quot;Incompatible With Secondary Licenses\u0026quot; Notice\u003c/p\u003e\n\n \u003cdiv class\u003d\"optional-license-text\"\u003e \u003cp\u003e---------------------------------------------------------\u003c/p\u003e\n\u003c/div\u003e\n\t \u003cp\u003eThis Source Code Form is \u0026quot;Incompatible With Secondary Licenses\u0026quot;, as defined by the Mozilla\n Public License, v. 2.0.\u003c/p\u003e\n\n \u003c/div\u003e\n ", + "standardLicenseHeaderHtml": "\n \u003cp\u003eThis Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL\n was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.\u003c/p\u003e\n\n " } \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/MS-LPL.json b/src/main/resources/license-list-data/json/details/MS-LPL.json index 0e265b6a34..2e5df9ec4d 100644 --- a/src/main/resources/license-list-data/json/details/MS-LPL.json +++ b/src/main/resources/license-list-data/json/details/MS-LPL.json @@ -7,30 +7,30 @@ "crossRef": [ { "match": "false", - "url": "https://en.wikipedia.org/wiki/Shared_Source_Initiative#Microsoft_Limited_Public_License_(Ms-LPL)", + "url": "https://github.com/gabegundy/atlserver/blob/master/License.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:56Z", + "timestamp": "2024-08-19T17:41:35Z", "isWayBackLink": false, - "order": 2 + "order": 1 }, { "match": "false", - "url": "https://github.com/gabegundy/atlserver/blob/master/License.txt", + "url": "https://www.openhub.net/licenses/mslpl", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:57Z", + "timestamp": "2024-08-19T17:41:35Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { "match": "false", - "url": "https://www.openhub.net/licenses/mslpl", + "url": "https://en.wikipedia.org/wiki/Shared_Source_Initiative#Microsoft_Limited_Public_License_(Ms-LPL)", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:57Z", + "timestamp": "2024-08-19T17:41:36Z", "isWayBackLink": false, - "order": 0 + "order": 2 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/MS-PL.json b/src/main/resources/license-list-data/json/details/MS-PL.json index a4dfd8e0d5..a24bcf592d 100644 --- a/src/main/resources/license-list-data/json/details/MS-PL.json +++ b/src/main/resources/license-list-data/json/details/MS-PL.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/MS-PL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:48Z", + "timestamp": "2024-08-19T17:40:01Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "http://www.microsoft.com/opensource/licenses.mspx", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:27:48Z", + "timestamp": "2024-08-19T17:40:01Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MS-RL.json b/src/main/resources/license-list-data/json/details/MS-RL.json index 574ada8bf2..519aaf27c1 100644 --- a/src/main/resources/license-list-data/json/details/MS-RL.json +++ b/src/main/resources/license-list-data/json/details/MS-RL.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/MS-RL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:17Z", + "timestamp": "2024-08-19T17:35:26Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "http://www.microsoft.com/opensource/licenses.mspx", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:23:17Z", + "timestamp": "2024-08-19T17:36:52Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MTLL.json b/src/main/resources/license-list-data/json/details/MTLL.json index 863d6b2868..64659bcc80 100644 --- a/src/main/resources/license-list-data/json/details/MTLL.json +++ b/src/main/resources/license-list-data/json/details/MTLL.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Matrix_Template_Library_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:20Z", + "timestamp": "2024-08-19T17:48:32Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Mackerras-3-Clause-acknowledgment.json b/src/main/resources/license-list-data/json/details/Mackerras-3-Clause-acknowledgment.json index b5efd4673f..264a9ca87e 100644 --- a/src/main/resources/license-list-data/json/details/Mackerras-3-Clause-acknowledgment.json +++ b/src/main/resources/license-list-data/json/details/Mackerras-3-Clause-acknowledgment.json @@ -11,7 +11,7 @@ "url": "https://github.com/ppp-project/ppp/blob/master/pppd/auth.c#L6-L28", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:00Z", + "timestamp": "2024-08-19T17:36:23Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Mackerras-3-Clause.json b/src/main/resources/license-list-data/json/details/Mackerras-3-Clause.json index ae9b7333b5..a8bf08488c 100644 --- a/src/main/resources/license-list-data/json/details/Mackerras-3-Clause.json +++ b/src/main/resources/license-list-data/json/details/Mackerras-3-Clause.json @@ -11,7 +11,7 @@ "url": "https://github.com/ppp-project/ppp/blob/master/pppd/chap_ms.c#L6-L28", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:42Z", + "timestamp": "2024-08-19T17:45:52Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MakeIndex.json b/src/main/resources/license-list-data/json/details/MakeIndex.json index 10f9f99ece..e63faed028 100644 --- a/src/main/resources/license-list-data/json/details/MakeIndex.json +++ b/src/main/resources/license-list-data/json/details/MakeIndex.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/MakeIndex", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:14Z", + "timestamp": "2024-08-19T17:46:22Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Martin-Birgmeier.json b/src/main/resources/license-list-data/json/details/Martin-Birgmeier.json index f5b7471c82..91948b5518 100644 --- a/src/main/resources/license-list-data/json/details/Martin-Birgmeier.json +++ b/src/main/resources/license-list-data/json/details/Martin-Birgmeier.json @@ -10,7 +10,7 @@ "url": "https://github.com/Perl/perl5/blob/blead/util.c#L6136", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:15Z", + "timestamp": "2024-08-19T17:40:08Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/McPhee-slideshow.json b/src/main/resources/license-list-data/json/details/McPhee-slideshow.json index 055becc839..8844c97a49 100644 --- a/src/main/resources/license-list-data/json/details/McPhee-slideshow.json +++ b/src/main/resources/license-list-data/json/details/McPhee-slideshow.json @@ -10,7 +10,7 @@ "url": "https://mirror.las.iastate.edu/tex-archive/graphics/metapost/contrib/macros/slideshow/slideshow.mp", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:29Z", + "timestamp": "2024-08-19T17:44:38Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Minpack.json b/src/main/resources/license-list-data/json/details/Minpack.json index c84db60f16..7ab5040782 100644 --- a/src/main/resources/license-list-data/json/details/Minpack.json +++ b/src/main/resources/license-list-data/json/details/Minpack.json @@ -7,21 +7,21 @@ "crossRef": [ { "match": "false", - "url": "http://www.netlib.org/minpack/disclaimer", + "url": "https://gitlab.com/libeigen/eigen/-/blob/master/COPYING.MINPACK", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:54Z", + "timestamp": "2024-08-19T17:42:03Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "https://gitlab.com/libeigen/eigen/-/blob/master/COPYING.MINPACK", + "url": "http://www.netlib.org/minpack/disclaimer", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:54Z", + "timestamp": "2024-08-19T17:42:04Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/MirOS.json b/src/main/resources/license-list-data/json/details/MirOS.json index c214c127e8..eb896ea6a3 100644 --- a/src/main/resources/license-list-data/json/details/MirOS.json +++ b/src/main/resources/license-list-data/json/details/MirOS.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/MirOS", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:54Z", + "timestamp": "2024-08-19T17:36:08Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Motosoto.json b/src/main/resources/license-list-data/json/details/Motosoto.json index e6286560c1..821dec9dcd 100644 --- a/src/main/resources/license-list-data/json/details/Motosoto.json +++ b/src/main/resources/license-list-data/json/details/Motosoto.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/Motosoto", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:23Z", + "timestamp": "2024-08-19T17:35:12Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/MulanPSL-1.0.json b/src/main/resources/license-list-data/json/details/MulanPSL-1.0.json index 85f6171a26..fdf731356a 100644 --- a/src/main/resources/license-list-data/json/details/MulanPSL-1.0.json +++ b/src/main/resources/license-list-data/json/details/MulanPSL-1.0.json @@ -13,7 +13,7 @@ "url": "https://license.coscl.org.cn/MulanPSL/", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:19:01Z", + "timestamp": "2024-08-19T17:36:45Z", "isWayBackLink": false, "order": 0 }, @@ -22,7 +22,7 @@ "url": "https://github.com/yuwenlong/longphp/blob/25dfb70cc2a466dc4bb55ba30901cbce08d164b5/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:01Z", + "timestamp": "2024-08-19T17:36:45Z", "isWayBackLink": false, "order": 1 } diff --git a/src/main/resources/license-list-data/json/details/MulanPSL-2.0.json b/src/main/resources/license-list-data/json/details/MulanPSL-2.0.json index 8d38aeea6d..6d60708b95 100644 --- a/src/main/resources/license-list-data/json/details/MulanPSL-2.0.json +++ b/src/main/resources/license-list-data/json/details/MulanPSL-2.0.json @@ -9,11 +9,11 @@ "standardLicenseHeader": "Copyright (c) [Year] [name of copyright holder]\n\n[Software Name] is licensed under Mulan PSL v2.\n\nYou can use this software according to the terms and conditions of the Mulan PSL v2.\n\nYou may obtain a copy of Mulan PSL v2 at:\n\nhttp://license.coscl.org.cn/MulanPSL2\n\nTHIS SOFTWARE IS PROVIDED ON AN \"AS IS\" BASIS, WITHOUT WARRANTIES OF ANY KIND,\n\nEITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,\n\nMERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.\n\nSee the Mulan PSL v2 for more details.\n\n", "crossRef": [ { - "match": "N/A", + "match": "false", "url": "https://license.coscl.org.cn/MulanPSL2", "isValid": true, - "isLive": false, - "timestamp": "2024-05-22T17:27:20Z", + "isLive": true, + "timestamp": "2024-08-19T17:45:05Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Multics.json b/src/main/resources/license-list-data/json/details/Multics.json index 84e7130b22..8be91e4632 100644 --- a/src/main/resources/license-list-data/json/details/Multics.json +++ b/src/main/resources/license-list-data/json/details/Multics.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/Multics", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:24Z", + "timestamp": "2024-08-19T17:42:59Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Mup.json b/src/main/resources/license-list-data/json/details/Mup.json index 87bf7a15bc..470e7d90a6 100644 --- a/src/main/resources/license-list-data/json/details/Mup.json +++ b/src/main/resources/license-list-data/json/details/Mup.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Mup", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:44Z", + "timestamp": "2024-08-19T17:44:49Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NAIST-2003.json b/src/main/resources/license-list-data/json/details/NAIST-2003.json index cbe00d48e0..e23f0b8646 100644 --- a/src/main/resources/license-list-data/json/details/NAIST-2003.json +++ b/src/main/resources/license-list-data/json/details/NAIST-2003.json @@ -7,21 +7,21 @@ "crossRef": [ { "match": "false", - "url": "https://enterprise.dejacode.com/licenses/public/naist-2003/#license-text", + "url": "https://github.com/nodejs/node/blob/4a19cc8947b1bba2b2d27816ec3d0edf9b28e503/LICENSE#L343", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:58Z", + "timestamp": "2024-08-19T17:35:46Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "https://github.com/nodejs/node/blob/4a19cc8947b1bba2b2d27816ec3d0edf9b28e503/LICENSE#L343", + "url": "https://enterprise.dejacode.com/licenses/public/naist-2003/#license-text", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:59Z", + "timestamp": "2024-08-19T17:35:47Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/NASA-1.3.json b/src/main/resources/license-list-data/json/details/NASA-1.3.json index dc79e458b4..61f620bfb0 100644 --- a/src/main/resources/license-list-data/json/details/NASA-1.3.json +++ b/src/main/resources/license-list-data/json/details/NASA-1.3.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/NASA-1.3", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:11Z", + "timestamp": "2024-08-19T17:43:51Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "http://ti.arc.nasa.gov/opensource/nosa/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:11Z", + "timestamp": "2024-08-19T17:43:51Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NBPL-1.0.json b/src/main/resources/license-list-data/json/details/NBPL-1.0.json index 9613d84474..f223a01de9 100644 --- a/src/main/resources/license-list-data/json/details/NBPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/NBPL-1.0.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d37b4b3f6cc4bf34e1d3dec61e69914b9819d8894", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:35Z", + "timestamp": "2024-08-19T17:36:17Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NCBI-PD.json b/src/main/resources/license-list-data/json/details/NCBI-PD.json index 76965019c7..d27e8ea0d2 100644 --- a/src/main/resources/license-list-data/json/details/NCBI-PD.json +++ b/src/main/resources/license-list-data/json/details/NCBI-PD.json @@ -10,45 +10,45 @@ "url": "https://github.com/ncbi/datasets/blob/master/LICENSE.md", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:44Z", + "timestamp": "2024-08-19T17:48:07Z", "isWayBackLink": false, "order": 4 }, { "match": "false", - "url": "https://github.com/ncbi/datasets/blob/0ea4cd16b61e5b799d9cc55aecfa016d6c9bd2bf/LICENSE.md", + "url": "https://github.com/ncbi/egapx/blob/08930b9dec0c69b2d1a05e5153c7b95ef0a3eb0f/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:45Z", + "timestamp": "2024-08-19T17:48:07Z", "isWayBackLink": false, - "order": 1 + "order": 3 }, { "match": "false", "url": "https://github.com/ncbi/sra-tools/blob/e8e5b6af4edc460156ad9ce5902d0779cffbf685/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:45Z", + "timestamp": "2024-08-19T17:48:08Z", "isWayBackLink": false, "order": 0 }, { "match": "false", - "url": "https://github.com/ncbi/egapx/blob/08930b9dec0c69b2d1a05e5153c7b95ef0a3eb0f/LICENSE", + "url": "https://github.com/ncbi/gprobe/blob/de64d30fee8b4c4013094d7d3139ea89b5dd1ace/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:45Z", + "timestamp": "2024-08-19T17:48:08Z", "isWayBackLink": false, - "order": 3 + "order": 2 }, { "match": "false", - "url": "https://github.com/ncbi/gprobe/blob/de64d30fee8b4c4013094d7d3139ea89b5dd1ace/LICENSE", + "url": "https://github.com/ncbi/datasets/blob/0ea4cd16b61e5b799d9cc55aecfa016d6c9bd2bf/LICENSE.md", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:45Z", + "timestamp": "2024-08-19T17:48:08Z", "isWayBackLink": false, - "order": 2 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/NCGL-UK-2.0.json b/src/main/resources/license-list-data/json/details/NCGL-UK-2.0.json index a295662eef..d9fb956e34 100644 --- a/src/main/resources/license-list-data/json/details/NCGL-UK-2.0.json +++ b/src/main/resources/license-list-data/json/details/NCGL-UK-2.0.json @@ -10,7 +10,7 @@ "url": "http://www.nationalarchives.gov.uk/doc/non-commercial-government-licence/version/2/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:52Z", + "timestamp": "2024-08-19T17:35:53Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NCL.json b/src/main/resources/license-list-data/json/details/NCL.json index 119ab2c6fa..cb99c53550 100644 --- a/src/main/resources/license-list-data/json/details/NCL.json +++ b/src/main/resources/license-list-data/json/details/NCL.json @@ -10,7 +10,7 @@ "url": "https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/master/src/modules/module-filter-chain/pffft.c?ref_type\u003dheads#L1-52", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:30Z", + "timestamp": "2024-08-19T17:44:50Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NCSA.json b/src/main/resources/license-list-data/json/details/NCSA.json index 581758690e..df9dbbcd71 100644 --- a/src/main/resources/license-list-data/json/details/NCSA.json +++ b/src/main/resources/license-list-data/json/details/NCSA.json @@ -7,22 +7,22 @@ "licenseId": "NCSA", "crossRef": [ { - "match": "N/A", - "url": "https://opensource.org/licenses/NCSA", + "match": "false", + "url": "http://otm.illinois.edu/uiuc_openSource", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:51Z", + "timestamp": "2024-08-19T17:50:14Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { - "match": "false", - "url": "http://otm.illinois.edu/uiuc_openSource", + "match": "N/A", + "url": "https://opensource.org/licenses/NCSA", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:51Z", + "timestamp": "2024-08-19T17:50:14Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/NGPL.json b/src/main/resources/license-list-data/json/details/NGPL.json index f769753882..5e8fcdec79 100644 --- a/src/main/resources/license-list-data/json/details/NGPL.json +++ b/src/main/resources/license-list-data/json/details/NGPL.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/NGPL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:37Z", + "timestamp": "2024-08-19T17:36:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NICTA-1.0.json b/src/main/resources/license-list-data/json/details/NICTA-1.0.json index e0ea00b396..ac2a284727 100644 --- a/src/main/resources/license-list-data/json/details/NICTA-1.0.json +++ b/src/main/resources/license-list-data/json/details/NICTA-1.0.json @@ -10,7 +10,7 @@ "url": "https://opensource.apple.com/source/mDNSResponder/mDNSResponder-320.10/mDNSPosix/nss_ReadMe.txt", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:22:29Z", + "timestamp": "2024-08-19T17:43:48Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NIST-PD-fallback.json b/src/main/resources/license-list-data/json/details/NIST-PD-fallback.json index b5dbbf95f3..6bca7ba86d 100644 --- a/src/main/resources/license-list-data/json/details/NIST-PD-fallback.json +++ b/src/main/resources/license-list-data/json/details/NIST-PD-fallback.json @@ -10,7 +10,7 @@ "url": "https://github.com/usnistgov/fipy/blob/86aaa5c2ba2c6f1be19593c5986071cf6568cc34/LICENSE.rst", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:34Z", + "timestamp": "2024-08-19T17:37:04Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://github.com/usnistgov/jsip/blob/59700e6926cbe96c5cdae897d9a7d2656b42abe3/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:35Z", + "timestamp": "2024-08-19T17:37:04Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NIST-PD.json b/src/main/resources/license-list-data/json/details/NIST-PD.json index b0ff19807e..8b6e767d7e 100644 --- a/src/main/resources/license-list-data/json/details/NIST-PD.json +++ b/src/main/resources/license-list-data/json/details/NIST-PD.json @@ -7,21 +7,21 @@ "crossRef": [ { "match": "false", - "url": "https://github.com/tcheneau/Routing/blob/f09f46fcfe636107f22f2c98348188a65a135d98/README.md", + "url": "https://github.com/tcheneau/simpleRPL/blob/e645e69e38dd4e3ccfeceb2db8cba05b7c2e0cd3/LICENSE.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:26Z", + "timestamp": "2024-08-19T17:36:38Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { "match": "false", - "url": "https://github.com/tcheneau/simpleRPL/blob/e645e69e38dd4e3ccfeceb2db8cba05b7c2e0cd3/LICENSE.txt", + "url": "https://github.com/tcheneau/Routing/blob/f09f46fcfe636107f22f2c98348188a65a135d98/README.md", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:26Z", + "timestamp": "2024-08-19T17:36:38Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/NIST-Software.json b/src/main/resources/license-list-data/json/details/NIST-Software.json index c5c20347e4..8882c5249e 100644 --- a/src/main/resources/license-list-data/json/details/NIST-Software.json +++ b/src/main/resources/license-list-data/json/details/NIST-Software.json @@ -6,11 +6,11 @@ "licenseId": "NIST-Software", "crossRef": [ { - "match": "false", + "match": "true", "url": "https://github.com/open-quantum-safe/liboqs/blob/40b01fdbb270f8614fde30e65d30e9da18c02393/src/common/rand/rand_nist.c#L1-L15", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:44Z", + "timestamp": "2024-08-19T17:37:36Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NLOD-1.0.json b/src/main/resources/license-list-data/json/details/NLOD-1.0.json index 4e9aeefa0d..84d820c79f 100644 --- a/src/main/resources/license-list-data/json/details/NLOD-1.0.json +++ b/src/main/resources/license-list-data/json/details/NLOD-1.0.json @@ -11,7 +11,7 @@ "url": "http://data.norge.no/nlod/en/1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:02Z", + "timestamp": "2024-08-19T17:38:18Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NLOD-2.0.json b/src/main/resources/license-list-data/json/details/NLOD-2.0.json index 5a469687ae..f0d40ddd9b 100644 --- a/src/main/resources/license-list-data/json/details/NLOD-2.0.json +++ b/src/main/resources/license-list-data/json/details/NLOD-2.0.json @@ -11,7 +11,7 @@ "url": "http://data.norge.no/nlod/en/2.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:11Z", + "timestamp": "2024-08-19T17:46:53Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NLPL.json b/src/main/resources/license-list-data/json/details/NLPL.json index 2b8ef27cd7..8b5ca97b4c 100644 --- a/src/main/resources/license-list-data/json/details/NLPL.json +++ b/src/main/resources/license-list-data/json/details/NLPL.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/NLPL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:45Z", + "timestamp": "2024-08-19T17:40:11Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NOSL.json b/src/main/resources/license-list-data/json/details/NOSL.json index 6dc32fdf0a..001f6cb5a2 100644 --- a/src/main/resources/license-list-data/json/details/NOSL.json +++ b/src/main/resources/license-list-data/json/details/NOSL.json @@ -12,7 +12,7 @@ "url": "http://bits.netizen.com.au/licenses/NOSL/nosl.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:11Z", + "timestamp": "2024-08-19T17:37:38Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NPL-1.0.json b/src/main/resources/license-list-data/json/details/NPL-1.0.json index 61ced9836d..fc50ccc818 100644 --- a/src/main/resources/license-list-data/json/details/NPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/NPL-1.0.json @@ -11,7 +11,7 @@ "url": "http://www.mozilla.org/MPL/NPL/1.0/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:22Z", + "timestamp": "2024-08-19T17:36:51Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NPL-1.1.json b/src/main/resources/license-list-data/json/details/NPL-1.1.json index 79bbe43593..2ebd2e261d 100644 --- a/src/main/resources/license-list-data/json/details/NPL-1.1.json +++ b/src/main/resources/license-list-data/json/details/NPL-1.1.json @@ -11,7 +11,7 @@ "url": "http://www.mozilla.org/MPL/NPL/1.1/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:10Z", + "timestamp": "2024-08-19T17:47:53Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NPOSL-3.0.json b/src/main/resources/license-list-data/json/details/NPOSL-3.0.json index 31513f3f7d..d7d8f910db 100644 --- a/src/main/resources/license-list-data/json/details/NPOSL-3.0.json +++ b/src/main/resources/license-list-data/json/details/NPOSL-3.0.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/NOSL3.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:19:29Z", + "timestamp": "2024-08-19T17:47:26Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NRL.json b/src/main/resources/license-list-data/json/details/NRL.json index 15181e48a6..6ade560a78 100644 --- a/src/main/resources/license-list-data/json/details/NRL.json +++ b/src/main/resources/license-list-data/json/details/NRL.json @@ -10,7 +10,7 @@ "url": "http://web.mit.edu/network/isakmp/nrllicense.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:37Z", + "timestamp": "2024-08-19T17:35:43Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NTP-0.json b/src/main/resources/license-list-data/json/details/NTP-0.json index 66ae97892e..76a13fb946 100644 --- a/src/main/resources/license-list-data/json/details/NTP-0.json +++ b/src/main/resources/license-list-data/json/details/NTP-0.json @@ -11,7 +11,7 @@ "url": "https://github.com/tytso/e2fsprogs/blob/master/lib/et/et_name.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:15Z", + "timestamp": "2024-08-19T17:35:26Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/NTP.json b/src/main/resources/license-list-data/json/details/NTP.json index 04d9444478..a2a1cbdc94 100644 --- a/src/main/resources/license-list-data/json/details/NTP.json +++ b/src/main/resources/license-list-data/json/details/NTP.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/NTP", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:31Z", + "timestamp": "2024-08-19T17:40:47Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Naumen.json b/src/main/resources/license-list-data/json/details/Naumen.json index 4a0355d91f..0cb1b07d00 100644 --- a/src/main/resources/license-list-data/json/details/Naumen.json +++ b/src/main/resources/license-list-data/json/details/Naumen.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/Naumen", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:13Z", + "timestamp": "2024-08-19T17:41:09Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Net-SNMP.json b/src/main/resources/license-list-data/json/details/Net-SNMP.json index b7be2beb53..218d703ee4 100644 --- a/src/main/resources/license-list-data/json/details/Net-SNMP.json +++ b/src/main/resources/license-list-data/json/details/Net-SNMP.json @@ -1,9 +1,9 @@ { - "isDeprecatedLicenseId": false, + "isDeprecatedLicenseId": true, "licenseText": " ---- Part 1: CMU/UCD copyright notice: (BSD like) -----\n\n Copyright 1989, 1991, 1992 by Carnegie Mellon University\n\n Derivative Work - 1996, 1998-2000 Copyright 1996, 1998-2000 The Regents of the University of California\n\n All Rights Reserved\n\nPermission to use, copy, modify and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of CMU and The Regents of the University of California not be used in advertising or publicity pertaining to distribution of the software without specific written permission.\n\nCMU AND THE REGENTS OF THE UNIVERSITY OF CALIFORNIA DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CMU OR THE REGENTS OF THE UNIVERSITY OF CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM THE LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n---- Part 2: Networks Associates Technology, Inc copyright notice (BSD) -----\n\nCopyright (c) 2001-2003, Networks Associates Technology, Inc All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n * Neither the name of the Networks Associates Technology, Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---- Part 3: Cambridge Broadband Ltd. copyright notice (BSD) -----\n\nPortions of this code are copyright (c) 2001-2003, Cambridge Broadband Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n * The name of Cambridge Broadband Ltd. may not be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---- Part 4: Sun Microsystems, Inc. copyright notice (BSD) -----\n\nCopyright © 2003 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A. All rights reserved.\n\nUse is subject to license terms below.\n\nThis distribution may include materials developed by third parties.\n\nSun, Sun Microsystems, the Sun logo and Solaris are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n * Neither the name of the Sun Microsystems, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---- Part 5: Sparta, Inc copyright notice (BSD) -----\n\nCopyright (c) 2003-2009, Sparta, Inc All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n * Neither the name of Sparta, Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---- Part 6: Cisco/BUPTNIC copyright notice (BSD) -----\n\nCopyright (c) 2004, Cisco, Inc and Information Network Center of Beijing University of Posts and Telecommunications. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n * Neither the name of Cisco, Inc, Beijing University of Posts and Telecommunications, nor the names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---- Part 7: Fabasoft R\u0026D Software GmbH \u0026 Co KG copyright notice (BSD) -----\n\nCopyright (c) Fabasoft R\u0026D Software GmbH \u0026 Co KG, 2003 oss@fabasoft.com Author: Bernhard Penz\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n * The name of Fabasoft R\u0026D Software GmbH \u0026 Co KG or any of its subsidiaries, brand or product names may not be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---- Part 8: Apple Inc. copyright notice (BSD) -----\n\nCopyright (c) 2007 Apple Inc. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n 3. Neither the name of Apple Inc. (\"Apple\") nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---- Part 9: ScienceLogic, LLC copyright notice (BSD) -----\n\nCopyright (c) 2009, ScienceLogic, LLC All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n * Neither the name of ScienceLogic, LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n", "standardLicenseTemplate": "---- Part 1: CMU/UCD copyright notice: (BSD like) -----\n\n\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright 1989, 1991, 1992 by Carnegie Mellon University Derivative Work - 1996, 1998-2000 Copyright 1996, 1998-2000 The Regents of the University of California All Rights Reserved \";match\u003d\".{0,5000}\"\u003e\u003e\n\nPermission to use, copy, modify and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of CMU and The Regents of the University of California not be used in advertising or publicity pertaining to distribution of the software without specific written permission.\n\nCMU AND THE REGENTS OF THE UNIVERSITY OF CALIFORNIA DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CMU OR THE REGENTS OF THE UNIVERSITY OF CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM THE LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n---- Part 2: Networks Associates Technology, Inc copyright notice (BSD) -----\n\nCopyright (c) 2001-2003, Networks Associates Technology, Inc All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Neither the name of the Networks Associates Technology, Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n ---- Part 3: Cambridge Broadband Ltd. copyright notice (BSD) -----\n\n Portions of this code are copyright (c) 2001-2003, Cambridge Broadband Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e The name of Cambridge Broadband Ltd. may not be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---- Part 4: Sun Microsystems, Inc. copyright notice (BSD) -----\n\n\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright © 2003 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A. All rights reserved. \";match\u003d\".{0,5000}\"\u003e\u003e\n\nUse is subject to license terms below.\n\nThis distribution may include materials developed by third parties.\n\nSun, Sun Microsystems, the Sun logo and Solaris are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries.\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Neither the name of the Sun Microsystems, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n ---- Part 5: Sparta, Inc copyright notice (BSD) -----\n\n Copyright (c) 2003-2009, Sparta, Inc All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Neither the name of Sparta, Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n ---- Part 6: Cisco/BUPTNIC copyright notice (BSD) -----\n\n Copyright (c) 2004, Cisco, Inc and Information Network Center of Beijing University of Posts and Telecommunications. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Neither the name of Cisco, Inc, Beijing University of Posts and Telecommunications, nor the names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---- Part 7: Fabasoft R\u0026D Software GmbH \u0026 Co KG copyright notice (BSD) -----\n\n\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright (c) Fabasoft R\u0026D Software GmbH \u0026 Co KG, 2003 oss@fabasoft.com Author: Bernhard Penz \";match\u003d\".{0,5000}\"\u003e\u003e\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e The name of Fabasoft R\u0026D Software GmbH \u0026 Co KG or any of its subsidiaries, brand or product names may not be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---- Part 8: Apple Inc. copyright notice (BSD) -----\n\n\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright (c) 2007 Apple Inc. All rights reserved. \";match\u003d\".{0,5000}\"\u003e\u003e\n\nRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1.\";match\u003d\".{0,20}\"\u003e\u003e Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2.\";match\u003d\".{0,20}\"\u003e\u003e Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3.\";match\u003d\".{0,20}\"\u003e\u003e Neither the name of Apple Inc. (\"Apple\") nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\n THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n ---- Part 9: ScienceLogic, LLC copyright notice (BSD) -----\n\n Copyright (c) 2009, ScienceLogic, LLC All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"*\";match\u003d\".{0,20}\"\u003e\u003e Neither the name of ScienceLogic, LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS\u0027\u0027 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n", "name": "Net-SNMP License", - "licenseComments": "This is the overall Net-SNMP license, which is comprised of several licenses which are referred to in totality by the notices in the source files.", + "licenseComments": "DEPRECATED: The Net-SNMP license id represented the license stack of 9 licenses found at https://net-snmp.sourceforge.io/about/license.html and net-snmp-5.6.2. Since then, more licenses have been added. Retaining this license id could consequently cause confusion and all of the licenses in the stack are already on the SPDX License List, thus can be identified with an SPDX license expression.", "licenseId": "Net-SNMP", "crossRef": [ { @@ -11,7 +11,7 @@ "url": "http://net-snmp.sourceforge.net/about/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:41Z", + "timestamp": "2024-08-19T17:43:17Z", "isWayBackLink": false, "order": 0 } @@ -20,5 +20,6 @@ "http://net-snmp.sourceforge.net/about/license.html" ], "isOsiApproved": false, - "licenseTextHtml": "\n \u003cp\u003e---- Part 1: CMU/UCD copyright notice: (BSD like) -----\u003c/p\u003e\n\n \u003cdiv class\u003d\"replaceable-license-text\"\u003e \n \u003cp\u003eCopyright 1989, 1991, 1992 by Carnegie Mellon University\u003c/p\u003e\n\n \u003cp\u003eDerivative Work - 1996, 1998-2000 Copyright 1996, 1998-2000 The\n Regents of the University of California\u003c/p\u003e\n\n \u003cp\u003eAll Rights Reserved\u003c/p\u003e\n\n \u003c/div\u003e\n\n \u003cp\u003ePermission to use, copy, modify and distribute this software and\n its documentation for any purpose and without fee is hereby\n granted, provided that the above copyright notice appears in\n all copies and that both that copyright notice and this\n permission notice appear in supporting documentation, and that\n the name of CMU and The Regents of the University of\n California not be used in advertising or publicity pertaining\n to distribution of the software without specific written\n permission.\u003c/p\u003e\n\n \u003cp\u003eCMU AND THE REGENTS OF THE UNIVERSITY OF CALIFORNIA DISCLAIM ALL\n WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED\n WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL\n CMU OR THE REGENTS OF THE UNIVERSITY OF CALIFORNIA BE LIABLE\n FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY\n DAMAGES WHATSOEVER RESULTING FROM THE LOSS OF USE, DATA OR\n PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\n TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE\n OR PERFORMANCE OF THIS SOFTWARE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 2: Networks Associates Technology, Inc copyright notice\n (BSD) -----\u003c/p\u003e\n\n \u003cp\u003eCopyright (c) 2001-2003, Networks Associates Technology, Inc All\n rights reserved. Redistribution and use in source and binary\n forms, with or without modification, are permitted provided\n that the following conditions are met:\u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Neither the name of the Networks Associates Technology,\n Inc nor the names of its contributors may be used to\n endorse or promote products derived from this software\n without specific prior written permission.\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n CONTRIBUTORS ``AS IS\u0026apos;\u0026apos; AND ANY EXPRESS OR IMPLIED\n WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\n PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL\n THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR\n ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,\n OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER\n IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\n USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n POSSIBILITY OF SUCH DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 3: Cambridge Broadband Ltd. copyright notice\n (BSD) -----\u003c/p\u003e\n\n \u003cp\u003ePortions of this code are copyright (c) 2001-2003,\n Cambridge Broadband Ltd. All rights reserved.\n Redistribution and use in source and binary forms,\n with or without modification, are permitted provided\n that the following conditions are met:\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n The name of Cambridge Broadband Ltd. may not be used to\n endorse or promote products derived from this software\n without specific prior written permission.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS\u0026apos;\u0026apos; AND\n ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT,\n INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\n GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\n DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 4: Sun Microsystems, Inc. copyright notice (BSD) -----\u003c/p\u003e\n\n \u003cdiv class\u003d\"replaceable-license-text\"\u003e \n \u003cp\u003eCopyright © 2003 Sun Microsystems, Inc., 4150 Network Circle,\n Santa Clara, California 95054, U.S.A. All rights reserved.\u003c/p\u003e\n\n \u003c/div\u003e\n\n \u003cp\u003eUse is subject to license terms below.\u003c/p\u003e\n\n \u003cp\u003eThis distribution may include materials developed by third parties.\u003c/p\u003e\n\n \u003cp\u003eSun, Sun Microsystems, the Sun logo and Solaris are trademarks or\n registered trademarks of Sun Microsystems, Inc. in the U.S.\n and other countries.\u003c/p\u003e\n\n \u003cp\u003eRedistribution and use in source and binary forms, with or\n without modification, are permitted provided that the\n following conditions are met:\u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Neither the name of the Sun Microsystems, Inc. nor the\n names of its contributors may be used to endorse or\n promote products derived from this software without\n specific prior written permission.\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n CONTRIBUTORS ``AS IS\u0026apos;\u0026apos; AND ANY EXPRESS OR IMPLIED\n WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\n PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL\n THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR\n ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,\n OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER\n IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\n USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n POSSIBILITY OF SUCH DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 5: Sparta, Inc copyright notice (BSD) -----\u003c/p\u003e\n\n \u003cp\u003eCopyright (c) 2003-2009, Sparta, Inc All rights reserved.\n Redistribution and use in source and binary forms,\n with or without modification, are permitted provided\n that the following conditions are met:\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Neither the name of Sparta, Inc nor the names of its\n contributors may be used to endorse or promote\n products derived from this software without specific\n prior written permission.\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n CONTRIBUTORS ``AS IS\u0026apos;\u0026apos; AND ANY EXPRESS OR IMPLIED\n WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\n PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL\n THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR\n ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,\n OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER\n IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\n USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n POSSIBILITY OF SUCH DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 6: Cisco/BUPTNIC copyright notice (BSD) -----\u003c/p\u003e\n\n \u003cp\u003eCopyright (c) 2004, Cisco, Inc and Information Network\n Center of Beijing University of Posts and\n Telecommunications. All rights reserved.\n Redistribution and use in source and binary forms,\n with or without modification, are permitted provided\n that the following conditions are met:\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Neither the name of Cisco, Inc, Beijing University of\n Posts and Telecommunications, nor the names of their\n contributors may be used to endorse or promote\n products derived from this software without specific\n prior written permission.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n CONTRIBUTORS ``AS IS\u0026apos;\u0026apos; AND ANY EXPRESS OR IMPLIED WARRANTIES,\n INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR\n CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 7: Fabasoft R\u0026amp;D Software GmbH \u0026amp; Co KG copyright\n notice (BSD) -----\u003c/p\u003e\n\n \u003cdiv class\u003d\"replaceable-license-text\"\u003e \n \u003cp\u003eCopyright (c) Fabasoft R\u0026amp;D Software GmbH \u0026amp; Co KG, 2003\n oss@fabasoft.com Author: Bernhard Penz\u003c/p\u003e\n\n \u003c/div\u003e\n\n \u003cp\u003eRedistribution and use in source and binary forms, with or\n without modification, are permitted provided that the\n following conditions are met:\u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n The name of Fabasoft R\u0026amp;D Software GmbH \u0026amp; Co KG or\n any of its subsidiaries, brand or product names may\n not be used to endorse or promote products derived\n from this software without specific prior written\n permission.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS\u0026apos;\u0026apos; AND\n ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT,\n INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\n GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\n DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 8: Apple Inc. copyright notice (BSD) -----\u003c/p\u003e\n\n \u003cdiv class\u003d\"replaceable-license-text\"\u003e \n \u003cp\u003eCopyright (c) 2007 Apple Inc. All rights reserved.\u003c/p\u003e\n\n \u003c/div\u003e\n\n \u003cp\u003eRedistribution and use in source and binary forms, with or\n without modification, are permitted provided that the\n following conditions are met:\u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.\u003c/var\u003e\n Neither the name of Apple Inc. (\u0026quot;Apple\u0026quot;) nor the names of\n its contributors may be used to endorse or promote\n products derived from this software without specific\n prior written permission.\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS\n \u0026quot;AS IS\u0026quot; AND ANY EXPRESS OR IMPLIED WARRANTIES,\n INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS\n CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\n DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 9: ScienceLogic, LLC copyright notice (BSD) -----\u003c/p\u003e\n\n \u003cp\u003eCopyright (c) 2009, ScienceLogic, LLC All rights\n reserved. Redistribution and use in source and binary\n forms, with or without modification, are permitted\n provided that the following conditions are met:\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Neither the name of ScienceLogic, LLC nor the names of\n its contributors may be used to endorse or promote\n products derived from this software without specific\n prior written permission.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n CONTRIBUTORS ``AS IS\u0026apos;\u0026apos; AND ANY EXPRESS OR IMPLIED WARRANTIES,\n INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR\n CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\u003c/p\u003e\n\n " + "licenseTextHtml": "\n \u003cp\u003e---- Part 1: CMU/UCD copyright notice: (BSD like) -----\u003c/p\u003e\n\n \u003cdiv class\u003d\"replaceable-license-text\"\u003e \n \u003cp\u003eCopyright 1989, 1991, 1992 by Carnegie Mellon University\u003c/p\u003e\n\n \u003cp\u003eDerivative Work - 1996, 1998-2000 Copyright 1996, 1998-2000 The\n Regents of the University of California\u003c/p\u003e\n\n \u003cp\u003eAll Rights Reserved\u003c/p\u003e\n\n \u003c/div\u003e\n\n \u003cp\u003ePermission to use, copy, modify and distribute this software and\n its documentation for any purpose and without fee is hereby\n granted, provided that the above copyright notice appears in\n all copies and that both that copyright notice and this\n permission notice appear in supporting documentation, and that\n the name of CMU and The Regents of the University of\n California not be used in advertising or publicity pertaining\n to distribution of the software without specific written\n permission.\u003c/p\u003e\n\n \u003cp\u003eCMU AND THE REGENTS OF THE UNIVERSITY OF CALIFORNIA DISCLAIM ALL\n WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED\n WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL\n CMU OR THE REGENTS OF THE UNIVERSITY OF CALIFORNIA BE LIABLE\n FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY\n DAMAGES WHATSOEVER RESULTING FROM THE LOSS OF USE, DATA OR\n PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\n TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE\n OR PERFORMANCE OF THIS SOFTWARE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 2: Networks Associates Technology, Inc copyright notice\n (BSD) -----\u003c/p\u003e\n\n \u003cp\u003eCopyright (c) 2001-2003, Networks Associates Technology, Inc All\n rights reserved. Redistribution and use in source and binary\n forms, with or without modification, are permitted provided\n that the following conditions are met:\u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Neither the name of the Networks Associates Technology,\n Inc nor the names of its contributors may be used to\n endorse or promote products derived from this software\n without specific prior written permission.\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n CONTRIBUTORS ``AS IS\u0026apos;\u0026apos; AND ANY EXPRESS OR IMPLIED\n WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\n PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL\n THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR\n ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,\n OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER\n IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\n USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n POSSIBILITY OF SUCH DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 3: Cambridge Broadband Ltd. copyright notice\n (BSD) -----\u003c/p\u003e\n\n \u003cp\u003ePortions of this code are copyright (c) 2001-2003,\n Cambridge Broadband Ltd. All rights reserved.\n Redistribution and use in source and binary forms,\n with or without modification, are permitted provided\n that the following conditions are met:\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n The name of Cambridge Broadband Ltd. may not be used to\n endorse or promote products derived from this software\n without specific prior written permission.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS\u0026apos;\u0026apos; AND\n ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT,\n INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\n GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\n DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 4: Sun Microsystems, Inc. copyright notice (BSD) -----\u003c/p\u003e\n\n \u003cdiv class\u003d\"replaceable-license-text\"\u003e \n \u003cp\u003eCopyright © 2003 Sun Microsystems, Inc., 4150 Network Circle,\n Santa Clara, California 95054, U.S.A. All rights reserved.\u003c/p\u003e\n\n \u003c/div\u003e\n\n \u003cp\u003eUse is subject to license terms below.\u003c/p\u003e\n\n \u003cp\u003eThis distribution may include materials developed by third parties.\u003c/p\u003e\n\n \u003cp\u003eSun, Sun Microsystems, the Sun logo and Solaris are trademarks or\n registered trademarks of Sun Microsystems, Inc. in the U.S.\n and other countries.\u003c/p\u003e\n\n \u003cp\u003eRedistribution and use in source and binary forms, with or\n without modification, are permitted provided that the\n following conditions are met:\u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Neither the name of the Sun Microsystems, Inc. nor the\n names of its contributors may be used to endorse or\n promote products derived from this software without\n specific prior written permission.\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n CONTRIBUTORS ``AS IS\u0026apos;\u0026apos; AND ANY EXPRESS OR IMPLIED\n WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\n PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL\n THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR\n ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,\n OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER\n IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\n USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n POSSIBILITY OF SUCH DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 5: Sparta, Inc copyright notice (BSD) -----\u003c/p\u003e\n\n \u003cp\u003eCopyright (c) 2003-2009, Sparta, Inc All rights reserved.\n Redistribution and use in source and binary forms,\n with or without modification, are permitted provided\n that the following conditions are met:\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Neither the name of Sparta, Inc nor the names of its\n contributors may be used to endorse or promote\n products derived from this software without specific\n prior written permission.\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n CONTRIBUTORS ``AS IS\u0026apos;\u0026apos; AND ANY EXPRESS OR IMPLIED\n WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\n PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL\n THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR\n ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,\n OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER\n IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\n USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n POSSIBILITY OF SUCH DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 6: Cisco/BUPTNIC copyright notice (BSD) -----\u003c/p\u003e\n\n \u003cp\u003eCopyright (c) 2004, Cisco, Inc and Information Network\n Center of Beijing University of Posts and\n Telecommunications. All rights reserved.\n Redistribution and use in source and binary forms,\n with or without modification, are permitted provided\n that the following conditions are met:\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Neither the name of Cisco, Inc, Beijing University of\n Posts and Telecommunications, nor the names of their\n contributors may be used to endorse or promote\n products derived from this software without specific\n prior written permission.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n CONTRIBUTORS ``AS IS\u0026apos;\u0026apos; AND ANY EXPRESS OR IMPLIED WARRANTIES,\n INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR\n CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 7: Fabasoft R\u0026amp;D Software GmbH \u0026amp; Co KG copyright\n notice (BSD) -----\u003c/p\u003e\n\n \u003cdiv class\u003d\"replaceable-license-text\"\u003e \n \u003cp\u003eCopyright (c) Fabasoft R\u0026amp;D Software GmbH \u0026amp; Co KG, 2003\n oss@fabasoft.com Author: Bernhard Penz\u003c/p\u003e\n\n \u003c/div\u003e\n\n \u003cp\u003eRedistribution and use in source and binary forms, with or\n without modification, are permitted provided that the\n following conditions are met:\u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n The name of Fabasoft R\u0026amp;D Software GmbH \u0026amp; Co KG or\n any of its subsidiaries, brand or product names may\n not be used to endorse or promote products derived\n from this software without specific prior written\n permission.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS\u0026apos;\u0026apos; AND\n ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT,\n INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\n GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\n DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 8: Apple Inc. copyright notice (BSD) -----\u003c/p\u003e\n\n \u003cdiv class\u003d\"replaceable-license-text\"\u003e \n \u003cp\u003eCopyright (c) 2007 Apple Inc. All rights reserved.\u003c/p\u003e\n\n \u003c/div\u003e\n\n \u003cp\u003eRedistribution and use in source and binary forms, with or\n without modification, are permitted provided that the\n following conditions are met:\u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1.\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2.\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3.\u003c/var\u003e\n Neither the name of Apple Inc. (\u0026quot;Apple\u0026quot;) nor the names of\n its contributors may be used to endorse or promote\n products derived from this software without specific\n prior written permission.\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS\n \u0026quot;AS IS\u0026quot; AND ANY EXPRESS OR IMPLIED WARRANTIES,\n INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS\n CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\n DAMAGE.\u003c/p\u003e\n\n \u003cp\u003e---- Part 9: ScienceLogic, LLC copyright notice (BSD) -----\u003c/p\u003e\n\n \u003cp\u003eCopyright (c) 2009, ScienceLogic, LLC All rights\n reserved. Redistribution and use in source and binary\n forms, with or without modification, are permitted\n provided that the following conditions are met:\u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions of source code must retain the above\n copyright notice, this list of conditions and the\n following disclaimer.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Redistributions in binary form must reproduce the above\n copyright notice, this list of conditions and the\n following disclaimer in the documentation and/or other\n materials provided with the distribution.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e *\u003c/var\u003e\n Neither the name of ScienceLogic, LLC nor the names of\n its contributors may be used to endorse or promote\n products derived from this software without specific\n prior written permission.\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n CONTRIBUTORS ``AS IS\u0026apos;\u0026apos; AND ANY EXPRESS OR IMPLIED WARRANTIES,\n INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR\n CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\n EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\u003c/p\u003e\n\n ", + "deprecatedVersion": "3.25.0" } \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/NetCDF.json b/src/main/resources/license-list-data/json/details/NetCDF.json index 15f0290e29..76d23d4915 100644 --- a/src/main/resources/license-list-data/json/details/NetCDF.json +++ b/src/main/resources/license-list-data/json/details/NetCDF.json @@ -10,7 +10,7 @@ "url": "http://www.unidata.ucar.edu/software/netcdf/copyright.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:58Z", + "timestamp": "2024-08-19T17:36:36Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Newsletr.json b/src/main/resources/license-list-data/json/details/Newsletr.json index 999fa0dd6b..9c28a0ac84 100644 --- a/src/main/resources/license-list-data/json/details/Newsletr.json +++ b/src/main/resources/license-list-data/json/details/Newsletr.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Newsletr", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:52Z", + "timestamp": "2024-08-19T17:44:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Nokia.json b/src/main/resources/license-list-data/json/details/Nokia.json index 35ea2fe9ca..e32f296b08 100644 --- a/src/main/resources/license-list-data/json/details/Nokia.json +++ b/src/main/resources/license-list-data/json/details/Nokia.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/nokia", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:38Z", + "timestamp": "2024-08-19T17:36:03Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Noweb.json b/src/main/resources/license-list-data/json/details/Noweb.json index f9d64ec504..99722c9582 100644 --- a/src/main/resources/license-list-data/json/details/Noweb.json +++ b/src/main/resources/license-list-data/json/details/Noweb.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Noweb", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:10Z", + "timestamp": "2024-08-19T17:35:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Nunit.json b/src/main/resources/license-list-data/json/details/Nunit.json index c72fb79098..614795d379 100644 --- a/src/main/resources/license-list-data/json/details/Nunit.json +++ b/src/main/resources/license-list-data/json/details/Nunit.json @@ -11,7 +11,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Nunit", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:47Z", + "timestamp": "2024-08-19T17:35:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/O-UDA-1.0.json b/src/main/resources/license-list-data/json/details/O-UDA-1.0.json index 16a9e58db7..55787d66e9 100644 --- a/src/main/resources/license-list-data/json/details/O-UDA-1.0.json +++ b/src/main/resources/license-list-data/json/details/O-UDA-1.0.json @@ -10,7 +10,7 @@ "url": "https://cdla.dev/open-use-of-data-agreement-v1-0/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:02Z", + "timestamp": "2024-08-19T17:36:13Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://github.com/microsoft/Open-Use-of-Data-Agreement/blob/v1.0/O-UDA-1.0.md", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:02Z", + "timestamp": "2024-08-19T17:36:14Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OAR.json b/src/main/resources/license-list-data/json/details/OAR.json index 0a1af43e55..6f387578b2 100644 --- a/src/main/resources/license-list-data/json/details/OAR.json +++ b/src/main/resources/license-list-data/json/details/OAR.json @@ -10,7 +10,7 @@ "url": "https://sourceware.org/git/?p\u003dnewlib-cygwin.git;a\u003dblob;f\u003dnewlib/libc/string/strsignal.c;hb\u003dHEAD#l35", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:26Z", + "timestamp": "2024-08-19T17:35:37Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OCCT-PL.json b/src/main/resources/license-list-data/json/details/OCCT-PL.json index 6ed59b626f..3544d2c235 100644 --- a/src/main/resources/license-list-data/json/details/OCCT-PL.json +++ b/src/main/resources/license-list-data/json/details/OCCT-PL.json @@ -10,7 +10,7 @@ "url": "http://www.opencascade.com/content/occt-public-license", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:16Z", + "timestamp": "2024-08-19T17:41:25Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OCLC-2.0.json b/src/main/resources/license-list-data/json/details/OCLC-2.0.json index 65c344e715..7b71cc0576 100644 --- a/src/main/resources/license-list-data/json/details/OCLC-2.0.json +++ b/src/main/resources/license-list-data/json/details/OCLC-2.0.json @@ -13,7 +13,7 @@ "url": "https://opensource.org/licenses/OCLC-2.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:19Z", + "timestamp": "2024-08-19T17:43:09Z", "isWayBackLink": false, "order": 1 }, @@ -22,7 +22,7 @@ "url": "http://www.oclc.org/research/activities/software/license/v2final.htm", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:19Z", + "timestamp": "2024-08-19T17:43:10Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ODC-By-1.0.json b/src/main/resources/license-list-data/json/details/ODC-By-1.0.json index 37331105ce..523e5f2801 100644 --- a/src/main/resources/license-list-data/json/details/ODC-By-1.0.json +++ b/src/main/resources/license-list-data/json/details/ODC-By-1.0.json @@ -10,7 +10,7 @@ "url": "https://opendatacommons.org/licenses/by/1.0/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:26Z", + "timestamp": "2024-08-19T17:35:11Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ODbL-1.0.json b/src/main/resources/license-list-data/json/details/ODbL-1.0.json index 55fa7351b2..db85f93599 100644 --- a/src/main/resources/license-list-data/json/details/ODbL-1.0.json +++ b/src/main/resources/license-list-data/json/details/ODbL-1.0.json @@ -8,21 +8,21 @@ "crossRef": [ { "match": "true", - "url": "https://opendatacommons.org/licenses/odbl/1-0/", + "url": "http://www.opendatacommons.org/licenses/odbl/1.0/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:47Z", + "timestamp": "2024-08-19T17:48:16Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { "match": "true", - "url": "http://www.opendatacommons.org/licenses/odbl/1.0/", + "url": "https://opendatacommons.org/licenses/odbl/1-0/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:48Z", + "timestamp": "2024-08-19T17:48:17Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/OFFIS.json b/src/main/resources/license-list-data/json/details/OFFIS.json index da0f5db971..fb386c75c5 100644 --- a/src/main/resources/license-list-data/json/details/OFFIS.json +++ b/src/main/resources/license-list-data/json/details/OFFIS.json @@ -10,7 +10,7 @@ "url": "https://sourceforge.net/p/xmedcon/code/ci/master/tree/libs/dicom/README", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:47Z", + "timestamp": "2024-08-19T17:41:49Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OFL-1.0-RFN.json b/src/main/resources/license-list-data/json/details/OFL-1.0-RFN.json index 9cf0fd8f40..84252ef277 100644 --- a/src/main/resources/license-list-data/json/details/OFL-1.0-RFN.json +++ b/src/main/resources/license-list-data/json/details/OFL-1.0-RFN.json @@ -11,7 +11,7 @@ "url": "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL10_web", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:25Z", + "timestamp": "2024-08-19T17:42:31Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OFL-1.0-no-RFN.json b/src/main/resources/license-list-data/json/details/OFL-1.0-no-RFN.json index 8166543fbb..1c42b65b73 100644 --- a/src/main/resources/license-list-data/json/details/OFL-1.0-no-RFN.json +++ b/src/main/resources/license-list-data/json/details/OFL-1.0-no-RFN.json @@ -11,7 +11,7 @@ "url": "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL10_web", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:25Z", + "timestamp": "2024-08-19T17:46:00Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OFL-1.0.json b/src/main/resources/license-list-data/json/details/OFL-1.0.json index 64d331c2e4..62854c79e5 100644 --- a/src/main/resources/license-list-data/json/details/OFL-1.0.json +++ b/src/main/resources/license-list-data/json/details/OFL-1.0.json @@ -12,7 +12,7 @@ "url": "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL10_web", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:53Z", + "timestamp": "2024-08-19T17:48:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OFL-1.1-RFN.json b/src/main/resources/license-list-data/json/details/OFL-1.1-RFN.json index c3e3b45e83..d756e77d16 100644 --- a/src/main/resources/license-list-data/json/details/OFL-1.1-RFN.json +++ b/src/main/resources/license-list-data/json/details/OFL-1.1-RFN.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/OFL-1.1", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:28:06Z", + "timestamp": "2024-08-19T17:35:33Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL_web", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:06Z", + "timestamp": "2024-08-19T17:35:33Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OFL-1.1-no-RFN.json b/src/main/resources/license-list-data/json/details/OFL-1.1-no-RFN.json index 604a34f71a..7a6129e629 100644 --- a/src/main/resources/license-list-data/json/details/OFL-1.1-no-RFN.json +++ b/src/main/resources/license-list-data/json/details/OFL-1.1-no-RFN.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/OFL-1.1", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:29:35Z", + "timestamp": "2024-08-19T17:35:42Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL_web", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:35Z", + "timestamp": "2024-08-19T17:35:42Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OFL-1.1.json b/src/main/resources/license-list-data/json/details/OFL-1.1.json index c95759899f..c6685bbf4f 100644 --- a/src/main/resources/license-list-data/json/details/OFL-1.1.json +++ b/src/main/resources/license-list-data/json/details/OFL-1.1.json @@ -8,22 +8,22 @@ "licenseId": "OFL-1.1", "crossRef": [ { - "match": "N/A", - "url": "https://opensource.org/licenses/OFL-1.1", + "match": "false", + "url": "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL_web", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:00Z", + "timestamp": "2024-08-19T17:35:08Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { - "match": "false", - "url": "http://scripts.sil.org/cms/scripts/page.php?item_id\u003dOFL_web", + "match": "N/A", + "url": "https://opensource.org/licenses/OFL-1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:01Z", + "timestamp": "2024-08-19T17:35:09Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/OGC-1.0.json b/src/main/resources/license-list-data/json/details/OGC-1.0.json index 637cc5f398..9e22574004 100644 --- a/src/main/resources/license-list-data/json/details/OGC-1.0.json +++ b/src/main/resources/license-list-data/json/details/OGC-1.0.json @@ -10,7 +10,7 @@ "url": "https://www.ogc.org/ogc/software/1.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:19:37Z", + "timestamp": "2024-08-19T17:44:21Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OGDL-Taiwan-1.0.json b/src/main/resources/license-list-data/json/details/OGDL-Taiwan-1.0.json index 23f79561b9..4e5757acb0 100644 --- a/src/main/resources/license-list-data/json/details/OGDL-Taiwan-1.0.json +++ b/src/main/resources/license-list-data/json/details/OGDL-Taiwan-1.0.json @@ -11,7 +11,7 @@ "url": "https://data.gov.tw/license", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:29Z", + "timestamp": "2024-08-19T17:35:13Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OGL-Canada-2.0.json b/src/main/resources/license-list-data/json/details/OGL-Canada-2.0.json index a81c698a8b..7314594f6a 100644 --- a/src/main/resources/license-list-data/json/details/OGL-Canada-2.0.json +++ b/src/main/resources/license-list-data/json/details/OGL-Canada-2.0.json @@ -10,7 +10,7 @@ "url": "https://open.canada.ca/en/open-government-licence-canada", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:21Z", + "timestamp": "2024-08-19T17:35:18Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OGL-UK-1.0.json b/src/main/resources/license-list-data/json/details/OGL-UK-1.0.json index f79222f8d3..269a8823d5 100644 --- a/src/main/resources/license-list-data/json/details/OGL-UK-1.0.json +++ b/src/main/resources/license-list-data/json/details/OGL-UK-1.0.json @@ -10,7 +10,7 @@ "url": "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/1/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:23Z", + "timestamp": "2024-08-19T17:44:12Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OGL-UK-2.0.json b/src/main/resources/license-list-data/json/details/OGL-UK-2.0.json index 58fda5bc75..2076441edf 100644 --- a/src/main/resources/license-list-data/json/details/OGL-UK-2.0.json +++ b/src/main/resources/license-list-data/json/details/OGL-UK-2.0.json @@ -10,7 +10,7 @@ "url": "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/2/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:26Z", + "timestamp": "2024-08-19T17:46:27Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OGL-UK-3.0.json b/src/main/resources/license-list-data/json/details/OGL-UK-3.0.json index a5739f6c75..7e79de364a 100644 --- a/src/main/resources/license-list-data/json/details/OGL-UK-3.0.json +++ b/src/main/resources/license-list-data/json/details/OGL-UK-3.0.json @@ -11,7 +11,7 @@ "url": "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:28Z", + "timestamp": "2024-08-19T17:47:32Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OGTSL.json b/src/main/resources/license-list-data/json/details/OGTSL.json index ae7c7359ad..1822856d60 100644 --- a/src/main/resources/license-list-data/json/details/OGTSL.json +++ b/src/main/resources/license-list-data/json/details/OGTSL.json @@ -6,22 +6,22 @@ "licenseId": "OGTSL", "crossRef": [ { - "match": "false", - "url": "http://www.opengroup.org/testing/downloads/The_Open_Group_TSL.txt", + "match": "N/A", + "url": "https://opensource.org/licenses/OGTSL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:52Z", + "timestamp": "2024-08-19T17:35:35Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { - "match": "N/A", - "url": "https://opensource.org/licenses/OGTSL", + "match": "false", + "url": "http://www.opengroup.org/testing/downloads/The_Open_Group_TSL.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:52Z", + "timestamp": "2024-08-19T17:35:36Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/OLDAP-1.1.json b/src/main/resources/license-list-data/json/details/OLDAP-1.1.json index 1639852376..a48611df2d 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-1.1.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-1.1.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d806557a5ad59804ef3a44d5abfbe91d706b0791f", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:51Z", + "timestamp": "2024-08-19T17:41:51Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-1.2.json b/src/main/resources/license-list-data/json/details/OLDAP-1.2.json index f2b63b1434..298c868da9 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-1.2.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-1.2.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d42b0383c50c299977b5893ee695cf4e486fb0dc7", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:39Z", + "timestamp": "2024-08-19T17:43:49Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-1.3.json b/src/main/resources/license-list-data/json/details/OLDAP-1.3.json index f32413fcdf..f05fa992c3 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-1.3.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-1.3.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003de5f8117f0ce088d0bd7a8e18ddf37eaa40eb09b1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:34Z", + "timestamp": "2024-08-19T17:49:21Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-1.4.json b/src/main/resources/license-list-data/json/details/OLDAP-1.4.json index e1a23337e8..64f884e3ea 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-1.4.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-1.4.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dc9f95c2f3f2ffb5e0ae55fe7388af75547660941", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:56Z", + "timestamp": "2024-08-19T17:35:17Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-2.0.1.json b/src/main/resources/license-list-data/json/details/OLDAP-2.0.1.json index 8b5344a8d4..9ada2c8e57 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-2.0.1.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-2.0.1.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003db6d68acd14e51ca3aab4428bf26522aa74873f0e", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:47Z", + "timestamp": "2024-08-19T17:36:25Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-2.0.json b/src/main/resources/license-list-data/json/details/OLDAP-2.0.json index e323f5df96..969e9dc671 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-2.0.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-2.0.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dcbf50f4e1185a21abd4c0a54d3f4341fe28f36ea", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:19Z", + "timestamp": "2024-08-19T17:45:45Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-2.1.json b/src/main/resources/license-list-data/json/details/OLDAP-2.1.json index ba477fc3c3..8fe686c58b 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-2.1.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-2.1.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003db0d176738e96a0d3b9f85cb51e140a86f21be715", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:41Z", + "timestamp": "2024-08-19T17:39:37Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-2.2.1.json b/src/main/resources/license-list-data/json/details/OLDAP-2.2.1.json index 638e93a331..a4a8eb520d 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-2.2.1.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-2.2.1.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d4bc786f34b50aa301be6f5600f58a980070f481e", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:23Z", + "timestamp": "2024-08-19T17:36:49Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-2.2.2.json b/src/main/resources/license-list-data/json/details/OLDAP-2.2.2.json index 284b14a8f3..67ad2ac605 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-2.2.2.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-2.2.2.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003ddf2cc1e21eb7c160695f5b7cffd6296c151ba188", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:17Z", + "timestamp": "2024-08-19T17:38:40Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-2.2.json b/src/main/resources/license-list-data/json/details/OLDAP-2.2.json index 23fb30370e..07cb1a06b2 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-2.2.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-2.2.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d470b0c18ec67621c85881b2733057fecf4a1acc3", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:34Z", + "timestamp": "2024-08-19T17:39:42Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-2.3.json b/src/main/resources/license-list-data/json/details/OLDAP-2.3.json index ba93f810f8..3a7253bd41 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-2.3.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-2.3.json @@ -12,7 +12,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dd32cf54a32d581ab475d23c810b0a7fbaf8d63c3", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:09Z", + "timestamp": "2024-08-19T17:38:03Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-2.4.json b/src/main/resources/license-list-data/json/details/OLDAP-2.4.json index b78ab6974d..9c88116dbc 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-2.4.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-2.4.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003dcd1284c4a91a8a380d904eee68d1583f989ed386", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:52Z", + "timestamp": "2024-08-19T17:35:40Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-2.5.json b/src/main/resources/license-list-data/json/details/OLDAP-2.5.json index d55aeba826..9b7a3d0a63 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-2.5.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-2.5.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d6852b9d90022e8593c98205413380536b1b5a7cf", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:19Z", + "timestamp": "2024-08-19T17:38:54Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-2.6.json b/src/main/resources/license-list-data/json/details/OLDAP-2.6.json index 8d6e0a9e8e..001e0882d0 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-2.6.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-2.6.json @@ -11,7 +11,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d1cae062821881f41b73012ba816434897abf4205", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:06Z", + "timestamp": "2024-08-19T17:37:52Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-2.7.json b/src/main/resources/license-list-data/json/details/OLDAP-2.7.json index 3680e3d25f..f59fd9ccc6 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-2.7.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-2.7.json @@ -12,7 +12,7 @@ "url": "http://www.openldap.org/devel/gitweb.cgi?p\u003dopenldap.git;a\u003dblob;f\u003dLICENSE;hb\u003d47c2415c1df81556eeb39be6cad458ef87c534a2", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:31Z", + "timestamp": "2024-08-19T17:35:28Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLDAP-2.8.json b/src/main/resources/license-list-data/json/details/OLDAP-2.8.json index fe3a3d789a..bda42e42d0 100644 --- a/src/main/resources/license-list-data/json/details/OLDAP-2.8.json +++ b/src/main/resources/license-list-data/json/details/OLDAP-2.8.json @@ -10,7 +10,7 @@ "url": "http://www.openldap.org/software/release/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:29Z", + "timestamp": "2024-08-19T17:35:27Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OLFL-1.3.json b/src/main/resources/license-list-data/json/details/OLFL-1.3.json index 852537d10f..b518a7ac18 100644 --- a/src/main/resources/license-list-data/json/details/OLFL-1.3.json +++ b/src/main/resources/license-list-data/json/details/OLFL-1.3.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/license/olfl-1-3/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:05Z", + "timestamp": "2024-08-19T17:36:41Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "https://openlogisticsfoundation.org/licenses/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:09Z", + "timestamp": "2024-08-19T17:36:43Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OML.json b/src/main/resources/license-list-data/json/details/OML.json index 60ab3ee7b4..733bfdcea2 100644 --- a/src/main/resources/license-list-data/json/details/OML.json +++ b/src/main/resources/license-list-data/json/details/OML.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Open_Market_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:04Z", + "timestamp": "2024-08-19T17:44:25Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OPL-1.0.json b/src/main/resources/license-list-data/json/details/OPL-1.0.json index 3abef01c17..668b34d0c4 100644 --- a/src/main/resources/license-list-data/json/details/OPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/OPL-1.0.json @@ -6,23 +6,23 @@ "name": "Open Public License v1.0", "licenseId": "OPL-1.0", "crossRef": [ - { - "match": "false", - "url": "https://fedoraproject.org/wiki/Licensing/Open_Public_License", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:18:47Z", - "isWayBackLink": false, - "order": 1 - }, { "match": "N/A", "url": "http://old.koalateam.com/jackaroo/OPL_1_0.TXT", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:47Z", + "timestamp": "2024-08-19T17:35:28Z", "isWayBackLink": false, "order": 0 + }, + { + "match": "false", + "url": "https://fedoraproject.org/wiki/Licensing/Open_Public_License", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:35:29Z", + "isWayBackLink": false, + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/OPL-UK-3.0.json b/src/main/resources/license-list-data/json/details/OPL-UK-3.0.json index c6162506a1..406993e798 100644 --- a/src/main/resources/license-list-data/json/details/OPL-UK-3.0.json +++ b/src/main/resources/license-list-data/json/details/OPL-UK-3.0.json @@ -10,7 +10,7 @@ "url": "https://www.parliament.uk/site-information/copyright-parliament/open-parliament-licence/", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:26:50Z", + "timestamp": "2024-08-19T17:35:12Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OPUBL-1.0.json b/src/main/resources/license-list-data/json/details/OPUBL-1.0.json index 9e1ca52050..ab5db33ac7 100644 --- a/src/main/resources/license-list-data/json/details/OPUBL-1.0.json +++ b/src/main/resources/license-list-data/json/details/OPUBL-1.0.json @@ -11,27 +11,27 @@ "url": "https://www.debian.org/opl", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:45Z", + "timestamp": "2024-08-19T17:37:47Z", "isWayBackLink": false, "order": 1 }, { "match": "false", - "url": "https://www.ctan.org/license/opl", + "url": "http://opencontent.org/openpub/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:46Z", + "timestamp": "2024-08-19T17:37:48Z", "isWayBackLink": false, - "order": 2 + "order": 0 }, { "match": "false", - "url": "http://opencontent.org/openpub/", + "url": "https://www.ctan.org/license/opl", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:46Z", + "timestamp": "2024-08-19T17:37:49Z", "isWayBackLink": false, - "order": 0 + "order": 2 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/OSET-PL-2.1.json b/src/main/resources/license-list-data/json/details/OSET-PL-2.1.json index 3c8a351edb..ccfbfca914 100644 --- a/src/main/resources/license-list-data/json/details/OSET-PL-2.1.json +++ b/src/main/resources/license-list-data/json/details/OSET-PL-2.1.json @@ -5,23 +5,23 @@ "name": "OSET Public License version 2.1", "licenseId": "OSET-PL-2.1", "crossRef": [ - { - "match": "false", - "url": "http://www.osetfoundation.org/public-license", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:28:32Z", - "isWayBackLink": false, - "order": 0 - }, { "match": "N/A", "url": "https://opensource.org/licenses/OPL-2.1", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:28:33Z", + "timestamp": "2024-08-19T17:38:44Z", "isWayBackLink": false, "order": 1 + }, + { + "match": "false", + "url": "http://www.osetfoundation.org/public-license", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:38:45Z", + "isWayBackLink": false, + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/OSL-1.0.json b/src/main/resources/license-list-data/json/details/OSL-1.0.json index 0a587dc571..30ca9f58eb 100644 --- a/src/main/resources/license-list-data/json/details/OSL-1.0.json +++ b/src/main/resources/license-list-data/json/details/OSL-1.0.json @@ -14,7 +14,7 @@ "url": "https://opensource.org/licenses/OSL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:36Z", + "timestamp": "2024-08-19T17:38:43Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OSL-1.1.json b/src/main/resources/license-list-data/json/details/OSL-1.1.json index 7dbdabc9af..8b004bf47f 100644 --- a/src/main/resources/license-list-data/json/details/OSL-1.1.json +++ b/src/main/resources/license-list-data/json/details/OSL-1.1.json @@ -13,7 +13,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/OSL1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:55Z", + "timestamp": "2024-08-19T17:35:42Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OSL-2.0.json b/src/main/resources/license-list-data/json/details/OSL-2.0.json index 6f02e7a5da..6c6719ee6b 100644 --- a/src/main/resources/license-list-data/json/details/OSL-2.0.json +++ b/src/main/resources/license-list-data/json/details/OSL-2.0.json @@ -13,7 +13,7 @@ "url": "http://web.archive.org/web/20041020171434/http://www.rosenlaw.com/osl2.0.html", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:29:04Z", + "timestamp": "2024-08-19T17:42:48Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OSL-2.1.json b/src/main/resources/license-list-data/json/details/OSL-2.1.json index fe3042504f..7855e93ba4 100644 --- a/src/main/resources/license-list-data/json/details/OSL-2.1.json +++ b/src/main/resources/license-list-data/json/details/OSL-2.1.json @@ -9,23 +9,23 @@ "licenseId": "OSL-2.1", "standardLicenseHeader": "Licensed under the Open Software License version 2.1\n\n", "crossRef": [ - { - "match": "N/A", - "url": "https://opensource.org/licenses/OSL-2.1", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:19:11Z", - "isWayBackLink": false, - "order": 1 - }, { "match": "N/A", "url": "http://web.archive.org/web/20050212003940/http://www.rosenlaw.com/osl21.htm", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:19:11Z", + "timestamp": "2024-08-19T17:37:30Z", "isWayBackLink": false, "order": 0 + }, + { + "match": "N/A", + "url": "https://opensource.org/licenses/OSL-2.1", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:37:30Z", + "isWayBackLink": false, + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/OSL-3.0.json b/src/main/resources/license-list-data/json/details/OSL-3.0.json index 72aa7e009d..f3b7a754ca 100644 --- a/src/main/resources/license-list-data/json/details/OSL-3.0.json +++ b/src/main/resources/license-list-data/json/details/OSL-3.0.json @@ -8,23 +8,23 @@ "licenseId": "OSL-3.0", "standardLicenseHeader": "Licensed under the Open Software License version 3.0\n\n", "crossRef": [ - { - "match": "N/A", - "url": "https://opensource.org/licenses/OSL-3.0", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:22:02Z", - "isWayBackLink": false, - "order": 1 - }, { "match": "N/A", "url": "https://web.archive.org/web/20120101081418/http://rosenlaw.com:80/OSL3.0.htm", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:22:02Z", + "timestamp": "2024-08-19T17:42:09Z", "isWayBackLink": false, "order": 0 + }, + { + "match": "N/A", + "url": "https://opensource.org/licenses/OSL-3.0", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:42:09Z", + "isWayBackLink": false, + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/OpenPBS-2.3.json b/src/main/resources/license-list-data/json/details/OpenPBS-2.3.json index 8e61868468..9912f7839f 100644 --- a/src/main/resources/license-list-data/json/details/OpenPBS-2.3.json +++ b/src/main/resources/license-list-data/json/details/OpenPBS-2.3.json @@ -7,21 +7,21 @@ "crossRef": [ { "match": "false", - "url": "https://www.mcs.anl.gov/research/projects/openpbs/PBS_License.txt", + "url": "https://github.com/adaptivecomputing/torque/blob/master/PBS_License.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:32Z", + "timestamp": "2024-08-19T17:36:12Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { "match": "false", - "url": "https://github.com/adaptivecomputing/torque/blob/master/PBS_License.txt", + "url": "https://www.mcs.anl.gov/research/projects/openpbs/PBS_License.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:32Z", + "timestamp": "2024-08-19T17:36:13Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/OpenSSL-standalone.json b/src/main/resources/license-list-data/json/details/OpenSSL-standalone.json index 5bf4e37a8c..e84e69fe09 100644 --- a/src/main/resources/license-list-data/json/details/OpenSSL-standalone.json +++ b/src/main/resources/license-list-data/json/details/OpenSSL-standalone.json @@ -8,21 +8,21 @@ "crossRef": [ { "match": "false", - "url": "https://library.netapp.com/ecm/ecm_download_file/ECMP1196395", + "url": "https://hstechdocs.helpsystems.com/manuals/globalscape/archive/cuteftp6/open_ssl_license_agreement.htm", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:30Z", + "timestamp": "2024-08-19T17:49:37Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "https://hstechdocs.helpsystems.com/manuals/globalscape/archive/cuteftp6/open_ssl_license_agreement.htm", + "url": "https://library.netapp.com/ecm/ecm_download_file/ECMP1196395", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:31Z", + "timestamp": "2024-08-19T17:49:38Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/OpenSSL.json b/src/main/resources/license-list-data/json/details/OpenSSL.json index f9060ac9f6..70c57613fd 100644 --- a/src/main/resources/license-list-data/json/details/OpenSSL.json +++ b/src/main/resources/license-list-data/json/details/OpenSSL.json @@ -12,7 +12,7 @@ "url": "http://www.openssl.org/source/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:16Z", + "timestamp": "2024-08-19T17:42:27Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/OpenVision.json b/src/main/resources/license-list-data/json/details/OpenVision.json index a5ff815aaf..b942ad3e25 100644 --- a/src/main/resources/license-list-data/json/details/OpenVision.json +++ b/src/main/resources/license-list-data/json/details/OpenVision.json @@ -5,21 +5,12 @@ "name": "OpenVision License", "licenseId": "OpenVision", "crossRef": [ - { - "match": "true", - "url": "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L66-L98", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:30:37Z", - "isWayBackLink": false, - "order": 0 - }, { "match": "false", "url": "https://fedoraproject.org/wiki/Licensing:MIT#OpenVision_Variant", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:38Z", + "timestamp": "2024-08-19T17:47:38Z", "isWayBackLink": false, "order": 2 }, @@ -28,9 +19,18 @@ "url": "https://web.mit.edu/kerberos/krb5-1.21/doc/mitK5license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:39Z", + "timestamp": "2024-08-19T17:47:38Z", "isWayBackLink": false, "order": 1 + }, + { + "match": "false", + "url": "https://github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L66-L98", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:47:39Z", + "isWayBackLink": false, + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/PADL.json b/src/main/resources/license-list-data/json/details/PADL.json index 926e327201..93568da67a 100644 --- a/src/main/resources/license-list-data/json/details/PADL.json +++ b/src/main/resources/license-list-data/json/details/PADL.json @@ -11,7 +11,7 @@ "url": "https://git.openldap.org/openldap/openldap/-/blob/master/libraries/libldap/os-local.c?ref_type\u003dheads#L19-23", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:04Z", + "timestamp": "2024-08-19T17:35:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/PDDL-1.0.json b/src/main/resources/license-list-data/json/details/PDDL-1.0.json index 9230b159f9..bc15124af8 100644 --- a/src/main/resources/license-list-data/json/details/PDDL-1.0.json +++ b/src/main/resources/license-list-data/json/details/PDDL-1.0.json @@ -10,7 +10,7 @@ "url": "https://opendatacommons.org/licenses/pddl/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:32Z", + "timestamp": "2024-08-19T17:36:49Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "http://opendatacommons.org/licenses/pddl/1.0/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:32Z", + "timestamp": "2024-08-19T17:36:49Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/PHP-3.0.json b/src/main/resources/license-list-data/json/details/PHP-3.0.json index 4cae037e26..72479ddb88 100644 --- a/src/main/resources/license-list-data/json/details/PHP-3.0.json +++ b/src/main/resources/license-list-data/json/details/PHP-3.0.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/PHP-3.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:48Z", + "timestamp": "2024-08-19T17:47:17Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "http://www.php.net/license/3_0.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:48Z", + "timestamp": "2024-08-19T17:47:18Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/PHP-3.01.json b/src/main/resources/license-list-data/json/details/PHP-3.01.json index 657bc7b01e..844a3c37e7 100644 --- a/src/main/resources/license-list-data/json/details/PHP-3.01.json +++ b/src/main/resources/license-list-data/json/details/PHP-3.01.json @@ -12,7 +12,7 @@ "url": "http://www.php.net/license/3_01.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:19Z", + "timestamp": "2024-08-19T17:47:47Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/PPL.json b/src/main/resources/license-list-data/json/details/PPL.json index acfee05d11..5546921130 100644 --- a/src/main/resources/license-list-data/json/details/PPL.json +++ b/src/main/resources/license-list-data/json/details/PPL.json @@ -12,7 +12,7 @@ "url": "http://www.networkcultures.org/_uploads/%233notebook_telekommunist.pdf", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:34Z", + "timestamp": "2024-08-19T17:42:42Z", "isWayBackLink": false, "order": 1 }, @@ -21,7 +21,7 @@ "url": "https://wiki.p2pfoundation.net/Peer_Production_License", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:30:35Z", + "timestamp": "2024-08-19T17:42:43Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/PSF-2.0.json b/src/main/resources/license-list-data/json/details/PSF-2.0.json index c1dc6a07c6..c5eb931169 100644 --- a/src/main/resources/license-list-data/json/details/PSF-2.0.json +++ b/src/main/resources/license-list-data/json/details/PSF-2.0.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/Python-2.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:41Z", + "timestamp": "2024-08-19T17:35:25Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Parity-6.0.0.json b/src/main/resources/license-list-data/json/details/Parity-6.0.0.json index ca1f218b5b..d63c0dec18 100644 --- a/src/main/resources/license-list-data/json/details/Parity-6.0.0.json +++ b/src/main/resources/license-list-data/json/details/Parity-6.0.0.json @@ -10,7 +10,7 @@ "url": "https://paritylicense.com/versions/6.0.0.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:59Z", + "timestamp": "2024-08-19T17:35:21Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Parity-7.0.0.json b/src/main/resources/license-list-data/json/details/Parity-7.0.0.json index 055b2e2625..e1d5b473da 100644 --- a/src/main/resources/license-list-data/json/details/Parity-7.0.0.json +++ b/src/main/resources/license-list-data/json/details/Parity-7.0.0.json @@ -11,7 +11,7 @@ "url": "https://paritylicense.com/versions/7.0.0.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:23Z", + "timestamp": "2024-08-19T17:43:37Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Pixar.json b/src/main/resources/license-list-data/json/details/Pixar.json index a93f39feee..d46d8984a7 100644 --- a/src/main/resources/license-list-data/json/details/Pixar.json +++ b/src/main/resources/license-list-data/json/details/Pixar.json @@ -13,7 +13,7 @@ "url": "https://graphics.pixar.com/opensubdiv/docs/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:11Z", + "timestamp": "2024-08-19T17:48:57Z", "isWayBackLink": false, "order": 1 }, @@ -22,7 +22,7 @@ "url": "https://github.com/PixarAnimationStudios/OpenSubdiv/raw/v3_5_0/LICENSE.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:11Z", + "timestamp": "2024-08-19T17:48:57Z", "isWayBackLink": false, "order": 0 }, @@ -31,7 +31,7 @@ "url": "https://github.com/PixarAnimationStudios/OpenSubdiv/blob/v3_5_0/opensubdiv/version.cpp#L2-L22", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:12Z", + "timestamp": "2024-08-19T17:48:58Z", "isWayBackLink": false, "order": 2 } diff --git a/src/main/resources/license-list-data/json/details/Plexus.json b/src/main/resources/license-list-data/json/details/Plexus.json index c6979a363d..4cb96e68e7 100644 --- a/src/main/resources/license-list-data/json/details/Plexus.json +++ b/src/main/resources/license-list-data/json/details/Plexus.json @@ -11,7 +11,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Plexus_Classworlds_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:22Z", + "timestamp": "2024-08-19T17:42:23Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/PolyForm-Noncommercial-1.0.0.json b/src/main/resources/license-list-data/json/details/PolyForm-Noncommercial-1.0.0.json index 360cb673c3..5764f2a3e1 100644 --- a/src/main/resources/license-list-data/json/details/PolyForm-Noncommercial-1.0.0.json +++ b/src/main/resources/license-list-data/json/details/PolyForm-Noncommercial-1.0.0.json @@ -11,7 +11,7 @@ "url": "https://polyformproject.org/licenses/noncommercial/1.0.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:41Z", + "timestamp": "2024-08-19T17:46:53Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/PolyForm-Small-Business-1.0.0.json b/src/main/resources/license-list-data/json/details/PolyForm-Small-Business-1.0.0.json index a60ae465e2..923e8fe077 100644 --- a/src/main/resources/license-list-data/json/details/PolyForm-Small-Business-1.0.0.json +++ b/src/main/resources/license-list-data/json/details/PolyForm-Small-Business-1.0.0.json @@ -11,7 +11,7 @@ "url": "https://polyformproject.org/licenses/small-business/1.0.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:08Z", + "timestamp": "2024-08-19T17:35:08Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/PostgreSQL.json b/src/main/resources/license-list-data/json/details/PostgreSQL.json index aec196b16e..003aa24e75 100644 --- a/src/main/resources/license-list-data/json/details/PostgreSQL.json +++ b/src/main/resources/license-list-data/json/details/PostgreSQL.json @@ -6,22 +6,22 @@ "licenseId": "PostgreSQL", "crossRef": [ { - "match": "false", - "url": "http://www.postgresql.org/about/licence", + "match": "N/A", + "url": "https://opensource.org/licenses/PostgreSQL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:33:14Z", + "timestamp": "2024-08-19T17:35:33Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { - "match": "N/A", - "url": "https://opensource.org/licenses/PostgreSQL", + "match": "false", + "url": "http://www.postgresql.org/about/licence", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:33:15Z", + "timestamp": "2024-08-19T17:35:34Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Python-2.0.1.json b/src/main/resources/license-list-data/json/details/Python-2.0.1.json index d9c788b47a..87b791304c 100644 --- a/src/main/resources/license-list-data/json/details/Python-2.0.1.json +++ b/src/main/resources/license-list-data/json/details/Python-2.0.1.json @@ -8,30 +8,30 @@ "crossRef": [ { "match": "false", - "url": "https://github.com/python/cpython/blob/main/LICENSE", + "url": "https://www.python.org/download/releases/2.0.1/license/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:43Z", + "timestamp": "2024-08-19T17:42:39Z", "isWayBackLink": false, - "order": 2 + "order": 0 }, { "match": "false", - "url": "https://docs.python.org/3/license.html", + "url": "https://github.com/python/cpython/blob/main/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:43Z", + "timestamp": "2024-08-19T17:42:40Z", "isWayBackLink": false, - "order": 1 + "order": 2 }, { "match": "false", - "url": "https://www.python.org/download/releases/2.0.1/license/", + "url": "https://docs.python.org/3/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:43Z", + "timestamp": "2024-08-19T17:42:40Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Python-2.0.json b/src/main/resources/license-list-data/json/details/Python-2.0.json index a17905b4e1..26d2a5d425 100644 --- a/src/main/resources/license-list-data/json/details/Python-2.0.json +++ b/src/main/resources/license-list-data/json/details/Python-2.0.json @@ -12,7 +12,7 @@ "url": "https://opensource.org/licenses/Python-2.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:51Z", + "timestamp": "2024-08-19T17:44:13Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/QPL-1.0-INRIA-2004.json b/src/main/resources/license-list-data/json/details/QPL-1.0-INRIA-2004.json index 48968f427f..e51554ccfb 100644 --- a/src/main/resources/license-list-data/json/details/QPL-1.0-INRIA-2004.json +++ b/src/main/resources/license-list-data/json/details/QPL-1.0-INRIA-2004.json @@ -11,7 +11,7 @@ "url": "https://github.com/maranget/hevea/blob/master/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:15Z", + "timestamp": "2024-08-19T17:43:33Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/QPL-1.0.json b/src/main/resources/license-list-data/json/details/QPL-1.0.json index c29b782930..d372cf92d0 100644 --- a/src/main/resources/license-list-data/json/details/QPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/QPL-1.0.json @@ -6,12 +6,21 @@ "name": "Q Public License 1.0", "licenseId": "QPL-1.0", "crossRef": [ + { + "match": "N/A", + "url": "http://doc.qt.nokia.com/3.3/license.html", + "isValid": true, + "isLive": false, + "timestamp": "2024-08-19T17:39:14Z", + "isWayBackLink": false, + "order": 0 + }, { "match": "false", "url": "https://doc.qt.io/archives/3.3/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:00Z", + "timestamp": "2024-08-19T17:39:14Z", "isWayBackLink": false, "order": 2 }, @@ -20,18 +29,9 @@ "url": "https://opensource.org/licenses/QPL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:00Z", + "timestamp": "2024-08-19T17:39:15Z", "isWayBackLink": false, "order": 1 - }, - { - "match": "N/A", - "url": "http://doc.qt.nokia.com/3.3/license.html", - "isValid": true, - "isLive": false, - "timestamp": "2024-05-22T17:25:01Z", - "isWayBackLink": false, - "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Qhull.json b/src/main/resources/license-list-data/json/details/Qhull.json index 46104a89cb..6718ba418f 100644 --- a/src/main/resources/license-list-data/json/details/Qhull.json +++ b/src/main/resources/license-list-data/json/details/Qhull.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Qhull", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:53Z", + "timestamp": "2024-08-19T17:35:48Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/RHeCos-1.1.json b/src/main/resources/license-list-data/json/details/RHeCos-1.1.json index 4f10785061..0d10f254b3 100644 --- a/src/main/resources/license-list-data/json/details/RHeCos-1.1.json +++ b/src/main/resources/license-list-data/json/details/RHeCos-1.1.json @@ -11,7 +11,7 @@ "url": "http://ecos.sourceware.org/old-license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:56Z", + "timestamp": "2024-08-19T17:40:24Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/RPL-1.1.json b/src/main/resources/license-list-data/json/details/RPL-1.1.json index e5b85c8f74..c424fc75c2 100644 --- a/src/main/resources/license-list-data/json/details/RPL-1.1.json +++ b/src/main/resources/license-list-data/json/details/RPL-1.1.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/RPL-1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:14Z", + "timestamp": "2024-08-19T17:40:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/RPL-1.5.json b/src/main/resources/license-list-data/json/details/RPL-1.5.json index 06586ccc07..7c12344f3d 100644 --- a/src/main/resources/license-list-data/json/details/RPL-1.5.json +++ b/src/main/resources/license-list-data/json/details/RPL-1.5.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/RPL-1.5", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:40Z", + "timestamp": "2024-08-19T17:35:38Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/RPSL-1.0.json b/src/main/resources/license-list-data/json/details/RPSL-1.0.json index dc7d1e61bf..2d190847a3 100644 --- a/src/main/resources/license-list-data/json/details/RPSL-1.0.json +++ b/src/main/resources/license-list-data/json/details/RPSL-1.0.json @@ -13,7 +13,7 @@ "url": "https://opensource.org/licenses/RPSL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:48Z", + "timestamp": "2024-08-19T17:45:49Z", "isWayBackLink": false, "order": 1 }, @@ -22,7 +22,7 @@ "url": "https://helixcommunity.org/content/rpsl", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:32:57Z", + "timestamp": "2024-08-19T17:45:49Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/RSA-MD.json b/src/main/resources/license-list-data/json/details/RSA-MD.json index 39694a9737..20d8273f7f 100644 --- a/src/main/resources/license-list-data/json/details/RSA-MD.json +++ b/src/main/resources/license-list-data/json/details/RSA-MD.json @@ -10,7 +10,7 @@ "url": "http://www.faqs.org/rfcs/rfc1321.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:38Z", + "timestamp": "2024-08-19T17:35:59Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/RSCPL.json b/src/main/resources/license-list-data/json/details/RSCPL.json index 0a09a256d1..410bad9c02 100644 --- a/src/main/resources/license-list-data/json/details/RSCPL.json +++ b/src/main/resources/license-list-data/json/details/RSCPL.json @@ -5,23 +5,23 @@ "name": "Ricoh Source Code Public License", "licenseId": "RSCPL", "crossRef": [ - { - "match": "N/A", - "url": "https://opensource.org/licenses/RSCPL", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:20:43Z", - "isWayBackLink": false, - "order": 1 - }, { "match": "N/A", "url": "http://wayback.archive.org/web/20060715140826/http://www.risource.org/RPL/RPL-1.0A.shtml", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:20:43Z", + "timestamp": "2024-08-19T17:41:18Z", "isWayBackLink": false, "order": 0 + }, + { + "match": "N/A", + "url": "https://opensource.org/licenses/RSCPL", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:41:18Z", + "isWayBackLink": false, + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Rdisc.json b/src/main/resources/license-list-data/json/details/Rdisc.json index dfa8804b82..2347f4ed77 100644 --- a/src/main/resources/license-list-data/json/details/Rdisc.json +++ b/src/main/resources/license-list-data/json/details/Rdisc.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Rdisc_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:37Z", + "timestamp": "2024-08-19T17:35:37Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Ruby-pty.json b/src/main/resources/license-list-data/json/details/Ruby-pty.json new file mode 100644 index 0000000000..eefbf10412 --- /dev/null +++ b/src/main/resources/license-list-data/json/details/Ruby-pty.json @@ -0,0 +1,44 @@ +{ + "isDeprecatedLicenseId": false, + "licenseText": "(c) Copyright 1998 by Akinori Ito.\n\nThis software may be redistributed freely for this purpose, in full\nor in part, provided that this entire copyright notice is included\non any copies of this software and applications and derivations thereof.\n\nThis software is provided on an \"as is\" basis, without warranty of any\nkind, either expressed or implied, as to any matter including, but not\nlimited to warranty of fitness of purpose, or merchantability, or\nresults obtained from use of this software.\n", + "standardLicenseTemplate": "\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"(c) Copyright 1998 by Akinori Ito. \";match\u003d\".{0,5000}\"\u003e\u003e\n\nThis software may be redistributed freely for this purpose, in full or in part, provided that this entire copyright notice is included on any copies of this software and applications and derivations thereof.\n\nThis software is provided on an \"as is\" basis, without warranty of any kind, either expressed or implied, as to any matter including, but not limited to warranty of fitness of purpose, or merchantability, or results obtained from use of this software.\n\n", + "name": "Ruby pty extension license", + "licenseComments": "There is a Japanese translation of this license included with some instances of use.", + "licenseId": "Ruby-pty", + "crossRef": [ + { + "match": "false", + "url": "https://github.com/ruby/ruby/commit/0a64817fb80016030c03518fb9459f63c11605ea#diff-fedf217c1ce44bda01f0a678d3ff8b198bed478754d699c527a698ad933979a0", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:43:23Z", + "isWayBackLink": false, + "order": 2 + }, + { + "match": "false", + "url": "https://github.com/ruby/ruby/commit/0a64817fb80016030c03518fb9459f63c11605ea#diff-ef5fa30838d6d0cecad9e675cc50b24628cfe2cb277c346053fafcc36c91c204", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:43:23Z", + "isWayBackLink": false, + "order": 1 + }, + { + "match": "false", + "url": "https://github.com/ruby/ruby/blob/9f6deaa6888a423720b4b127b5314f0ad26cc2e6/ext/pty/pty.c#L775-L786", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:43:24Z", + "isWayBackLink": false, + "order": 0 + } + ], + "seeAlso": [ + "https://github.com/ruby/ruby/blob/9f6deaa6888a423720b4b127b5314f0ad26cc2e6/ext/pty/pty.c#L775-L786", + "https://github.com/ruby/ruby/commit/0a64817fb80016030c03518fb9459f63c11605ea#diff-ef5fa30838d6d0cecad9e675cc50b24628cfe2cb277c346053fafcc36c91c204", + "https://github.com/ruby/ruby/commit/0a64817fb80016030c03518fb9459f63c11605ea#diff-fedf217c1ce44bda01f0a678d3ff8b198bed478754d699c527a698ad933979a0" + ], + "isOsiApproved": false, + "licenseTextHtml": "\n \u003cdiv class\u003d\"replaceable-license-text\"\u003e \n \u003cp\u003e\n (c) Copyright 1998 by Akinori Ito.\n \u003c/p\u003e\n\n \u003c/div\u003e\n \u003cp\u003e\n This software may be redistributed freely for this\n purpose, in full or in part, provided that this entire\n copyright notice is included on any copies of this\n software and applications and derivations thereof.\n \u003c/p\u003e\n\n \u003cp\u003e\n This software is provided on an \u0026quot;as is\u0026quot; basis, without warranty\n of any kind, either expressed or implied, as to any matter\n including, but not limited to warranty of fitness of purpose, or\n merchantability, or results obtained from use of this software.\n \u003c/p\u003e\n\n " +} \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/Ruby.json b/src/main/resources/license-list-data/json/details/Ruby.json index 4264e49026..69fb0a0890 100644 --- a/src/main/resources/license-list-data/json/details/Ruby.json +++ b/src/main/resources/license-list-data/json/details/Ruby.json @@ -12,7 +12,7 @@ "url": "https://www.ruby-lang.org/en/about/license.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:34Z", + "timestamp": "2024-08-19T17:36:39Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SAX-PD-2.0.json b/src/main/resources/license-list-data/json/details/SAX-PD-2.0.json index f3f051d255..b996c586db 100644 --- a/src/main/resources/license-list-data/json/details/SAX-PD-2.0.json +++ b/src/main/resources/license-list-data/json/details/SAX-PD-2.0.json @@ -10,7 +10,7 @@ "url": "http://www.saxproject.org/copying.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:00Z", + "timestamp": "2024-08-19T17:36:49Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SAX-PD.json b/src/main/resources/license-list-data/json/details/SAX-PD.json index 1eff0134a8..be6d8583ef 100644 --- a/src/main/resources/license-list-data/json/details/SAX-PD.json +++ b/src/main/resources/license-list-data/json/details/SAX-PD.json @@ -10,7 +10,7 @@ "url": "http://www.saxproject.org/copying.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:14Z", + "timestamp": "2024-08-19T17:36:47Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SCEA.json b/src/main/resources/license-list-data/json/details/SCEA.json index fdd00f61a5..b044a27b55 100644 --- a/src/main/resources/license-list-data/json/details/SCEA.json +++ b/src/main/resources/license-list-data/json/details/SCEA.json @@ -12,7 +12,7 @@ "url": "http://research.scea.com/scea_shared_source_license.html", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:18:35Z", + "timestamp": "2024-08-19T17:48:53Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SGI-B-1.0.json b/src/main/resources/license-list-data/json/details/SGI-B-1.0.json index 6814e65289..8d5993370c 100644 --- a/src/main/resources/license-list-data/json/details/SGI-B-1.0.json +++ b/src/main/resources/license-list-data/json/details/SGI-B-1.0.json @@ -13,7 +13,7 @@ "url": "http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.1.0.html", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:26:54Z", + "timestamp": "2024-08-19T17:49:30Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SGI-B-1.1.json b/src/main/resources/license-list-data/json/details/SGI-B-1.1.json index ab4582c270..9a44140796 100644 --- a/src/main/resources/license-list-data/json/details/SGI-B-1.1.json +++ b/src/main/resources/license-list-data/json/details/SGI-B-1.1.json @@ -13,7 +13,7 @@ "url": "http://oss.sgi.com/projects/FreeB/", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:26:07Z", + "timestamp": "2024-08-19T17:35:21Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SGI-B-2.0.json b/src/main/resources/license-list-data/json/details/SGI-B-2.0.json index 41616ba4fd..7852ed6a6d 100644 --- a/src/main/resources/license-list-data/json/details/SGI-B-2.0.json +++ b/src/main/resources/license-list-data/json/details/SGI-B-2.0.json @@ -12,7 +12,7 @@ "url": "http://oss.sgi.com/projects/FreeB/SGIFreeSWLicB.2.0.pdf", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:24:50Z", + "timestamp": "2024-08-19T17:36:40Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SGI-OpenGL.json b/src/main/resources/license-list-data/json/details/SGI-OpenGL.json index 2701fc9974..1da377f94c 100644 --- a/src/main/resources/license-list-data/json/details/SGI-OpenGL.json +++ b/src/main/resources/license-list-data/json/details/SGI-OpenGL.json @@ -10,7 +10,7 @@ "url": "https://gitlab.freedesktop.org/mesa/glw/-/blob/master/README?ref_type\u003dheads", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:33Z", + "timestamp": "2024-08-19T17:46:31Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SGP4.json b/src/main/resources/license-list-data/json/details/SGP4.json index 9d3ccbb7b4..47749f0f69 100644 --- a/src/main/resources/license-list-data/json/details/SGP4.json +++ b/src/main/resources/license-list-data/json/details/SGP4.json @@ -10,7 +10,7 @@ "url": "https://celestrak.org/publications/AIAA/2006-6753/faq.php", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:10Z", + "timestamp": "2024-08-19T17:38:24Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SHL-0.5.json b/src/main/resources/license-list-data/json/details/SHL-0.5.json index c03882ee1e..bedf6a9231 100644 --- a/src/main/resources/license-list-data/json/details/SHL-0.5.json +++ b/src/main/resources/license-list-data/json/details/SHL-0.5.json @@ -12,7 +12,7 @@ "url": "https://solderpad.org/licenses/SHL-0.5/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:10Z", + "timestamp": "2024-08-19T17:49:06Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SHL-0.51.json b/src/main/resources/license-list-data/json/details/SHL-0.51.json index 40820e8ae5..d9bb1404af 100644 --- a/src/main/resources/license-list-data/json/details/SHL-0.51.json +++ b/src/main/resources/license-list-data/json/details/SHL-0.51.json @@ -13,7 +13,7 @@ "url": "https://solderpad.org/licenses/SHL-0.51/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:33Z", + "timestamp": "2024-08-19T17:35:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SISSL-1.2.json b/src/main/resources/license-list-data/json/details/SISSL-1.2.json index 38106903d0..974881b4bc 100644 --- a/src/main/resources/license-list-data/json/details/SISSL-1.2.json +++ b/src/main/resources/license-list-data/json/details/SISSL-1.2.json @@ -12,7 +12,7 @@ "url": "http://gridscheduler.sourceforge.net/Gridengine_SISSL_license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:54Z", + "timestamp": "2024-08-19T17:41:10Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SISSL.json b/src/main/resources/license-list-data/json/details/SISSL.json index 8e820ced6d..2338920f9c 100644 --- a/src/main/resources/license-list-data/json/details/SISSL.json +++ b/src/main/resources/license-list-data/json/details/SISSL.json @@ -13,7 +13,7 @@ "url": "http://www.openoffice.org/licenses/sissl_license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:27Z", + "timestamp": "2024-08-19T17:50:23Z", "isWayBackLink": false, "order": 0 }, @@ -22,7 +22,7 @@ "url": "https://opensource.org/licenses/SISSL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:28Z", + "timestamp": "2024-08-19T17:50:23Z", "isWayBackLink": false, "order": 1 } diff --git a/src/main/resources/license-list-data/json/details/SL.json b/src/main/resources/license-list-data/json/details/SL.json index 6183e45b58..5b6e162075 100644 --- a/src/main/resources/license-list-data/json/details/SL.json +++ b/src/main/resources/license-list-data/json/details/SL.json @@ -10,7 +10,7 @@ "url": "https://github.com/mtoyoda/sl/blob/master/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:33:20Z", + "timestamp": "2024-08-19T17:49:32Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SMLNJ.json b/src/main/resources/license-list-data/json/details/SMLNJ.json index 5e0d8edb14..0cb2cf81d0 100644 --- a/src/main/resources/license-list-data/json/details/SMLNJ.json +++ b/src/main/resources/license-list-data/json/details/SMLNJ.json @@ -11,7 +11,7 @@ "url": "https://www.smlnj.org/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:26Z", + "timestamp": "2024-08-19T17:41:41Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SMPPL.json b/src/main/resources/license-list-data/json/details/SMPPL.json index 48c68b7d8a..36de46c67f 100644 --- a/src/main/resources/license-list-data/json/details/SMPPL.json +++ b/src/main/resources/license-list-data/json/details/SMPPL.json @@ -10,7 +10,7 @@ "url": "https://github.com/dcblake/SMP/blob/master/Documentation/License.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:03Z", + "timestamp": "2024-08-19T17:38:59Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SNIA.json b/src/main/resources/license-list-data/json/details/SNIA.json index 75f93e0e19..4f4a27da04 100644 --- a/src/main/resources/license-list-data/json/details/SNIA.json +++ b/src/main/resources/license-list-data/json/details/SNIA.json @@ -11,7 +11,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/SNIA_Public_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:34Z", + "timestamp": "2024-08-19T17:35:13Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SPL-1.0.json b/src/main/resources/license-list-data/json/details/SPL-1.0.json index 6cebb569dd..1d21e0dfdc 100644 --- a/src/main/resources/license-list-data/json/details/SPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/SPL-1.0.json @@ -14,7 +14,7 @@ "url": "https://opensource.org/licenses/SPL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:42Z", + "timestamp": "2024-08-19T17:47:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SSH-OpenSSH.json b/src/main/resources/license-list-data/json/details/SSH-OpenSSH.json index db1cd8385c..da20194316 100644 --- a/src/main/resources/license-list-data/json/details/SSH-OpenSSH.json +++ b/src/main/resources/license-list-data/json/details/SSH-OpenSSH.json @@ -11,7 +11,7 @@ "url": "https://github.com/openssh/openssh-portable/blob/1b11ea7c58cd5c59838b5fa574cd456d6047b2d4/LICENCE#L10", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:05Z", + "timestamp": "2024-08-19T17:45:42Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SSH-short.json b/src/main/resources/license-list-data/json/details/SSH-short.json index a61f6b60b0..4f603f5cee 100644 --- a/src/main/resources/license-list-data/json/details/SSH-short.json +++ b/src/main/resources/license-list-data/json/details/SSH-short.json @@ -6,21 +6,12 @@ "licenseComments": "This is short version of SSH-OpenSSH that appears in some files associated with the original SSH implementation.", "licenseId": "SSH-short", "crossRef": [ - { - "match": "false", - "url": "https://github.com/openssh/openssh-portable/blob/1b11ea7c58cd5c59838b5fa574cd456d6047b2d4/pathnames.h", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:30:03Z", - "isWayBackLink": false, - "order": 0 - }, { "match": "N/A", "url": "https://joinup.ec.europa.eu/svn/lesoll/trunk/italc/lib/src/dsa_key.cpp", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:30:04Z", + "timestamp": "2024-08-19T17:42:19Z", "isWayBackLink": false, "order": 2 }, @@ -29,9 +20,18 @@ "url": "http://web.mit.edu/kolya/.f/root/athena.mit.edu/sipb.mit.edu/project/openssh/OldFiles/src/openssh-2.9.9p2/ssh-add.1", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:30:04Z", + "timestamp": "2024-08-19T17:42:19Z", "isWayBackLink": false, "order": 1 + }, + { + "match": "false", + "url": "https://github.com/openssh/openssh-portable/blob/1b11ea7c58cd5c59838b5fa574cd456d6047b2d4/pathnames.h", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:42:20Z", + "isWayBackLink": false, + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/SSLeay-standalone.json b/src/main/resources/license-list-data/json/details/SSLeay-standalone.json index 58850869f3..8140a0a6f4 100644 --- a/src/main/resources/license-list-data/json/details/SSLeay-standalone.json +++ b/src/main/resources/license-list-data/json/details/SSLeay-standalone.json @@ -11,7 +11,7 @@ "url": "https://www.tq-group.com/filedownloads/files/software-license-conditions/OriginalSSLeay/OriginalSSLeay.pdf", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:06Z", + "timestamp": "2024-08-19T17:41:51Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SSPL-1.0.json b/src/main/resources/license-list-data/json/details/SSPL-1.0.json index 9f54aea9e0..eed288160b 100644 --- a/src/main/resources/license-list-data/json/details/SSPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/SSPL-1.0.json @@ -13,7 +13,7 @@ "url": "https://www.mongodb.com/licensing/server-side-public-license", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:30Z", + "timestamp": "2024-08-19T17:38:09Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SWL.json b/src/main/resources/license-list-data/json/details/SWL.json index e1eae14343..e5141a0247 100644 --- a/src/main/resources/license-list-data/json/details/SWL.json +++ b/src/main/resources/license-list-data/json/details/SWL.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/SWL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:33:32Z", + "timestamp": "2024-08-19T17:39:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Saxpath.json b/src/main/resources/license-list-data/json/details/Saxpath.json index 9b83e7d30b..0a1676f6d8 100644 --- a/src/main/resources/license-list-data/json/details/Saxpath.json +++ b/src/main/resources/license-list-data/json/details/Saxpath.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Saxpath_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:01Z", + "timestamp": "2024-08-19T17:35:25Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Sendmail-8.23.json b/src/main/resources/license-list-data/json/details/Sendmail-8.23.json index 2ccabc6045..a9eda74801 100644 --- a/src/main/resources/license-list-data/json/details/Sendmail-8.23.json +++ b/src/main/resources/license-list-data/json/details/Sendmail-8.23.json @@ -5,23 +5,23 @@ "name": "Sendmail License 8.23", "licenseId": "Sendmail-8.23", "crossRef": [ - { - "match": "N/A", - "url": "https://web.archive.org/web/20181003101040/https://www.proofpoint.com/sites/default/files/sendmail-license.pdf", - "isValid": false, - "isLive": false, - "timestamp": "2024-05-22T17:21:00Z", - "isWayBackLink": false, - "order": 1 - }, { "match": "false", "url": "https://www.proofpoint.com/sites/default/files/sendmail-license.pdf", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:00Z", + "timestamp": "2024-08-19T17:37:07Z", "isWayBackLink": false, "order": 0 + }, + { + "match": "N/A", + "url": "https://web.archive.org/web/20181003101040/https://www.proofpoint.com/sites/default/files/sendmail-license.pdf", + "isValid": false, + "isLive": false, + "timestamp": "2024-08-19T17:37:07Z", + "isWayBackLink": false, + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Sendmail.json b/src/main/resources/license-list-data/json/details/Sendmail.json index 05bda20dda..48f22beed1 100644 --- a/src/main/resources/license-list-data/json/details/Sendmail.json +++ b/src/main/resources/license-list-data/json/details/Sendmail.json @@ -10,7 +10,7 @@ "url": "https://web.archive.org/web/20160322142305/https://www.sendmail.com/pdfs/open_source/sendmail_license.pdf", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:21:34Z", + "timestamp": "2024-08-19T17:35:57Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "http://www.sendmail.com/pdfs/open_source/sendmail_license.pdf", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:34Z", + "timestamp": "2024-08-19T17:35:57Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SimPL-2.0.json b/src/main/resources/license-list-data/json/details/SimPL-2.0.json index 515657ecfa..7bf7ccb74a 100644 --- a/src/main/resources/license-list-data/json/details/SimPL-2.0.json +++ b/src/main/resources/license-list-data/json/details/SimPL-2.0.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/SimPL-2.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:25:47Z", + "timestamp": "2024-08-19T17:49:28Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Sleepycat.json b/src/main/resources/license-list-data/json/details/Sleepycat.json index 0c7c8670be..35581c5c4f 100644 --- a/src/main/resources/license-list-data/json/details/Sleepycat.json +++ b/src/main/resources/license-list-data/json/details/Sleepycat.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/Sleepycat", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:52Z", + "timestamp": "2024-08-19T17:37:23Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Soundex.json b/src/main/resources/license-list-data/json/details/Soundex.json index c4292e4de7..bf757c7e75 100644 --- a/src/main/resources/license-list-data/json/details/Soundex.json +++ b/src/main/resources/license-list-data/json/details/Soundex.json @@ -10,7 +10,7 @@ "url": "https://metacpan.org/release/RJBS/Text-Soundex-3.05/source/Soundex.pm#L3-11", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:56Z", + "timestamp": "2024-08-19T17:35:08Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Spencer-86.json b/src/main/resources/license-list-data/json/details/Spencer-86.json index 5cc2e1ab4d..3dbe8e2744 100644 --- a/src/main/resources/license-list-data/json/details/Spencer-86.json +++ b/src/main/resources/license-list-data/json/details/Spencer-86.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:35Z", + "timestamp": "2024-08-19T17:37:41Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Spencer-94.json b/src/main/resources/license-list-data/json/details/Spencer-94.json index 3ff256b37d..c45a5439ef 100644 --- a/src/main/resources/license-list-data/json/details/Spencer-94.json +++ b/src/main/resources/license-list-data/json/details/Spencer-94.json @@ -7,21 +7,21 @@ "crossRef": [ { "match": "false", - "url": "https://metacpan.org/release/KNOK/File-MMagic-1.30/source/COPYING#L28", + "url": "https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:10Z", + "timestamp": "2024-08-19T17:37:11Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { "match": "false", - "url": "https://fedoraproject.org/wiki/Licensing/Henry_Spencer_Reg-Ex_Library_License", + "url": "https://metacpan.org/release/KNOK/File-MMagic-1.30/source/COPYING#L28", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:10Z", + "timestamp": "2024-08-19T17:37:11Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Spencer-99.json b/src/main/resources/license-list-data/json/details/Spencer-99.json index 8d2b243bf6..1690a5a8f7 100644 --- a/src/main/resources/license-list-data/json/details/Spencer-99.json +++ b/src/main/resources/license-list-data/json/details/Spencer-99.json @@ -10,7 +10,7 @@ "url": "http://www.opensource.apple.com/source/tcl/tcl-5/tcl/generic/regfronts.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:21Z", + "timestamp": "2024-08-19T17:38:07Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/StandardML-NJ.json b/src/main/resources/license-list-data/json/details/StandardML-NJ.json index ca0bfd4557..4cff70c103 100644 --- a/src/main/resources/license-list-data/json/details/StandardML-NJ.json +++ b/src/main/resources/license-list-data/json/details/StandardML-NJ.json @@ -12,7 +12,7 @@ "url": "https://www.smlnj.org/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:57Z", + "timestamp": "2024-08-19T17:35:59Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SugarCRM-1.1.3.json b/src/main/resources/license-list-data/json/details/SugarCRM-1.1.3.json index 31987c1d5a..532c981408 100644 --- a/src/main/resources/license-list-data/json/details/SugarCRM-1.1.3.json +++ b/src/main/resources/license-list-data/json/details/SugarCRM-1.1.3.json @@ -10,7 +10,7 @@ "url": "http://www.sugarcrm.com/crm/SPL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:37Z", + "timestamp": "2024-08-19T17:35:11Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Sun-PPP-2000.json b/src/main/resources/license-list-data/json/details/Sun-PPP-2000.json index f23718eaac..5caf289c7f 100644 --- a/src/main/resources/license-list-data/json/details/Sun-PPP-2000.json +++ b/src/main/resources/license-list-data/json/details/Sun-PPP-2000.json @@ -10,7 +10,7 @@ "url": "https://github.com/ppp-project/ppp/blob/master/modules/ppp_ahdlc.c#L7-L19", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:24Z", + "timestamp": "2024-08-19T17:43:48Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Sun-PPP.json b/src/main/resources/license-list-data/json/details/Sun-PPP.json index a8f9595fa6..8f729f074f 100644 --- a/src/main/resources/license-list-data/json/details/Sun-PPP.json +++ b/src/main/resources/license-list-data/json/details/Sun-PPP.json @@ -10,7 +10,7 @@ "url": "https://github.com/ppp-project/ppp/blob/master/pppd/eap.c#L7-L16", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:17Z", + "timestamp": "2024-08-19T17:38:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/SunPro.json b/src/main/resources/license-list-data/json/details/SunPro.json index 5b98126fda..79217c01fc 100644 --- a/src/main/resources/license-list-data/json/details/SunPro.json +++ b/src/main/resources/license-list-data/json/details/SunPro.json @@ -10,7 +10,7 @@ "url": "https://github.com/freebsd/freebsd-src/blob/main/lib/msun/src/e_lgammal.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:40Z", + "timestamp": "2024-08-19T17:42:21Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://github.com/freebsd/freebsd-src/blob/main/lib/msun/src/e_acosh.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:40Z", + "timestamp": "2024-08-19T17:42:22Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Symlinks.json b/src/main/resources/license-list-data/json/details/Symlinks.json index 9aaf60b02c..b8e65a6306 100644 --- a/src/main/resources/license-list-data/json/details/Symlinks.json +++ b/src/main/resources/license-list-data/json/details/Symlinks.json @@ -10,7 +10,7 @@ "url": "https://www.mail-archive.com/debian-bugs-rc@lists.debian.org/msg11494.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:02Z", + "timestamp": "2024-08-19T17:35:55Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/TAPR-OHL-1.0.json b/src/main/resources/license-list-data/json/details/TAPR-OHL-1.0.json index 4f8a7a4d50..1806639d3a 100644 --- a/src/main/resources/license-list-data/json/details/TAPR-OHL-1.0.json +++ b/src/main/resources/license-list-data/json/details/TAPR-OHL-1.0.json @@ -10,7 +10,7 @@ "url": "https://www.tapr.org/OHL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:51Z", + "timestamp": "2024-08-19T17:38:39Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/TCL.json b/src/main/resources/license-list-data/json/details/TCL.json index 3ed6e64121..5b8e15b418 100644 --- a/src/main/resources/license-list-data/json/details/TCL.json +++ b/src/main/resources/license-list-data/json/details/TCL.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/TCL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:28Z", + "timestamp": "2024-08-19T17:50:01Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "http://www.tcl.tk/software/tcltk/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:28Z", + "timestamp": "2024-08-19T17:50:02Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/TCP-wrappers.json b/src/main/resources/license-list-data/json/details/TCP-wrappers.json index 8c317ffeed..a8378fe20f 100644 --- a/src/main/resources/license-list-data/json/details/TCP-wrappers.json +++ b/src/main/resources/license-list-data/json/details/TCP-wrappers.json @@ -10,7 +10,7 @@ "url": "http://rc.quest.com/topics/openssh/license.php#tcpwrappers", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:30Z", + "timestamp": "2024-08-19T17:37:27Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/TGPPL-1.0.json b/src/main/resources/license-list-data/json/details/TGPPL-1.0.json index effb3ad894..fca61f7cfa 100644 --- a/src/main/resources/license-list-data/json/details/TGPPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/TGPPL-1.0.json @@ -9,21 +9,21 @@ "crossRef": [ { "match": "false", - "url": "https://fedoraproject.org/wiki/Licensing/TGPPL", + "url": "https://tahoe-lafs.org/trac/tahoe-lafs/browser/trunk/COPYING.TGPPL.rst", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:48Z", + "timestamp": "2024-08-19T17:35:34Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "https://tahoe-lafs.org/trac/tahoe-lafs/browser/trunk/COPYING.TGPPL.rst", + "url": "https://fedoraproject.org/wiki/Licensing/TGPPL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:54Z", + "timestamp": "2024-08-19T17:35:43Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/TMate.json b/src/main/resources/license-list-data/json/details/TMate.json index 27836265e9..32e13d906c 100644 --- a/src/main/resources/license-list-data/json/details/TMate.json +++ b/src/main/resources/license-list-data/json/details/TMate.json @@ -10,7 +10,7 @@ "url": "http://svnkit.com/license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:20Z", + "timestamp": "2024-08-19T17:44:34Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/TORQUE-1.1.json b/src/main/resources/license-list-data/json/details/TORQUE-1.1.json index a8ab9199e6..76e04f5004 100644 --- a/src/main/resources/license-list-data/json/details/TORQUE-1.1.json +++ b/src/main/resources/license-list-data/json/details/TORQUE-1.1.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/TORQUEv1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:43Z", + "timestamp": "2024-08-19T17:35:40Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/TOSL.json b/src/main/resources/license-list-data/json/details/TOSL.json index d204360f75..89295ea6fb 100644 --- a/src/main/resources/license-list-data/json/details/TOSL.json +++ b/src/main/resources/license-list-data/json/details/TOSL.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/TOSL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:25Z", + "timestamp": "2024-08-19T17:35:41Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/TPDL.json b/src/main/resources/license-list-data/json/details/TPDL.json index 5c1b57f246..915c8e70b6 100644 --- a/src/main/resources/license-list-data/json/details/TPDL.json +++ b/src/main/resources/license-list-data/json/details/TPDL.json @@ -10,7 +10,7 @@ "url": "https://metacpan.org/pod/Time::ParseDate#LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:45Z", + "timestamp": "2024-08-19T17:35:49Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/TPL-1.0.json b/src/main/resources/license-list-data/json/details/TPL-1.0.json index 40f764cf26..5ed6e89832 100644 --- a/src/main/resources/license-list-data/json/details/TPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/TPL-1.0.json @@ -13,7 +13,7 @@ "url": "https://fedoraproject.org/wiki/Licensing:ThorPublicLicense", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:07Z", + "timestamp": "2024-08-19T17:43:53Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/TTWL.json b/src/main/resources/license-list-data/json/details/TTWL.json index 3591c5e7ed..c058e5db0a 100644 --- a/src/main/resources/license-list-data/json/details/TTWL.json +++ b/src/main/resources/license-list-data/json/details/TTWL.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/TTWL", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:49Z", + "timestamp": "2024-08-19T17:35:16Z", "isWayBackLink": false, "order": 0 }, @@ -19,7 +19,7 @@ "url": "https://github.com/ap/Text-Tabs/blob/master/lib.modern/Text/Tabs.pm#L148", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:49Z", + "timestamp": "2024-08-19T17:35:16Z", "isWayBackLink": false, "order": 1 } diff --git a/src/main/resources/license-list-data/json/details/TTYP0.json b/src/main/resources/license-list-data/json/details/TTYP0.json index 073ca7e8bd..827d83ac20 100644 --- a/src/main/resources/license-list-data/json/details/TTYP0.json +++ b/src/main/resources/license-list-data/json/details/TTYP0.json @@ -10,7 +10,7 @@ "url": "https://people.mpi-inf.mpg.de/~uwe/misc/uw-ttyp0/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:00Z", + "timestamp": "2024-08-19T17:45:54Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/TU-Berlin-1.0.json b/src/main/resources/license-list-data/json/details/TU-Berlin-1.0.json index 879ca466cc..057a059fc2 100644 --- a/src/main/resources/license-list-data/json/details/TU-Berlin-1.0.json +++ b/src/main/resources/license-list-data/json/details/TU-Berlin-1.0.json @@ -10,7 +10,7 @@ "url": "https://github.com/swh/ladspa/blob/7bf6f3799fdba70fda297c2d8fd9f526803d9680/gsm/COPYRIGHT", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:03Z", + "timestamp": "2024-08-19T17:40:24Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/TU-Berlin-2.0.json b/src/main/resources/license-list-data/json/details/TU-Berlin-2.0.json index 6fb5200999..4d36861a18 100644 --- a/src/main/resources/license-list-data/json/details/TU-Berlin-2.0.json +++ b/src/main/resources/license-list-data/json/details/TU-Berlin-2.0.json @@ -10,7 +10,7 @@ "url": "https://github.com/CorsixTH/deps/blob/fd339a9f526d1d9c9f01ccf39e438a015da50035/licences/libgsm.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:45Z", + "timestamp": "2024-08-19T17:37:30Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/TermReadKey.json b/src/main/resources/license-list-data/json/details/TermReadKey.json index c30edf1f44..bbf7b66b27 100644 --- a/src/main/resources/license-list-data/json/details/TermReadKey.json +++ b/src/main/resources/license-list-data/json/details/TermReadKey.json @@ -10,7 +10,7 @@ "url": "https://github.com/jonathanstowe/TermReadKey/blob/master/README#L9-L10", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:08Z", + "timestamp": "2024-08-19T17:35:15Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/UCAR.json b/src/main/resources/license-list-data/json/details/UCAR.json index 683275503c..c6139b0713 100644 --- a/src/main/resources/license-list-data/json/details/UCAR.json +++ b/src/main/resources/license-list-data/json/details/UCAR.json @@ -10,7 +10,7 @@ "url": "https://github.com/Unidata/UDUNITS-2/blob/master/COPYRIGHT", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:48Z", + "timestamp": "2024-08-19T17:42:38Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/UCL-1.0.json b/src/main/resources/license-list-data/json/details/UCL-1.0.json index aa3d95dd78..44ad321306 100644 --- a/src/main/resources/license-list-data/json/details/UCL-1.0.json +++ b/src/main/resources/license-list-data/json/details/UCL-1.0.json @@ -12,7 +12,7 @@ "url": "https://opensource.org/licenses/UCL-1.0", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:33:24Z", + "timestamp": "2024-08-19T17:46:16Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/UMich-Merit.json b/src/main/resources/license-list-data/json/details/UMich-Merit.json index afdf4bb470..7d36c495e2 100644 --- a/src/main/resources/license-list-data/json/details/UMich-Merit.json +++ b/src/main/resources/license-list-data/json/details/UMich-Merit.json @@ -11,7 +11,7 @@ "url": "https://github.com/radcli/radcli/blob/master/COPYRIGHT#L64", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:25Z", + "timestamp": "2024-08-19T17:47:26Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/UPL-1.0.json b/src/main/resources/license-list-data/json/details/UPL-1.0.json index 914aa3a38f..e36bc68f9a 100644 --- a/src/main/resources/license-list-data/json/details/UPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/UPL-1.0.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/UPL", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:28:16Z", + "timestamp": "2024-08-19T17:35:11Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/URT-RLE.json b/src/main/resources/license-list-data/json/details/URT-RLE.json index ca4c4cd92a..135947b000 100644 --- a/src/main/resources/license-list-data/json/details/URT-RLE.json +++ b/src/main/resources/license-list-data/json/details/URT-RLE.json @@ -7,21 +7,21 @@ "crossRef": [ { "match": "false", - "url": "https://sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/pnmtorle.c", + "url": "https://sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/rletopnm.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:46Z", + "timestamp": "2024-08-19T17:36:14Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "https://sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/rletopnm.c", + "url": "https://sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/pnmtorle.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:47Z", + "timestamp": "2024-08-19T17:36:15Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Ubuntu-font-1.0.json b/src/main/resources/license-list-data/json/details/Ubuntu-font-1.0.json new file mode 100644 index 0000000000..44257da95f --- /dev/null +++ b/src/main/resources/license-list-data/json/details/Ubuntu-font-1.0.json @@ -0,0 +1,34 @@ +{ + "isDeprecatedLicenseId": false, + "licenseText": "-------------------------------\nUBUNTU FONT LICENCE Version 1.0\n-------------------------------\n\nPREAMBLE\nThis licence allows the licensed fonts to be used, studied, modified and\nredistributed freely. The fonts, including any derivative works, can be\nbundled, embedded, and redistributed provided the terms of this licence\nare met. The fonts and derivatives, however, cannot be released under\nany other licence. The requirement for fonts to remain under this\nlicence does not require any document created using the fonts or their\nderivatives to be published under this licence, as long as the primary\npurpose of the document is not to be a vehicle for the distribution of\nthe fonts.\n\nDEFINITIONS\n\"Font Software\" refers to the set of files released by the Copyright\nHolder(s) under this licence and clearly marked as such. This may\ninclude source files, build scripts and documentation.\n\n\"Original Version\" refers to the collection of Font Software components\nas received under this licence.\n\n\"Modified Version\" refers to any derivative made by adding to, deleting,\nor substituting -- in part or in whole -- any of the components of the\nOriginal Version, by changing formats or by porting the Font Software to\na new environment.\n\n\"Copyright Holder(s)\" refers to all individuals and companies who have a\ncopyright ownership of the Font Software.\n\n\"Substantially Changed\" refers to Modified Versions which can be easily\nidentified as dissimilar to the Font Software by users of the Font\nSoftware comparing the Original Version with the Modified Version.\n\nTo \"Propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy. Propagation includes copying,\ndistribution (with or without modification and with or without charging\na redistribution fee), making available to the public, and in some\ncountries other activities as well.\n\nPERMISSION \u0026 CONDITIONS\nThis licence does not grant any rights under trademark law and all such\nrights are reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of the Font Software, to propagate the Font Software, subject to\nthe below conditions:\n\n1) Each copy of the Font Software must contain the above copyright\nnotice and this licence. These can be included either as stand-alone\ntext files, human-readable headers or in the appropriate machine-\nreadable metadata fields within text or binary files as long as those\nfields can be easily viewed by the user.\n\n2) The font name complies with the following:\n(a) The Original Version must retain its name, unmodified.\n(b) Modified Versions which are Substantially Changed must be renamed to\navoid use of the name of the Original Version or similar names entirely.\n(c) Modified Versions which are not Substantially Changed must be\nrenamed to both (i) retain the name of the Original Version and (ii) add\nadditional naming elements to distinguish the Modified Version from the\nOriginal Version. The name of such Modified Versions must be the name of\nthe Original Version, with \"derivative X\" where X represents the name of\nthe new work, appended to that name.\n\n3) The name(s) of the Copyright Holder(s) and any contributor to the\nFont Software shall not be used to promote, endorse or advertise any\nModified Version, except (i) as required by this licence, (ii) to\nacknowledge the contribution(s) of the Copyright Holder(s) or (iii) with\ntheir explicit written permission.\n\n4) The Font Software, modified or unmodified, in part or in whole, must\nbe distributed entirely under this licence, and must not be distributed\nunder any other licence. The requirement for fonts to remain under this\nlicence does not affect any document created using the Font Software,\nexcept any version of the Font Software extracted from a document\ncreated using the Font Software may only be distributed under this\nlicence.\n\nTERMINATION\nThis licence becomes null and void if any of the above conditions are\nnot met.\n\nDISCLAIMER\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF\nCOPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER\nDEALINGS IN THE FONT SOFTWARE.\n", + "standardLicenseTemplate": "-------------------------------\n\nUBUNTU FONT LICENCE Version 1.0\n\n-------------------------------\n\nPREAMBLE This licence allows the licensed fonts to be used, studied, modified and redistributed freely. The fonts, including any derivative works, can be bundled, embedded, and redistributed provided the terms of this licence are met. The fonts and derivatives, however, cannot be released under any other licence. The requirement for fonts to remain under this licence does not require any document created using the fonts or their derivatives to be published under this licence, as long as the primary purpose of the document is not to be a vehicle for the distribution of the fonts.\n\nDEFINITIONS \"Font Software\" refers to the set of files released by the Copyright Holder(s) under this licence and clearly marked as such. This may include source files, build scripts and documentation.\n\n\"Original Version\" refers to the collection of Font Software components as received under this licence.\n\n\"Modified Version\" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.\n\n\"Copyright Holder(s)\" refers to all individuals and companies who have a copyright ownership of the Font Software.\n\n\"Substantially Changed\" refers to Modified Versions which can be easily identified as dissimilar to the Font Software by users of the Font Software comparing the Original Version with the Modified Version.\n\nTo \"Propagate\" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification and with or without charging a redistribution fee), making available to the public, and in some countries other activities as well.\n\nPERMISSION \u0026 CONDITIONS This licence does not grant any rights under trademark law and all such rights are reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to propagate the Font Software, subject to the below conditions:\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"1)\";match\u003d\".{0,20}\"\u003e\u003e\n\n Each copy of the Font Software must contain the above copyright notice and this licence. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine- readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"2)\";match\u003d\".{0,20}\"\u003e\u003e\n\n The font name complies with the following: (a) The Original Version must retain its name, unmodified. (b) Modified Versions which are Substantially Changed must be renamed to avoid use of the name of the Original Version or similar names entirely. (c) Modified Versions which are not Substantially Changed must be renamed to both (i) retain the name of the Original Version and (ii) add additional naming elements to distinguish the Modified Version from the Original Version. The name of such Modified Versions must be the name of the Original Version, with \"derivative X\" where X represents the name of the new work, appended to that name.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"3)\";match\u003d\".{0,20}\"\u003e\u003e\n\n The name(s) of the Copyright Holder(s) and any contributor to the Font Software shall not be used to promote, endorse or advertise any Modified Version, except (i) as required by this licence, (ii) to acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with their explicit written permission.\n\n \u003c\u003cvar;name\u003d\"bullet\";original\u003d\"4)\";match\u003d\".{0,20}\"\u003e\u003e\n\n The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this licence, and must not be distributed under any other licence. The requirement for fonts to remain under this licence does not affect any document created using the Font Software, except any version of the Font Software extracted from a document created using the Font Software may only be distributed under this licence.\n\nTERMINATION This licence becomes null and void if any of the above conditions are not met.\n\nDISCLAIMER THE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.\n\n", + "name": "Ubuntu Font Licence v1.0", + "licenseId": "Ubuntu-font-1.0", + "crossRef": [ + { + "match": "false", + "url": "https://assets.ubuntu.com/v1/81e5605d-ubuntu-font-licence-1.0.txt", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:36:37Z", + "isWayBackLink": false, + "order": 1 + }, + { + "match": "false", + "url": "https://ubuntu.com/legal/font-licence", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:36:38Z", + "isWayBackLink": false, + "order": 0 + } + ], + "seeAlso": [ + "https://ubuntu.com/legal/font-licence", + "https://assets.ubuntu.com/v1/81e5605d-ubuntu-font-licence-1.0.txt" + ], + "isOsiApproved": false, + "licenseTextHtml": "\n \u003cp\u003e\n -------------------------------\n \u003c/p\u003e\n\n \u003cp\u003e\n UBUNTU FONT LICENCE Version 1.0\n \u003c/p\u003e\n\n \u003cp\u003e\n -------------------------------\n \u003c/p\u003e\n\n \u003cp\u003e\n PREAMBLE This licence allows the licensed fonts to be used,\n studied, modified and redistributed freely. The fonts,\n including any derivative works, can be bundled, embedded,\n and redistributed provided the terms of this licence are\n met. The fonts and derivatives, however, cannot be released\n under any other licence. The requirement for fonts to remain\n under this licence does not require any document created\n using the fonts or their derivatives to be published under\n this licence, as long as the primary purpose of the document\n is not to be a vehicle for the distribution of the fonts.\n \u003c/p\u003e\n\n \u003cp\u003e\n DEFINITIONS \u0026quot;Font Software\u0026quot; refers to the set of\n files released by the Copyright Holder(s) under this\n licence and clearly marked as such. This may include\n source files, build scripts and documentation.\n \u003c/p\u003e\n\n \u003cp\u003e\n \u0026quot;Original Version\u0026quot; refers to the collection of Font\n Software components as received under this licence.\n \u003c/p\u003e\n\n \u003cp\u003e\n \u0026quot;Modified Version\u0026quot; refers to any derivative made by adding\n to, deleting, or substituting -- in part or in whole --\n any of the components of the Original Version, by changing\n formats or by porting the Font Software to a new environment.\n \u003c/p\u003e\n\n \u003cp\u003e\n \u0026quot;Copyright Holder(s)\u0026quot; refers to all individuals and companies\n who have a copyright ownership of the Font Software.\n \u003c/p\u003e\n\n \u003cp\u003e\n \u0026quot;Substantially Changed\u0026quot; refers to Modified Versions\n which can be easily identified as dissimilar to the\n Font Software by users of the Font Software comparing\n the Original Version with the Modified Version.\n \u003c/p\u003e\n\n \u003cp\u003e\n To \u0026quot;Propagate\u0026quot; a work means to do anything with it that, without\n permission, would make you directly or secondarily liable for\n infringement under applicable copyright law, except executing it\n on a computer or modifying a private copy. Propagation includes\n copying, distribution (with or without modification and with\n or without charging a redistribution fee), making available\n to the public, and in some countries other activities as well.\n \u003c/p\u003e\n\n \u003cp\u003e\n PERMISSION \u0026amp; CONDITIONS This licence does not grant any\n rights under trademark law and all such rights are reserved.\n \u003c/p\u003e\n\n \u003cp\u003e\n Permission is hereby granted, free of charge, to any\n person obtaining a copy of the Font Software, to propagate\n the Font Software, subject to the below conditions:\n \u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 1)\u003c/var\u003e\n \u003cp\u003e\n Each copy of the Font Software must contain the above\n copyright notice and this licence. These can be included\n either as stand-alone text files, human-readable\n headers or in the appropriate machine- readable\n metadata fields within text or binary files as long\n as those fields can be easily viewed by the user.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 2)\u003c/var\u003e\n \u003cp\u003e\n The font name complies with the following: (a) The Original\n Version must retain its name, unmodified. (b) Modified\n Versions which are Substantially Changed must be renamed to\n avoid use of the name of the Original Version or similar names\n entirely. (c) Modified Versions which are not Substantially\n Changed must be renamed to both (i) retain the name of the\n Original Version and (ii) add additional naming elements\n to distinguish the Modified Version from the Original\n Version. The name of such Modified Versions must be the\n name of the Original Version, with \u0026quot;derivative X\u0026quot; where X\n represents the name of the new work, appended to that name.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 3)\u003c/var\u003e\n \u003cp\u003e\n The name(s) of the Copyright Holder(s) and any contributor\n to the Font Software shall not be used to promote,\n endorse or advertise any Modified Version, except\n (i) as required by this licence, (ii) to acknowledge\n the contribution(s) of the Copyright Holder(s)\n or (iii) with their explicit written permission.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e 4)\u003c/var\u003e\n \u003cp\u003e\n The Font Software, modified or unmodified, in part\n or in whole, must be distributed entirely under this\n licence, and must not be distributed under any other\n licence. The requirement for fonts to remain under this\n licence does not affect any document created using the\n Font Software, except any version of the Font Software\n extracted from a document created using the Font\n Software may only be distributed under this licence.\n \u003c/p\u003e\n\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003e\n TERMINATION This licence becomes null and void\n if any of the above conditions are not met.\n \u003c/p\u003e\n\n \u003cp\u003e\n DISCLAIMER THE FONT SOFTWARE IS PROVIDED \u0026quot;AS IS\u0026quot;, WITHOUT\n WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT\n LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,\n TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT\n HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR\n CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT\n OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE\n THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.\n \u003c/p\u003e\n\n ", + "standardLicenseHeaderHtml": "\n " +} \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/Unicode-3.0.json b/src/main/resources/license-list-data/json/details/Unicode-3.0.json index 91e4e4a8b8..2a007fea86 100644 --- a/src/main/resources/license-list-data/json/details/Unicode-3.0.json +++ b/src/main/resources/license-list-data/json/details/Unicode-3.0.json @@ -11,7 +11,7 @@ "url": "https://www.unicode.org/license.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:26Z", + "timestamp": "2024-08-19T17:37:23Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Unicode-DFS-2015.json b/src/main/resources/license-list-data/json/details/Unicode-DFS-2015.json index e9ed292b2e..96689593a0 100644 --- a/src/main/resources/license-list-data/json/details/Unicode-DFS-2015.json +++ b/src/main/resources/license-list-data/json/details/Unicode-DFS-2015.json @@ -10,7 +10,7 @@ "url": "https://web.archive.org/web/20151224134844/http://unicode.org/copyright.html", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:33:26Z", + "timestamp": "2024-08-19T17:39:09Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Unicode-DFS-2016.json b/src/main/resources/license-list-data/json/details/Unicode-DFS-2016.json index c9d3622cb7..d7acba9650 100644 --- a/src/main/resources/license-list-data/json/details/Unicode-DFS-2016.json +++ b/src/main/resources/license-list-data/json/details/Unicode-DFS-2016.json @@ -5,21 +5,12 @@ "name": "Unicode License Agreement - Data Files and Software (2016)", "licenseId": "Unicode-DFS-2016", "crossRef": [ - { - "match": "false", - "url": "https://www.unicode.org/license.txt", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:19:07Z", - "isWayBackLink": false, - "order": 0 - }, { "match": "false", "url": "http://www.unicode.org/copyright.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:07Z", + "timestamp": "2024-08-19T17:43:24Z", "isWayBackLink": false, "order": 2 }, @@ -28,9 +19,18 @@ "url": "http://web.archive.org/web/20160823201924/http://www.unicode.org/copyright.html#License", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:19:07Z", + "timestamp": "2024-08-19T17:43:24Z", "isWayBackLink": false, "order": 1 + }, + { + "match": "false", + "url": "https://www.unicode.org/license.txt", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:43:24Z", + "isWayBackLink": false, + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Unicode-TOU.json b/src/main/resources/license-list-data/json/details/Unicode-TOU.json index 32f1640918..3bb718b5b1 100644 --- a/src/main/resources/license-list-data/json/details/Unicode-TOU.json +++ b/src/main/resources/license-list-data/json/details/Unicode-TOU.json @@ -5,23 +5,23 @@ "name": "Unicode Terms of Use", "licenseId": "Unicode-TOU", "crossRef": [ - { - "match": "false", - "url": "http://www.unicode.org/copyright.html", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:31:36Z", - "isWayBackLink": false, - "order": 1 - }, { "match": "N/A", "url": "http://web.archive.org/web/20140704074106/http://www.unicode.org/copyright.html", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:31:36Z", + "timestamp": "2024-08-19T17:49:22Z", "isWayBackLink": false, "order": 0 + }, + { + "match": "false", + "url": "http://www.unicode.org/copyright.html", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:49:23Z", + "isWayBackLink": false, + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/UnixCrypt.json b/src/main/resources/license-list-data/json/details/UnixCrypt.json index b142b4d95f..e14da70f02 100644 --- a/src/main/resources/license-list-data/json/details/UnixCrypt.json +++ b/src/main/resources/license-list-data/json/details/UnixCrypt.json @@ -5,21 +5,12 @@ "name": "UnixCrypt License", "licenseId": "UnixCrypt", "crossRef": [ - { - "match": "false", - "url": "https://archive.eclipse.org/jetty/8.0.1.v20110908/xref/org/eclipse/jetty/http/security/UnixCrypt.html", - "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:26:18Z", - "isWayBackLink": false, - "order": 2 - }, { "match": "N/A", "url": "https://opensource.apple.com/source/JBoss/JBoss-737/jboss-all/jetty/src/main/org/mortbay/util/UnixCrypt.java.auto.html", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:26:18Z", + "timestamp": "2024-08-19T17:35:29Z", "isWayBackLink": false, "order": 1 }, @@ -28,9 +19,18 @@ "url": "https://foss.heptapod.net/python-libs/passlib/-/blob/branch/stable/LICENSE#L70", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:19Z", + "timestamp": "2024-08-19T17:35:30Z", "isWayBackLink": false, "order": 0 + }, + { + "match": "N/A", + "url": "https://archive.eclipse.org/jetty/8.0.1.v20110908/xref/org/eclipse/jetty/http/security/UnixCrypt.html", + "isValid": true, + "isLive": false, + "timestamp": "2024-08-19T17:35:32Z", + "isWayBackLink": false, + "order": 2 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Unlicense.json b/src/main/resources/license-list-data/json/details/Unlicense.json index fbcc0f5c0f..8675b9b7cf 100644 --- a/src/main/resources/license-list-data/json/details/Unlicense.json +++ b/src/main/resources/license-list-data/json/details/Unlicense.json @@ -12,7 +12,7 @@ "url": "https://unlicense.org/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:59Z", + "timestamp": "2024-08-19T17:37:00Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/VOSTROM.json b/src/main/resources/license-list-data/json/details/VOSTROM.json index 5a2aac646f..f060f11f5f 100644 --- a/src/main/resources/license-list-data/json/details/VOSTROM.json +++ b/src/main/resources/license-list-data/json/details/VOSTROM.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/VOSTROM", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:04Z", + "timestamp": "2024-08-19T17:46:02Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/VSL-1.0.json b/src/main/resources/license-list-data/json/details/VSL-1.0.json index b983eccc22..d286420562 100644 --- a/src/main/resources/license-list-data/json/details/VSL-1.0.json +++ b/src/main/resources/license-list-data/json/details/VSL-1.0.json @@ -10,7 +10,7 @@ "url": "https://opensource.org/licenses/VSL-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:14Z", + "timestamp": "2024-08-19T17:35:42Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Vim.json b/src/main/resources/license-list-data/json/details/Vim.json index b1bdedd562..8b3f3d2443 100644 --- a/src/main/resources/license-list-data/json/details/Vim.json +++ b/src/main/resources/license-list-data/json/details/Vim.json @@ -11,7 +11,7 @@ "url": "http://vimdoc.sourceforge.net/htmldoc/uganda.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:00Z", + "timestamp": "2024-08-19T17:46:10Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/W3C-19980720.json b/src/main/resources/license-list-data/json/details/W3C-19980720.json index aa9962282c..0be756b7ba 100644 --- a/src/main/resources/license-list-data/json/details/W3C-19980720.json +++ b/src/main/resources/license-list-data/json/details/W3C-19980720.json @@ -10,7 +10,7 @@ "url": "http://www.w3.org/Consortium/Legal/copyright-software-19980720.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:32Z", + "timestamp": "2024-08-19T17:49:25Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/W3C-20150513.json b/src/main/resources/license-list-data/json/details/W3C-20150513.json index 7160ed0994..235e753461 100644 --- a/src/main/resources/license-list-data/json/details/W3C-20150513.json +++ b/src/main/resources/license-list-data/json/details/W3C-20150513.json @@ -10,30 +10,30 @@ "crossRef": [ { "match": "false", - "url": "https://www.w3.org/copyright/software-license-2015/", + "url": "https://www.w3.org/copyright/software-license-2023/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:00Z", + "timestamp": "2024-08-19T17:38:56Z", "isWayBackLink": false, - "order": 1 + "order": 2 }, { "match": "false", - "url": "https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document", + "url": "https://www.w3.org/copyright/software-license-2015/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:00Z", + "timestamp": "2024-08-19T17:38:56Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "https://www.w3.org/copyright/software-license-2023/", + "url": "https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:00Z", + "timestamp": "2024-08-19T17:38:56Z", "isWayBackLink": false, - "order": 2 + "order": 0 } ], "seeAlso": [ @@ -41,7 +41,7 @@ "https://www.w3.org/copyright/software-license-2015/", "https://www.w3.org/copyright/software-license-2023/" ], - "isOsiApproved": false, + "isOsiApproved": true, "licenseTextHtml": "\n \u003cp\u003eThis work is being provided by the copyright holders under the\n following license.\u003c/p\u003e\n\n \u003cp\u003eLicense\n \u003cbr /\u003e\n\nBy obtaining and/or copying this work, you (the licensee)\n agree that you have read, understood, and will comply\n with the following terms and conditions.\n \u003c/p\u003e\n\n \u003cp\u003ePermission to copy, modify, and distribute this work, with or\n without modification, for any purpose and without fee or\n royalty is hereby granted, provided that you include the\n following on ALL copies of the work or portions thereof,\n including modifications:\u003c/p\u003e\n\n\u003cul style\u003d\"list-style:none\"\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e •\u003c/var\u003e\n The full text of this NOTICE in a location viewable to\n users of the redistributed or derivative work.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e •\u003c/var\u003e\n Any pre-existing intellectual property disclaimers,\n notices, or terms and conditions. If none exist, the\n W3C Software and Document Short Notice should be\n included.\n \u003c/li\u003e\n \n\u003cli\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e •\u003c/var\u003e\n Notice of any changes or modifications, through a\n copyright statement on the new code or document such\n as \u0026quot;This software or document includes material copied\n from or derived from \u003cvar class\u003d\"replaceable-license-text\"\u003e [title and URI of the W3C\n document]. Copyright (c) [YEAR] W3C® (MIT, ERCIM,\n Keio, Beihang)\u003c/var\u003e.\u0026quot;\n \u003c/li\u003e\n \n\u003c/ul\u003e\n \u003cp\u003eDisclaimers\n \u003cbr /\u003e\n\nTHIS WORK IS PROVIDED \u0026quot;AS IS,\u0026quot; AND COPYRIGHT HOLDERS MAKE\n NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED,\n INCLUDING BUT NOT LIMITED TO, WARRANTIES OF\n MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE\n OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT\n INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS,\n TRADEMARKS OR OTHER RIGHTS.\n \u003c/p\u003e\n\n \u003cp\u003eCOPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT,\n SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE\n SOFTWARE OR DOCUMENT.\u003c/p\u003e\n\n \u003cp\u003eThe name and trademarks of copyright holders may NOT be used in\n advertising or publicity pertaining to the work without\n specific, written prior permission. Title to copyright in this\n work will at all times remain with copyright holders.\u003c/p\u003e\n\n ", "standardLicenseHeaderHtml": "\n \u003cvar class\u003d\"replaceable-license-text\"\u003e [$name_of_software:\n $distribution_URI]\u003c/var\u003e\n\nCopyright (c)\n \u003cvar class\u003d\"replaceable-license-text\"\u003e [$date-of-software]\u003c/var\u003e World Wide Web\n Consortium, (Massachusetts Institute of Technology, European Research\n Consortium for Informatics and Mathematics, Keio University, Beihang).\n All Rights Reserved. This work is distributed under the W3C® Software\n License [1] in the hope that it will be useful, but WITHOUT ANY\n WARRANTY; without even the implied warranty of MERCHANTABILITY or\n FITNESS FOR A PARTICULAR PURPOSE.\n\n \u003cvar class\u003d\"replaceable-license-text\"\u003e [1]\u003c/var\u003e http://www.w3.org/Consortium/Legal/copyright-software\n\n " } \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/W3C.json b/src/main/resources/license-list-data/json/details/W3C.json index 860b65a94c..ecd36af313 100644 --- a/src/main/resources/license-list-data/json/details/W3C.json +++ b/src/main/resources/license-list-data/json/details/W3C.json @@ -14,7 +14,7 @@ "url": "http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:47Z", + "timestamp": "2024-08-19T17:35:11Z", "isWayBackLink": false, "order": 0 }, @@ -23,7 +23,7 @@ "url": "https://opensource.org/licenses/W3C", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:48Z", + "timestamp": "2024-08-19T17:35:11Z", "isWayBackLink": false, "order": 1 } diff --git a/src/main/resources/license-list-data/json/details/WTFPL.json b/src/main/resources/license-list-data/json/details/WTFPL.json index f3974fa62f..f43013e23e 100644 --- a/src/main/resources/license-list-data/json/details/WTFPL.json +++ b/src/main/resources/license-list-data/json/details/WTFPL.json @@ -11,7 +11,7 @@ "url": "http://sam.zoy.org/wtfpl/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:46Z", + "timestamp": "2024-08-19T17:35:38Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "http://www.wtfpl.net/about/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:47Z", + "timestamp": "2024-08-19T17:35:39Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Watcom-1.0.json b/src/main/resources/license-list-data/json/details/Watcom-1.0.json index c8f6f4f9fc..0a3cb2771e 100644 --- a/src/main/resources/license-list-data/json/details/Watcom-1.0.json +++ b/src/main/resources/license-list-data/json/details/Watcom-1.0.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/Watcom-1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:09Z", + "timestamp": "2024-08-19T17:48:35Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Widget-Workshop.json b/src/main/resources/license-list-data/json/details/Widget-Workshop.json index 36ce0f603e..fdc1240070 100644 --- a/src/main/resources/license-list-data/json/details/Widget-Workshop.json +++ b/src/main/resources/license-list-data/json/details/Widget-Workshop.json @@ -10,7 +10,7 @@ "url": "https://github.com/novnc/noVNC/blob/master/core/crypto/des.js#L24", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:33Z", + "timestamp": "2024-08-19T17:37:33Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Wsuipa.json b/src/main/resources/license-list-data/json/details/Wsuipa.json index 1b4bcdab85..2260de0890 100644 --- a/src/main/resources/license-list-data/json/details/Wsuipa.json +++ b/src/main/resources/license-list-data/json/details/Wsuipa.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Wsuipa", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:20Z", + "timestamp": "2024-08-19T17:36:41Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/X11-distribute-modifications-variant.json b/src/main/resources/license-list-data/json/details/X11-distribute-modifications-variant.json index f9f969f95c..3b8a86aac4 100644 --- a/src/main/resources/license-list-data/json/details/X11-distribute-modifications-variant.json +++ b/src/main/resources/license-list-data/json/details/X11-distribute-modifications-variant.json @@ -11,7 +11,7 @@ "url": "https://github.com/mirror/ncurses/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:46Z", + "timestamp": "2024-08-19T17:38:03Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/X11-swapped.json b/src/main/resources/license-list-data/json/details/X11-swapped.json new file mode 100644 index 0000000000..3e7f921b37 --- /dev/null +++ b/src/main/resources/license-list-data/json/details/X11-swapped.json @@ -0,0 +1,24 @@ +{ + "isDeprecatedLicenseId": false, + "licenseText": "Copyright (c) 2008-2010 Derick Eddington. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this software and associated documentation files (the \"Software\"),\nto deal in the Software without restriction, including without limitation\nthe rights to use, copy, modify, merge, publish, distribute, sublicense,\nand/or sell copies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nExcept as contained in this notice, the name(s) of the above copyright\nholders shall not be used in advertising or otherwise to promote the sale,\nuse or other dealings in this Software without prior written authorization.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\nTHE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n", + "standardLicenseTemplate": "\u003c\u003cbeginOptional\u003e\u003eX11 License\n\n\u003c\u003cendOptional\u003e\u003e \u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright (c) \u003cyear\u003e \u003ccopyright holders\u003e \";match\u003d\".{0,5000}\"\u003e\u003e\n\nPermission 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:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nExcept as contained in this notice, the name(s) of \u003c\u003cvar;name\u003d\"nameDisclaimer\";original\u003d\"the above copyright holders\";match\u003d\".+\"\u003e\u003e shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization\u003c\u003cbeginOptional\u003e\u003e from \u003c\u003cvar;name\u003d\"name2Disclaimer\";original\u003d\"the above copyright holders\";match\u003d\".+\"\u003e\u003e\u003c\u003cendOptional\u003e\u003e .\n\nTHE 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 \u003c\u003cvar;name\u003d\"name3Disclaimer\";original\u003d\"THE AUTHORS OR COPYRIGHT HOLDERS\";match\u003d\".+\"\u003e\u003e 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.\n\n", + "name": "X11 swapped final paragraphs", + "licenseComments": "This is same as X11, but the order of the last two paragraphs is swapped.", + "licenseId": "X11-swapped", + "crossRef": [ + { + "match": "false", + "url": "https://github.com/fedeinthemix/chez-srfi/blob/master/srfi/LICENSE", + "isValid": true, + "isLive": true, + "timestamp": "2024-08-19T17:35:09Z", + "isWayBackLink": false, + "order": 0 + } + ], + "seeAlso": [ + "https://github.com/fedeinthemix/chez-srfi/blob/master/srfi/LICENSE" + ], + "isOsiApproved": false, + "licenseTextHtml": "\n \u003cdiv class\u003d\"optional-license-text\"\u003e \n \u003cp\u003eX11 License\u003c/p\u003e\n\n \u003c/div\u003e\n \u003cdiv class\u003d\"replaceable-license-text\"\u003e \n \u003cp\u003eCopyright (c) \u0026lt;year\u0026gt; \u0026lt;copyright holders\u0026gt;\u003c/p\u003e\n\n \u003c/div\u003e\n\n \u003cp\u003ePermission is hereby granted, free of charge, to any person obtaining a\n copy of this software and associated documentation files (the \u0026quot;Software\u0026quot;),\n to deal in the Software without restriction, including without limitation\n the rights to use, copy, modify, merge, publish, distribute, sublicense,\n and/or sell copies of the Software, and to permit persons to whom the\n Software is furnished to do so, subject to the following conditions:\u003c/p\u003e\n\n \u003cp\u003eThe above copyright notice and this permission notice shall be included in\n all copies or substantial portions of the Software.\u003c/p\u003e\n\n \u003cp\u003eExcept as contained in this notice, the name(s) of \u003cvar class\u003d\"replaceable-license-text\"\u003e the above copyright\n holders\u003c/var\u003e shall not be used in advertising or otherwise to promote the sale,\n use or other dealings in this Software without prior written authorization \u003cvar class\u003d\"optional-license-text\"\u003e from \u003cvar class\u003d\"replaceable-license-text\"\u003e the above copyright\n holders\u003c/var\u003e\u003c/var\u003e.\u003c/p\u003e\n\n \u003cp\u003eTHE SOFTWARE IS PROVIDED \u0026quot;AS IS\u0026quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n \u003cvar class\u003d\"replaceable-license-text\"\u003e THE AUTHORS OR COPYRIGHT HOLDERS\u003c/var\u003e BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n DEALINGS IN THE SOFTWARE.\u003c/p\u003e\n\n " +} \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/X11.json b/src/main/resources/license-list-data/json/details/X11.json index 03d3adf8a5..3cf904ed61 100644 --- a/src/main/resources/license-list-data/json/details/X11.json +++ b/src/main/resources/license-list-data/json/details/X11.json @@ -12,7 +12,7 @@ "url": "http://www.xfree86.org/3.3.6/COPYRIGHT2.html#3", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:13Z", + "timestamp": "2024-08-19T17:38:02Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/XFree86-1.1.json b/src/main/resources/license-list-data/json/details/XFree86-1.1.json index 64298c3e80..f0f41424ad 100644 --- a/src/main/resources/license-list-data/json/details/XFree86-1.1.json +++ b/src/main/resources/license-list-data/json/details/XFree86-1.1.json @@ -11,7 +11,7 @@ "url": "http://www.xfree86.org/current/LICENSE4.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:55Z", + "timestamp": "2024-08-19T17:38:53Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/XSkat.json b/src/main/resources/license-list-data/json/details/XSkat.json index 90b21d6fc5..6833745468 100644 --- a/src/main/resources/license-list-data/json/details/XSkat.json +++ b/src/main/resources/license-list-data/json/details/XSkat.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/XSkat_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:26Z", + "timestamp": "2024-08-19T17:45:42Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Xdebug-1.03.json b/src/main/resources/license-list-data/json/details/Xdebug-1.03.json index 9b4c26db22..1ce246ead2 100644 --- a/src/main/resources/license-list-data/json/details/Xdebug-1.03.json +++ b/src/main/resources/license-list-data/json/details/Xdebug-1.03.json @@ -11,7 +11,7 @@ "url": "https://github.com/xdebug/xdebug/blob/master/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:30Z", + "timestamp": "2024-08-19T17:43:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Xerox.json b/src/main/resources/license-list-data/json/details/Xerox.json index e2b0c8160f..aa946e4613 100644 --- a/src/main/resources/license-list-data/json/details/Xerox.json +++ b/src/main/resources/license-list-data/json/details/Xerox.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Xerox", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:12Z", + "timestamp": "2024-08-19T17:41:48Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Xfig.json b/src/main/resources/license-list-data/json/details/Xfig.json index f94ddbd130..15a8a2c011 100644 --- a/src/main/resources/license-list-data/json/details/Xfig.json +++ b/src/main/resources/license-list-data/json/details/Xfig.json @@ -7,30 +7,30 @@ "crossRef": [ { "match": "false", - "url": "https://github.com/Distrotech/transfig/blob/master/transfig/transfig.c", + "url": "https://sourceforge.net/p/mcj/xfig/ci/master/tree/src/Makefile.am", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:46Z", + "timestamp": "2024-08-19T17:35:24Z", "isWayBackLink": false, - "order": 0 + "order": 2 }, { "match": "false", - "url": "https://sourceforge.net/p/mcj/xfig/ci/master/tree/src/Makefile.am", + "url": "https://fedoraproject.org/wiki/Licensing:MIT#Xfig_Variant", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:46Z", + "timestamp": "2024-08-19T17:35:24Z", "isWayBackLink": false, - "order": 2 + "order": 1 }, { "match": "false", - "url": "https://fedoraproject.org/wiki/Licensing:MIT#Xfig_Variant", + "url": "https://github.com/Distrotech/transfig/blob/master/transfig/transfig.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:47Z", + "timestamp": "2024-08-19T17:35:25Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/Xnet.json b/src/main/resources/license-list-data/json/details/Xnet.json index 75f0f10ecb..0fe89be997 100644 --- a/src/main/resources/license-list-data/json/details/Xnet.json +++ b/src/main/resources/license-list-data/json/details/Xnet.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/Xnet", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:37Z", + "timestamp": "2024-08-19T17:49:46Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/YPL-1.0.json b/src/main/resources/license-list-data/json/details/YPL-1.0.json index 7fb6976b80..0a63f219d2 100644 --- a/src/main/resources/license-list-data/json/details/YPL-1.0.json +++ b/src/main/resources/license-list-data/json/details/YPL-1.0.json @@ -10,7 +10,7 @@ "url": "http://www.zimbra.com/license/yahoo_public_license_1.0.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:27Z", + "timestamp": "2024-08-19T17:44:29Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/YPL-1.1.json b/src/main/resources/license-list-data/json/details/YPL-1.1.json index 2cd04f0fe1..c39f82c9c4 100644 --- a/src/main/resources/license-list-data/json/details/YPL-1.1.json +++ b/src/main/resources/license-list-data/json/details/YPL-1.1.json @@ -11,7 +11,7 @@ "url": "http://www.zimbra.com/license/yahoo_public_license_1.1.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:24Z", + "timestamp": "2024-08-19T17:45:44Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ZPL-1.1.json b/src/main/resources/license-list-data/json/details/ZPL-1.1.json index 5d972df6ef..9801f9fcd9 100644 --- a/src/main/resources/license-list-data/json/details/ZPL-1.1.json +++ b/src/main/resources/license-list-data/json/details/ZPL-1.1.json @@ -10,7 +10,7 @@ "url": "http://old.zope.org/Resources/License/ZPL-1.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:28Z", + "timestamp": "2024-08-19T17:48:02Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ZPL-2.0.json b/src/main/resources/license-list-data/json/details/ZPL-2.0.json index e6311671d1..f0c079bfa7 100644 --- a/src/main/resources/license-list-data/json/details/ZPL-2.0.json +++ b/src/main/resources/license-list-data/json/details/ZPL-2.0.json @@ -7,22 +7,22 @@ "licenseId": "ZPL-2.0", "crossRef": [ { - "match": "false", - "url": "http://old.zope.org/Resources/License/ZPL-2.0", + "match": "N/A", + "url": "https://opensource.org/licenses/ZPL-2.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:38Z", + "timestamp": "2024-08-19T17:45:00Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { - "match": "N/A", - "url": "https://opensource.org/licenses/ZPL-2.0", + "match": "false", + "url": "http://old.zope.org/Resources/License/ZPL-2.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:38Z", + "timestamp": "2024-08-19T17:45:00Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/ZPL-2.1.json b/src/main/resources/license-list-data/json/details/ZPL-2.1.json index dde67de1c1..6ff79d2fa9 100644 --- a/src/main/resources/license-list-data/json/details/ZPL-2.1.json +++ b/src/main/resources/license-list-data/json/details/ZPL-2.1.json @@ -12,7 +12,7 @@ "url": "http://old.zope.org/Resources/ZPL/", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:30Z", + "timestamp": "2024-08-19T17:49:45Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Zed.json b/src/main/resources/license-list-data/json/details/Zed.json index a15fff0ae0..3be81cd5c3 100644 --- a/src/main/resources/license-list-data/json/details/Zed.json +++ b/src/main/resources/license-list-data/json/details/Zed.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Zed", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:23Z", + "timestamp": "2024-08-19T17:44:18Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Zeeff.json b/src/main/resources/license-list-data/json/details/Zeeff.json index ab2501313b..d45a2cd05d 100644 --- a/src/main/resources/license-list-data/json/details/Zeeff.json +++ b/src/main/resources/license-list-data/json/details/Zeeff.json @@ -10,7 +10,7 @@ "url": "ftp://ftp.tin.org/pub/news/utils/newsx/newsx-1.6.tar.gz", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:20:28Z", + "timestamp": "2024-08-19T17:40:35Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Zend-2.0.json b/src/main/resources/license-list-data/json/details/Zend-2.0.json index 1f2764bceb..8ad0133fa7 100644 --- a/src/main/resources/license-list-data/json/details/Zend-2.0.json +++ b/src/main/resources/license-list-data/json/details/Zend-2.0.json @@ -11,7 +11,7 @@ "url": "https://web.archive.org/web/20130517195954/http://www.zend.com/license/2_00.txt", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:27:08Z", + "timestamp": "2024-08-19T17:35:21Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Zimbra-1.3.json b/src/main/resources/license-list-data/json/details/Zimbra-1.3.json index 2402c70080..cba84238ca 100644 --- a/src/main/resources/license-list-data/json/details/Zimbra-1.3.json +++ b/src/main/resources/license-list-data/json/details/Zimbra-1.3.json @@ -11,7 +11,7 @@ "url": "http://web.archive.org/web/20100302225219/http://www.zimbra.com/license/zimbra-public-license-1-3.html", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:24:08Z", + "timestamp": "2024-08-19T17:46:08Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Zimbra-1.4.json b/src/main/resources/license-list-data/json/details/Zimbra-1.4.json index a256ca676d..7c240c2e3c 100644 --- a/src/main/resources/license-list-data/json/details/Zimbra-1.4.json +++ b/src/main/resources/license-list-data/json/details/Zimbra-1.4.json @@ -10,7 +10,7 @@ "url": "http://www.zimbra.com/legal/zimbra-public-license-1-4", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:15Z", + "timestamp": "2024-08-19T17:36:52Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/Zlib.json b/src/main/resources/license-list-data/json/details/Zlib.json index ce36d660d7..cee8b7c8b4 100644 --- a/src/main/resources/license-list-data/json/details/Zlib.json +++ b/src/main/resources/license-list-data/json/details/Zlib.json @@ -7,22 +7,22 @@ "licenseId": "Zlib", "crossRef": [ { - "match": "N/A", - "url": "https://opensource.org/licenses/Zlib", + "match": "false", + "url": "http://www.zlib.net/zlib_license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:21Z", + "timestamp": "2024-08-19T17:35:27Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { - "match": "false", - "url": "http://www.zlib.net/zlib_license.html", + "match": "N/A", + "url": "https://opensource.org/licenses/Zlib", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:21Z", + "timestamp": "2024-08-19T17:35:27Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/any-OSI.json b/src/main/resources/license-list-data/json/details/any-OSI.json index f86e0f15d5..504c578f4b 100644 --- a/src/main/resources/license-list-data/json/details/any-OSI.json +++ b/src/main/resources/license-list-data/json/details/any-OSI.json @@ -10,7 +10,7 @@ "url": "https://metacpan.org/pod/Exporter::Tidy#LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:59Z", + "timestamp": "2024-08-19T17:38:51Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/bcrypt-Solar-Designer.json b/src/main/resources/license-list-data/json/details/bcrypt-Solar-Designer.json index cd7ac3bc98..77d6feb7c8 100644 --- a/src/main/resources/license-list-data/json/details/bcrypt-Solar-Designer.json +++ b/src/main/resources/license-list-data/json/details/bcrypt-Solar-Designer.json @@ -10,7 +10,7 @@ "url": "https://github.com/bcrypt-ruby/bcrypt-ruby/blob/master/ext/mri/crypt_blowfish.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:33Z", + "timestamp": "2024-08-19T17:43:28Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/blessing.json b/src/main/resources/license-list-data/json/details/blessing.json index 6d7a29b3e2..895fe84541 100644 --- a/src/main/resources/license-list-data/json/details/blessing.json +++ b/src/main/resources/license-list-data/json/details/blessing.json @@ -10,7 +10,7 @@ "url": "https://sqlite.org/src/artifact/df5091916dbb40e6", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:31Z", + "timestamp": "2024-08-19T17:40:00Z", "isWayBackLink": false, "order": 1 }, @@ -19,7 +19,7 @@ "url": "https://www.sqlite.org/src/artifact/e33a4df7e32d742a?ln\u003d4-9", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:31Z", + "timestamp": "2024-08-19T17:40:01Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/bzip2-1.0.5.json b/src/main/resources/license-list-data/json/details/bzip2-1.0.5.json index 2f126e7415..8c101764b9 100644 --- a/src/main/resources/license-list-data/json/details/bzip2-1.0.5.json +++ b/src/main/resources/license-list-data/json/details/bzip2-1.0.5.json @@ -8,21 +8,21 @@ "crossRef": [ { "match": "false", - "url": "https://sourceware.org/bzip2/1.0.5/bzip2-manual-1.0.5.html", + "url": "http://bzip.org/1.0.5/bzip2-manual-1.0.5.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:11Z", + "timestamp": "2024-08-19T17:47:12Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "http://bzip.org/1.0.5/bzip2-manual-1.0.5.html", + "url": "https://sourceware.org/bzip2/1.0.5/bzip2-manual-1.0.5.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:12Z", + "timestamp": "2024-08-19T17:47:13Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/bzip2-1.0.6.json b/src/main/resources/license-list-data/json/details/bzip2-1.0.6.json index c64baecc5e..3faeae79dc 100644 --- a/src/main/resources/license-list-data/json/details/bzip2-1.0.6.json +++ b/src/main/resources/license-list-data/json/details/bzip2-1.0.6.json @@ -8,30 +8,30 @@ "crossRef": [ { "match": "false", - "url": "http://bzip.org/1.0.5/bzip2-manual-1.0.5.html", + "url": "https://sourceware.org/cgit/valgrind/tree/mpi/libmpiwrap.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:27Z", + "timestamp": "2024-08-19T17:45:34Z", "isWayBackLink": false, - "order": 1 + "order": 2 }, { "match": "false", - "url": "https://sourceware.org/git/?p\u003dbzip2.git;a\u003dblob;f\u003dLICENSE;hb\u003dbzip2-1.0.6", + "url": "http://bzip.org/1.0.5/bzip2-manual-1.0.5.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:28Z", + "timestamp": "2024-08-19T17:45:36Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "https://sourceware.org/cgit/valgrind/tree/mpi/libmpiwrap.c", + "url": "https://sourceware.org/git/?p\u003dbzip2.git;a\u003dblob;f\u003dLICENSE;hb\u003dbzip2-1.0.6", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:28Z", + "timestamp": "2024-08-19T17:45:37Z", "isWayBackLink": false, - "order": 2 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/check-cvs.json b/src/main/resources/license-list-data/json/details/check-cvs.json index ab1c9e8011..9badf39ab1 100644 --- a/src/main/resources/license-list-data/json/details/check-cvs.json +++ b/src/main/resources/license-list-data/json/details/check-cvs.json @@ -11,7 +11,7 @@ "url": "http://cvs.savannah.gnu.org/viewvc/cvs/ccvs/contrib/check_cvs.in?revision\u003d1.1.4.3\u0026view\u003dmarkup\u0026pathrev\u003dcvs1-11-23#l2", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:22Z", + "timestamp": "2024-08-19T17:39:04Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/checkmk.json b/src/main/resources/license-list-data/json/details/checkmk.json index 1ab55882d8..cfbc9e799b 100644 --- a/src/main/resources/license-list-data/json/details/checkmk.json +++ b/src/main/resources/license-list-data/json/details/checkmk.json @@ -10,7 +10,7 @@ "url": "https://github.com/libcheck/check/blob/master/checkmk/checkmk.in", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:31Z", + "timestamp": "2024-08-19T17:43:04Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/copyleft-next-0.3.0.json b/src/main/resources/license-list-data/json/details/copyleft-next-0.3.0.json index dc327ea6fd..4d9026b4a6 100644 --- a/src/main/resources/license-list-data/json/details/copyleft-next-0.3.0.json +++ b/src/main/resources/license-list-data/json/details/copyleft-next-0.3.0.json @@ -10,7 +10,7 @@ "url": "https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:16Z", + "timestamp": "2024-08-19T17:39:05Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/copyleft-next-0.3.1.json b/src/main/resources/license-list-data/json/details/copyleft-next-0.3.1.json index c054b4bdf3..bca60bd329 100644 --- a/src/main/resources/license-list-data/json/details/copyleft-next-0.3.1.json +++ b/src/main/resources/license-list-data/json/details/copyleft-next-0.3.1.json @@ -10,7 +10,7 @@ "url": "https://github.com/copyleft-next/copyleft-next/blob/master/Releases/copyleft-next-0.3.1", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:07Z", + "timestamp": "2024-08-19T17:41:14Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/curl.json b/src/main/resources/license-list-data/json/details/curl.json index 2c554e17d7..e863a68325 100644 --- a/src/main/resources/license-list-data/json/details/curl.json +++ b/src/main/resources/license-list-data/json/details/curl.json @@ -10,7 +10,7 @@ "url": "https://github.com/bagder/curl/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:26Z", + "timestamp": "2024-08-19T17:48:14Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/cve-tou.json b/src/main/resources/license-list-data/json/details/cve-tou.json index d9eee1b404..1ccdb7d8d6 100644 --- a/src/main/resources/license-list-data/json/details/cve-tou.json +++ b/src/main/resources/license-list-data/json/details/cve-tou.json @@ -10,7 +10,7 @@ "url": "https://www.cve.org/Legal/TermsOfUse", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:22Z", + "timestamp": "2024-08-19T17:50:28Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/diffmark.json b/src/main/resources/license-list-data/json/details/diffmark.json index 0901e4664d..d50c5db18e 100644 --- a/src/main/resources/license-list-data/json/details/diffmark.json +++ b/src/main/resources/license-list-data/json/details/diffmark.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/diffmark", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:58Z", + "timestamp": "2024-08-19T17:43:34Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/dtoa.json b/src/main/resources/license-list-data/json/details/dtoa.json index b786dac0ab..5b414cf640 100644 --- a/src/main/resources/license-list-data/json/details/dtoa.json +++ b/src/main/resources/license-list-data/json/details/dtoa.json @@ -7,21 +7,21 @@ "crossRef": [ { "match": "false", - "url": "https://github.com/SWI-Prolog/swipl-devel/blob/master/src/os/dtoa.c", + "url": "https://sourceware.org/git/?p\u003dnewlib-cygwin.git;a\u003dblob;f\u003dnewlib/libc/stdlib/mprec.h;hb\u003dHEAD", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:38Z", + "timestamp": "2024-08-19T17:37:49Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "https://sourceware.org/git/?p\u003dnewlib-cygwin.git;a\u003dblob;f\u003dnewlib/libc/stdlib/mprec.h;hb\u003dHEAD", + "url": "https://github.com/SWI-Prolog/swipl-devel/blob/master/src/os/dtoa.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:39Z", + "timestamp": "2024-08-19T17:37:50Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/dvipdfm.json b/src/main/resources/license-list-data/json/details/dvipdfm.json index 66b0ffa2d6..62d6999ba7 100644 --- a/src/main/resources/license-list-data/json/details/dvipdfm.json +++ b/src/main/resources/license-list-data/json/details/dvipdfm.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/dvipdfm", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:49Z", + "timestamp": "2024-08-19T17:35:22Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/eCos-2.0.json b/src/main/resources/license-list-data/json/details/eCos-2.0.json index 0377f58442..50f9e0a4ba 100644 --- a/src/main/resources/license-list-data/json/details/eCos-2.0.json +++ b/src/main/resources/license-list-data/json/details/eCos-2.0.json @@ -12,7 +12,7 @@ "url": "https://www.gnu.org/licenses/ecos-license.html", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:47Z", + "timestamp": "2024-08-19T17:38:26Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/eGenix.json b/src/main/resources/license-list-data/json/details/eGenix.json index 1e1dfc9895..74bddb19d5 100644 --- a/src/main/resources/license-list-data/json/details/eGenix.json +++ b/src/main/resources/license-list-data/json/details/eGenix.json @@ -6,22 +6,22 @@ "licenseId": "eGenix", "crossRef": [ { - "match": "false", - "url": "http://www.egenix.com/products/eGenix.com-Public-License-1.1.0.pdf", + "match": "true", + "url": "https://fedoraproject.org/wiki/Licensing/eGenix.com_Public_License_1.1.0", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:13Z", + "timestamp": "2024-08-19T17:39:40Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { - "match": "true", - "url": "https://fedoraproject.org/wiki/Licensing/eGenix.com_Public_License_1.1.0", + "match": "false", + "url": "http://www.egenix.com/products/eGenix.com-Public-License-1.1.0.pdf", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:14Z", + "timestamp": "2024-08-19T17:39:41Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/etalab-2.0.json b/src/main/resources/license-list-data/json/details/etalab-2.0.json index 0d0b9132ac..f143dac430 100644 --- a/src/main/resources/license-list-data/json/details/etalab-2.0.json +++ b/src/main/resources/license-list-data/json/details/etalab-2.0.json @@ -8,21 +8,21 @@ "crossRef": [ { "match": "false", - "url": "https://raw.githubusercontent.com/DISIC/politique-de-contribution-open-source/master/LICENSE", + "url": "https://github.com/DISIC/politique-de-contribution-open-source/blob/master/LICENSE.pdf", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:48Z", + "timestamp": "2024-08-19T17:35:53Z", "isWayBackLink": false, - "order": 1 + "order": 0 }, { "match": "false", - "url": "https://github.com/DISIC/politique-de-contribution-open-source/blob/master/LICENSE.pdf", + "url": "https://raw.githubusercontent.com/DISIC/politique-de-contribution-open-source/master/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:32:48Z", + "timestamp": "2024-08-19T17:35:53Z", "isWayBackLink": false, - "order": 0 + "order": 1 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/fwlw.json b/src/main/resources/license-list-data/json/details/fwlw.json index 4782155f3c..4778396735 100644 --- a/src/main/resources/license-list-data/json/details/fwlw.json +++ b/src/main/resources/license-list-data/json/details/fwlw.json @@ -11,7 +11,7 @@ "url": "https://mirrors.nic.cz/tex-archive/macros/latex/contrib/fwlw/README", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:02Z", + "timestamp": "2024-08-19T17:35:21Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/gSOAP-1.3b.json b/src/main/resources/license-list-data/json/details/gSOAP-1.3b.json index ef3ca548e2..df26a942cf 100644 --- a/src/main/resources/license-list-data/json/details/gSOAP-1.3b.json +++ b/src/main/resources/license-list-data/json/details/gSOAP-1.3b.json @@ -10,7 +10,7 @@ "url": "http://www.cs.fsu.edu/~engelen/license.html", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:28:23Z", + "timestamp": "2024-08-19T17:42:37Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/gnuplot.json b/src/main/resources/license-list-data/json/details/gnuplot.json index 7477484738..e2c7355037 100644 --- a/src/main/resources/license-list-data/json/details/gnuplot.json +++ b/src/main/resources/license-list-data/json/details/gnuplot.json @@ -11,7 +11,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Gnuplot", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:02Z", + "timestamp": "2024-08-19T17:38:00Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/gtkbook.json b/src/main/resources/license-list-data/json/details/gtkbook.json index 41c341d0cb..4d4caf14a4 100644 --- a/src/main/resources/license-list-data/json/details/gtkbook.json +++ b/src/main/resources/license-list-data/json/details/gtkbook.json @@ -7,21 +7,21 @@ "crossRef": [ { "match": "false", - "url": "https://github.com/slogan621/gtkbook", + "url": "https://github.com/oetiker/rrdtool-1.x/blob/master/src/plbasename.c#L8-L11", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:43Z", + "timestamp": "2024-08-19T17:40:41Z", "isWayBackLink": false, - "order": 0 + "order": 1 }, { "match": "false", - "url": "https://github.com/oetiker/rrdtool-1.x/blob/master/src/plbasename.c#L8-L11", + "url": "https://github.com/slogan621/gtkbook", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:43Z", + "timestamp": "2024-08-19T17:40:41Z", "isWayBackLink": false, - "order": 1 + "order": 0 } ], "seeAlso": [ diff --git a/src/main/resources/license-list-data/json/details/hdparm.json b/src/main/resources/license-list-data/json/details/hdparm.json index 799c6aef8a..e644638016 100644 --- a/src/main/resources/license-list-data/json/details/hdparm.json +++ b/src/main/resources/license-list-data/json/details/hdparm.json @@ -10,7 +10,7 @@ "url": "https://github.com/Distrotech/hdparm/blob/4517550db29a91420fb2b020349523b1b4512df2/LICENSE.TXT", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:44Z", + "timestamp": "2024-08-19T17:39:48Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/iMatix.json b/src/main/resources/license-list-data/json/details/iMatix.json index 5fa3d800aa..d7490012a3 100644 --- a/src/main/resources/license-list-data/json/details/iMatix.json +++ b/src/main/resources/license-list-data/json/details/iMatix.json @@ -11,7 +11,7 @@ "url": "http://legacy.imatix.com/html/sfl/sfl4.htm#license", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:22:56Z", + "timestamp": "2024-08-19T17:50:03Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/libpng-2.0.json b/src/main/resources/license-list-data/json/details/libpng-2.0.json index 023829858a..e64899168a 100644 --- a/src/main/resources/license-list-data/json/details/libpng-2.0.json +++ b/src/main/resources/license-list-data/json/details/libpng-2.0.json @@ -10,7 +10,7 @@ "url": "http://www.libpng.org/pub/png/src/libpng-LICENSE.txt", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:11Z", + "timestamp": "2024-08-19T17:36:06Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/libselinux-1.0.json b/src/main/resources/license-list-data/json/details/libselinux-1.0.json index 5ac27a02c7..f3ff931f19 100644 --- a/src/main/resources/license-list-data/json/details/libselinux-1.0.json +++ b/src/main/resources/license-list-data/json/details/libselinux-1.0.json @@ -10,7 +10,7 @@ "url": "https://github.com/SELinuxProject/selinux/blob/master/libselinux/LICENSE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:42Z", + "timestamp": "2024-08-19T17:38:55Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/libtiff.json b/src/main/resources/license-list-data/json/details/libtiff.json index 421ab344c9..70377f2b26 100644 --- a/src/main/resources/license-list-data/json/details/libtiff.json +++ b/src/main/resources/license-list-data/json/details/libtiff.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/libtiff", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:33Z", + "timestamp": "2024-08-19T17:35:14Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/libutil-David-Nugent.json b/src/main/resources/license-list-data/json/details/libutil-David-Nugent.json index 77694456f6..a4e9efbcc9 100644 --- a/src/main/resources/license-list-data/json/details/libutil-David-Nugent.json +++ b/src/main/resources/license-list-data/json/details/libutil-David-Nugent.json @@ -7,11 +7,11 @@ "licenseId": "libutil-David-Nugent", "crossRef": [ { - "match": "false", + "match": "N/A", "url": "https://cgit.freedesktop.org/libbsd/tree/man/setproctitle.3bsd", "isValid": true, - "isLive": true, - "timestamp": "2024-05-22T17:24:44Z", + "isLive": false, + "timestamp": "2024-08-19T17:50:43Z", "isWayBackLink": false, "order": 1 }, @@ -20,7 +20,7 @@ "url": "http://web.mit.edu/freebsd/head/lib/libutil/login_ok.3", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:44Z", + "timestamp": "2024-08-19T17:50:44Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/lsof.json b/src/main/resources/license-list-data/json/details/lsof.json index f61bd60c6a..1c9d910f45 100644 --- a/src/main/resources/license-list-data/json/details/lsof.json +++ b/src/main/resources/license-list-data/json/details/lsof.json @@ -10,7 +10,7 @@ "url": "https://github.com/lsof-org/lsof/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:31:14Z", + "timestamp": "2024-08-19T17:35:28Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/magaz.json b/src/main/resources/license-list-data/json/details/magaz.json index 13e242ebd5..8b83dda573 100644 --- a/src/main/resources/license-list-data/json/details/magaz.json +++ b/src/main/resources/license-list-data/json/details/magaz.json @@ -11,7 +11,7 @@ "url": "https://mirrors.nic.cz/tex-archive/macros/latex/contrib/magaz/magaz.tex", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:20Z", + "timestamp": "2024-08-19T17:35:35Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/mailprio.json b/src/main/resources/license-list-data/json/details/mailprio.json index f96f170daa..48d8842e33 100644 --- a/src/main/resources/license-list-data/json/details/mailprio.json +++ b/src/main/resources/license-list-data/json/details/mailprio.json @@ -10,7 +10,7 @@ "url": "https://fossies.org/linux/sendmail/contrib/mailprio", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:49Z", + "timestamp": "2024-08-19T17:38:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/metamail.json b/src/main/resources/license-list-data/json/details/metamail.json index 883bbe5856..a45de7216b 100644 --- a/src/main/resources/license-list-data/json/details/metamail.json +++ b/src/main/resources/license-list-data/json/details/metamail.json @@ -10,7 +10,7 @@ "url": "https://github.com/Dual-Life/mime-base64/blob/master/Base64.xs#L12", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:40Z", + "timestamp": "2024-08-19T17:39:06Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/mpi-permissive.json b/src/main/resources/license-list-data/json/details/mpi-permissive.json index a1782f1749..2c88b9b91d 100644 --- a/src/main/resources/license-list-data/json/details/mpi-permissive.json +++ b/src/main/resources/license-list-data/json/details/mpi-permissive.json @@ -6,11 +6,11 @@ "licenseId": "mpi-permissive", "crossRef": [ { - "match": "N/A", + "match": "false", "url": "https://sources.debian.org/src/openmpi/4.1.0-10/ompi/debuggers/msgq_interface.h/?hl\u003d19#L19", "isValid": true, - "isLive": false, - "timestamp": "2024-05-22T17:18:48Z", + "isLive": true, + "timestamp": "2024-08-19T17:42:51Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/mpich2.json b/src/main/resources/license-list-data/json/details/mpich2.json index 2cb69812b9..a0ef8f7a48 100644 --- a/src/main/resources/license-list-data/json/details/mpich2.json +++ b/src/main/resources/license-list-data/json/details/mpich2.json @@ -11,7 +11,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/MIT", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:27:10Z", + "timestamp": "2024-08-19T17:42:34Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/mplus.json b/src/main/resources/license-list-data/json/details/mplus.json index 27090d2753..e1cd7355ad 100644 --- a/src/main/resources/license-list-data/json/details/mplus.json +++ b/src/main/resources/license-list-data/json/details/mplus.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing:Mplus?rd\u003dLicensing/mplus", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:24Z", + "timestamp": "2024-08-19T17:46:23Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/pkgconf.json b/src/main/resources/license-list-data/json/details/pkgconf.json index a75fdd1dca..46439b5de2 100644 --- a/src/main/resources/license-list-data/json/details/pkgconf.json +++ b/src/main/resources/license-list-data/json/details/pkgconf.json @@ -10,7 +10,7 @@ "url": "https://github.com/pkgconf/pkgconf/blob/master/cli/main.c#L8", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:25Z", + "timestamp": "2024-08-19T17:35:12Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/pnmstitch.json b/src/main/resources/license-list-data/json/details/pnmstitch.json index 5873c21ca7..39727ad9ba 100644 --- a/src/main/resources/license-list-data/json/details/pnmstitch.json +++ b/src/main/resources/license-list-data/json/details/pnmstitch.json @@ -10,7 +10,7 @@ "url": "https://sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/editor/pnmstitch.c#l2", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:32Z", + "timestamp": "2024-08-19T17:44:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/psfrag.json b/src/main/resources/license-list-data/json/details/psfrag.json index 027c941192..ca92798a12 100644 --- a/src/main/resources/license-list-data/json/details/psfrag.json +++ b/src/main/resources/license-list-data/json/details/psfrag.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/psfrag", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:29:49Z", + "timestamp": "2024-08-19T17:38:07Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/psutils.json b/src/main/resources/license-list-data/json/details/psutils.json index 41b8d8843d..c151f970d4 100644 --- a/src/main/resources/license-list-data/json/details/psutils.json +++ b/src/main/resources/license-list-data/json/details/psutils.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/psutils", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:21:13Z", + "timestamp": "2024-08-19T17:40:36Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/python-ldap.json b/src/main/resources/license-list-data/json/details/python-ldap.json index ff0c50868b..8fd02cb04f 100644 --- a/src/main/resources/license-list-data/json/details/python-ldap.json +++ b/src/main/resources/license-list-data/json/details/python-ldap.json @@ -6,11 +6,11 @@ "licenseId": "python-ldap", "crossRef": [ { - "match": "false", + "match": "true", "url": "https://github.com/python-ldap/python-ldap/blob/main/LICENCE", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:50Z", + "timestamp": "2024-08-19T17:41:52Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/radvd.json b/src/main/resources/license-list-data/json/details/radvd.json index 5970d2ebc5..664e651168 100644 --- a/src/main/resources/license-list-data/json/details/radvd.json +++ b/src/main/resources/license-list-data/json/details/radvd.json @@ -11,7 +11,7 @@ "url": "https://github.com/radvd-project/radvd/blob/master/COPYRIGHT", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:25:33Z", + "timestamp": "2024-08-19T17:36:28Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/snprintf.json b/src/main/resources/license-list-data/json/details/snprintf.json index 44dc61f80d..2ecb3939ee 100644 --- a/src/main/resources/license-list-data/json/details/snprintf.json +++ b/src/main/resources/license-list-data/json/details/snprintf.json @@ -10,7 +10,7 @@ "url": "https://github.com/openssh/openssh-portable/blob/master/openbsd-compat/bsd-snprintf.c#L2", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:36Z", + "timestamp": "2024-08-19T17:44:31Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/softSurfer.json b/src/main/resources/license-list-data/json/details/softSurfer.json index 3e3c035403..713aa381df 100644 --- a/src/main/resources/license-list-data/json/details/softSurfer.json +++ b/src/main/resources/license-list-data/json/details/softSurfer.json @@ -10,7 +10,7 @@ "url": "https://github.com/mm2/Little-CMS/blob/master/src/cmssm.c#L207", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:50Z", + "timestamp": "2024-08-19T17:44:11Z", "isWayBackLink": false, "order": 0 }, @@ -19,7 +19,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/softSurfer", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:28:50Z", + "timestamp": "2024-08-19T17:44:11Z", "isWayBackLink": false, "order": 1 } diff --git a/src/main/resources/license-list-data/json/details/ssh-keyscan.json b/src/main/resources/license-list-data/json/details/ssh-keyscan.json index dcf44fb0d0..324e4673a7 100644 --- a/src/main/resources/license-list-data/json/details/ssh-keyscan.json +++ b/src/main/resources/license-list-data/json/details/ssh-keyscan.json @@ -10,7 +10,7 @@ "url": "https://github.com/openssh/openssh-portable/blob/master/LICENCE#L82", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:04Z", + "timestamp": "2024-08-19T17:35:28Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/swrule.json b/src/main/resources/license-list-data/json/details/swrule.json index 635eeaf478..6e341da792 100644 --- a/src/main/resources/license-list-data/json/details/swrule.json +++ b/src/main/resources/license-list-data/json/details/swrule.json @@ -10,7 +10,7 @@ "url": "https://ctan.math.utah.edu/ctan/tex-archive/macros/generic/misc/swrule.sty", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:18:52Z", + "timestamp": "2024-08-19T17:36:47Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/threeparttable.json b/src/main/resources/license-list-data/json/details/threeparttable.json index 6e75d2544b..7a5a0c7dd4 100644 --- a/src/main/resources/license-list-data/json/details/threeparttable.json +++ b/src/main/resources/license-list-data/json/details/threeparttable.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Threeparttable", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:24:41Z", + "timestamp": "2024-08-19T17:39:02Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/ulem.json b/src/main/resources/license-list-data/json/details/ulem.json index ba59de5a66..d4922b3822 100644 --- a/src/main/resources/license-list-data/json/details/ulem.json +++ b/src/main/resources/license-list-data/json/details/ulem.json @@ -1,7 +1,7 @@ { "isDeprecatedLicenseId": false, "licenseText": "Copyright 1989-2019 by Donald Arseneau (Vancouver, Canada, asnd@triumf.ca)\n\nThis software may be freely transmitted, reproduced, or modified\nfor any purpose provided that this copyright notice is left intact.\n", - "standardLicenseTemplate": "\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright 1989-2019 by Donald Arseneau (Vancouver, Canada, asnd@triumf.ca)\";match\u003d\".{0,5000}\"\u003e\u003e\n\nThis software may be freely transmitted, reproduced, or modified for any purpose provided that this copyright notice is left intact.\n\n", + "standardLicenseTemplate": "\u003c\u003cvar;name\u003d\"copyright\";original\u003d\"Copyright 1989-2019 by Donald Arseneau (Vancouver, Canada, asnd@triumf.ca)\";match\u003d\".{0,5000}\"\u003e\u003e\n\n\u003c\u003cvar;name\u003d\"software\";original\u003d\"This software\";match\u003d\"This software|These macros\"\u003e\u003e may be freely transmitted, reproduced, or modified for any purpose provided that this\u003c\u003cbeginOptional\u003e\u003e copyright\u003c\u003cendOptional\u003e\u003e notice is left intact.\n\n", "name": "ulem License", "licenseComments": "This license is a very similar to fwlw and magaz, but has slightly different obligations.", "licenseId": "ulem", @@ -11,7 +11,7 @@ "url": "https://mirrors.ctan.org/macros/latex/contrib/ulem/README", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:30:10Z", + "timestamp": "2024-08-19T17:41:07Z", "isWayBackLink": false, "order": 0 } @@ -20,5 +20,5 @@ "https://mirrors.ctan.org/macros/latex/contrib/ulem/README" ], "isOsiApproved": false, - "licenseTextHtml": "\n \u003cvar class\u003d\"replaceable-license-text\"\u003e \n Copyright 1989-2019 by Donald Arseneau (Vancouver, Canada, asnd@triumf.ca)\n \u003c/var\u003e\n \u003cp\u003e\n This software may be freely transmitted, reproduced, or modified\n for any purpose provided that this copyright notice is left intact.\n \u003c/p\u003e\n\n " + "licenseTextHtml": "\n \u003cvar class\u003d\"replaceable-license-text\"\u003e \n Copyright 1989-2019 by Donald Arseneau (Vancouver, Canada, asnd@triumf.ca)\n \u003c/var\u003e\n \u003cp\u003e\n \u003cvar class\u003d\"replaceable-license-text\"\u003e This software\u003c/var\u003e may be freely transmitted, reproduced, or modified\n for any purpose provided that this \u003cvar class\u003d\"optional-license-text\"\u003e copyright\u003c/var\u003e notice is left intact.\n \u003c/p\u003e\n\n " } \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/details/w3m.json b/src/main/resources/license-list-data/json/details/w3m.json index 178dfc4334..8afdd48310 100644 --- a/src/main/resources/license-list-data/json/details/w3m.json +++ b/src/main/resources/license-list-data/json/details/w3m.json @@ -10,7 +10,7 @@ "url": "https://github.com/tats/w3m/blob/master/COPYING", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:20:12Z", + "timestamp": "2024-08-19T17:40:32Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/wxWindows.json b/src/main/resources/license-list-data/json/details/wxWindows.json index ebce8312c6..39df435863 100644 --- a/src/main/resources/license-list-data/json/details/wxWindows.json +++ b/src/main/resources/license-list-data/json/details/wxWindows.json @@ -11,7 +11,7 @@ "url": "https://opensource.org/licenses/WXwindows", "isValid": true, "isLive": false, - "timestamp": "2024-05-22T17:21:10Z", + "timestamp": "2024-08-19T17:39:46Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/xinetd.json b/src/main/resources/license-list-data/json/details/xinetd.json index b7e7cfea7e..4e8dce4666 100644 --- a/src/main/resources/license-list-data/json/details/xinetd.json +++ b/src/main/resources/license-list-data/json/details/xinetd.json @@ -11,7 +11,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/Xinetd_License", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:26:23Z", + "timestamp": "2024-08-19T17:41:20Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/xkeyboard-config-Zinoviev.json b/src/main/resources/license-list-data/json/details/xkeyboard-config-Zinoviev.json index dfd8a997af..3309f1eba7 100644 --- a/src/main/resources/license-list-data/json/details/xkeyboard-config-Zinoviev.json +++ b/src/main/resources/license-list-data/json/details/xkeyboard-config-Zinoviev.json @@ -11,7 +11,7 @@ "url": "https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config/-/blob/master/COPYING?ref_type\u003dheads#L178", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:19:22Z", + "timestamp": "2024-08-19T17:35:23Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/xlock.json b/src/main/resources/license-list-data/json/details/xlock.json index f57889e952..bd06a18296 100644 --- a/src/main/resources/license-list-data/json/details/xlock.json +++ b/src/main/resources/license-list-data/json/details/xlock.json @@ -10,7 +10,7 @@ "url": "https://fossies.org/linux/tiff/contrib/ras/ras2tif.c", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:35Z", + "timestamp": "2024-08-19T17:35:59Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/xpp.json b/src/main/resources/license-list-data/json/details/xpp.json index 571f3f6d3a..474ede8ae2 100644 --- a/src/main/resources/license-list-data/json/details/xpp.json +++ b/src/main/resources/license-list-data/json/details/xpp.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/xpp", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:22:30Z", + "timestamp": "2024-08-19T17:37:26Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/xzoom.json b/src/main/resources/license-list-data/json/details/xzoom.json index e85f9413d0..ee7edf6731 100644 --- a/src/main/resources/license-list-data/json/details/xzoom.json +++ b/src/main/resources/license-list-data/json/details/xzoom.json @@ -10,7 +10,7 @@ "url": "https://metadata.ftp-master.debian.org/changelogs//main/x/xzoom/xzoom_0.3-27_copyright", "isValid": false, "isLive": false, - "timestamp": "2024-05-22T17:29:03Z", + "timestamp": "2024-08-19T17:39:34Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/details/zlib-acknowledgement.json b/src/main/resources/license-list-data/json/details/zlib-acknowledgement.json index 31d55cee05..7f21f41726 100644 --- a/src/main/resources/license-list-data/json/details/zlib-acknowledgement.json +++ b/src/main/resources/license-list-data/json/details/zlib-acknowledgement.json @@ -10,7 +10,7 @@ "url": "https://fedoraproject.org/wiki/Licensing/ZlibWithAcknowledgement", "isValid": true, "isLive": true, - "timestamp": "2024-05-22T17:23:26Z", + "timestamp": "2024-08-19T17:40:17Z", "isWayBackLink": false, "order": 0 } diff --git a/src/main/resources/license-list-data/json/exceptions.json b/src/main/resources/license-list-data/json/exceptions.json index e9bb9314b4..1b48b052bf 100644 --- a/src/main/resources/license-list-data/json/exceptions.json +++ b/src/main/resources/license-list-data/json/exceptions.json @@ -1,11 +1,11 @@ { - "licenseListVersion": "3.24.0", + "licenseListVersion": "3.25.0", "exceptions": [ { "reference": "./389-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./389-exception.html", - "referenceNumber": 52, + "referenceNumber": 53, "name": "389 Directory Server Exception", "licenseExceptionId": "389-exception", "seeAlso": [ @@ -17,7 +17,7 @@ "reference": "./Asterisk-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Asterisk-exception.html", - "referenceNumber": 39, + "referenceNumber": 60, "name": "Asterisk exception", "licenseExceptionId": "Asterisk-exception", "seeAlso": [ @@ -40,7 +40,7 @@ "reference": "./Autoconf-exception-2.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Autoconf-exception-2.0.html", - "referenceNumber": 60, + "referenceNumber": 72, "name": "Autoconf exception 2.0", "licenseExceptionId": "Autoconf-exception-2.0", "seeAlso": [ @@ -52,7 +52,7 @@ "reference": "./Autoconf-exception-3.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Autoconf-exception-3.0.html", - "referenceNumber": 42, + "referenceNumber": 17, "name": "Autoconf exception 3.0", "licenseExceptionId": "Autoconf-exception-3.0", "seeAlso": [ @@ -63,7 +63,7 @@ "reference": "./Autoconf-exception-generic.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Autoconf-exception-generic.html", - "referenceNumber": 70, + "referenceNumber": 48, "name": "Autoconf generic exception", "licenseExceptionId": "Autoconf-exception-generic", "seeAlso": [ @@ -77,7 +77,7 @@ "reference": "./Autoconf-exception-generic-3.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Autoconf-exception-generic-3.0.html", - "referenceNumber": 44, + "referenceNumber": 64, "name": "Autoconf generic exception for GPL-3.0", "licenseExceptionId": "Autoconf-exception-generic-3.0", "seeAlso": [ @@ -88,7 +88,7 @@ "reference": "./Autoconf-exception-macro.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Autoconf-exception-macro.html", - "referenceNumber": 15, + "referenceNumber": 51, "name": "Autoconf macro exception", "licenseExceptionId": "Autoconf-exception-macro", "seeAlso": [ @@ -101,7 +101,7 @@ "reference": "./Bison-exception-1.24.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Bison-exception-1.24.html", - "referenceNumber": 4, + "referenceNumber": 59, "name": "Bison exception 1.24", "licenseExceptionId": "Bison-exception-1.24", "seeAlso": [ @@ -112,7 +112,7 @@ "reference": "./Bison-exception-2.2.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Bison-exception-2.2.html", - "referenceNumber": 30, + "referenceNumber": 21, "name": "Bison exception 2.2", "licenseExceptionId": "Bison-exception-2.2", "seeAlso": [ @@ -123,7 +123,7 @@ "reference": "./Bootloader-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Bootloader-exception.html", - "referenceNumber": 21, + "referenceNumber": 40, "name": "Bootloader Distribution Exception", "licenseExceptionId": "Bootloader-exception", "seeAlso": [ @@ -134,7 +134,7 @@ "reference": "./Classpath-exception-2.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Classpath-exception-2.0.html", - "referenceNumber": 11, + "referenceNumber": 34, "name": "Classpath exception 2.0", "licenseExceptionId": "Classpath-exception-2.0", "seeAlso": [ @@ -146,7 +146,7 @@ "reference": "./CLISP-exception-2.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./CLISP-exception-2.0.html", - "referenceNumber": 49, + "referenceNumber": 71, "name": "CLISP exception 2.0", "licenseExceptionId": "CLISP-exception-2.0", "seeAlso": [ @@ -157,7 +157,7 @@ "reference": "./cryptsetup-OpenSSL-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./cryptsetup-OpenSSL-exception.html", - "referenceNumber": 26, + "referenceNumber": 5, "name": "cryptsetup OpenSSL exception", "licenseExceptionId": "cryptsetup-OpenSSL-exception", "seeAlso": [ @@ -172,7 +172,7 @@ "reference": "./DigiRule-FOSS-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./DigiRule-FOSS-exception.html", - "referenceNumber": 16, + "referenceNumber": 66, "name": "DigiRule FOSS License Exception", "licenseExceptionId": "DigiRule-FOSS-exception", "seeAlso": [ @@ -183,18 +183,31 @@ "reference": "./eCos-exception-2.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./eCos-exception-2.0.html", - "referenceNumber": 45, + "referenceNumber": 35, "name": "eCos exception 2.0", "licenseExceptionId": "eCos-exception-2.0", "seeAlso": [ "http://ecos.sourceware.org/license-overview.html" ] }, + { + "reference": "./erlang-otp-linking-exception.json", + "isDeprecatedLicenseId": false, + "detailsUrl": "./erlang-otp-linking-exception.html", + "referenceNumber": 46, + "name": "Erlang/OTP Linking Exception", + "licenseExceptionId": "erlang-otp-linking-exception", + "seeAlso": [ + "https://www.gnu.org/licenses/gpl-faq.en.html#GPLIncompatibleLibs", + "https://erlang.org/pipermail/erlang-questions/2012-May/066355.html", + "https://gitea.osmocom.org/erlang/osmo_ss7/src/commit/2286c1b8738d715950026650bf53f19a69d6ed0e/src/ss7_links.erl#L20" + ] + }, { "reference": "./Fawkes-Runtime-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Fawkes-Runtime-exception.html", - "referenceNumber": 31, + "referenceNumber": 23, "name": "Fawkes Runtime Exception", "licenseExceptionId": "Fawkes-Runtime-exception", "seeAlso": [ @@ -205,7 +218,7 @@ "reference": "./FLTK-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./FLTK-exception.html", - "referenceNumber": 59, + "referenceNumber": 9, "name": "FLTK exception", "licenseExceptionId": "FLTK-exception", "seeAlso": [ @@ -216,7 +229,7 @@ "reference": "./fmt-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./fmt-exception.html", - "referenceNumber": 48, + "referenceNumber": 69, "name": "fmt exception", "licenseExceptionId": "fmt-exception", "seeAlso": [ @@ -228,7 +241,7 @@ "reference": "./Font-exception-2.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Font-exception-2.0.html", - "referenceNumber": 62, + "referenceNumber": 19, "name": "Font exception 2.0", "licenseExceptionId": "Font-exception-2.0", "seeAlso": [ @@ -239,7 +252,7 @@ "reference": "./freertos-exception-2.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./freertos-exception-2.0.html", - "referenceNumber": 14, + "referenceNumber": 54, "name": "FreeRTOS Exception 2.0", "licenseExceptionId": "freertos-exception-2.0", "seeAlso": [ @@ -250,7 +263,7 @@ "reference": "./GCC-exception-2.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./GCC-exception-2.0.html", - "referenceNumber": 29, + "referenceNumber": 14, "name": "GCC Runtime Library exception 2.0", "licenseExceptionId": "GCC-exception-2.0", "seeAlso": [ @@ -262,7 +275,7 @@ "reference": "./GCC-exception-2.0-note.json", "isDeprecatedLicenseId": false, "detailsUrl": "./GCC-exception-2.0-note.html", - "referenceNumber": 1, + "referenceNumber": 55, "name": "GCC Runtime Library exception 2.0 - note variant", "licenseExceptionId": "GCC-exception-2.0-note", "seeAlso": [ @@ -273,7 +286,7 @@ "reference": "./GCC-exception-3.1.json", "isDeprecatedLicenseId": false, "detailsUrl": "./GCC-exception-3.1.html", - "referenceNumber": 64, + "referenceNumber": 6, "name": "GCC Runtime Library exception 3.1", "licenseExceptionId": "GCC-exception-3.1", "seeAlso": [ @@ -284,7 +297,7 @@ "reference": "./Gmsh-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Gmsh-exception.html", - "referenceNumber": 25, + "referenceNumber": 58, "name": "Gmsh exception\u003e", "licenseExceptionId": "Gmsh-exception", "seeAlso": [ @@ -295,7 +308,7 @@ "reference": "./GNAT-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./GNAT-exception.html", - "referenceNumber": 18, + "referenceNumber": 26, "name": "GNAT exception", "licenseExceptionId": "GNAT-exception", "seeAlso": [ @@ -306,7 +319,7 @@ "reference": "./GNOME-examples-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./GNOME-examples-exception.html", - "referenceNumber": 40, + "referenceNumber": 12, "name": "GNOME examples exception", "licenseExceptionId": "GNOME-examples-exception", "seeAlso": [ @@ -318,7 +331,7 @@ "reference": "./GNU-compiler-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./GNU-compiler-exception.html", - "referenceNumber": 9, + "referenceNumber": 18, "name": "GNU Compiler Exception", "licenseExceptionId": "GNU-compiler-exception", "seeAlso": [ @@ -329,7 +342,7 @@ "reference": "./gnu-javamail-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./gnu-javamail-exception.html", - "referenceNumber": 20, + "referenceNumber": 43, "name": "GNU JavaMail exception", "licenseExceptionId": "gnu-javamail-exception", "seeAlso": [ @@ -340,7 +353,7 @@ "reference": "./GPL-3.0-interface-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./GPL-3.0-interface-exception.html", - "referenceNumber": 3, + "referenceNumber": 28, "name": "GPL-3.0 Interface Exception", "licenseExceptionId": "GPL-3.0-interface-exception", "seeAlso": [ @@ -351,7 +364,7 @@ "reference": "./GPL-3.0-linking-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./GPL-3.0-linking-exception.html", - "referenceNumber": 41, + "referenceNumber": 45, "name": "GPL-3.0 Linking Exception", "licenseExceptionId": "GPL-3.0-linking-exception", "seeAlso": [ @@ -362,7 +375,7 @@ "reference": "./GPL-3.0-linking-source-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./GPL-3.0-linking-source-exception.html", - "referenceNumber": 68, + "referenceNumber": 39, "name": "GPL-3.0 Linking Exception (with Corresponding Source)", "licenseExceptionId": "GPL-3.0-linking-source-exception", "seeAlso": [ @@ -374,7 +387,7 @@ "reference": "./GPL-CC-1.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./GPL-CC-1.0.html", - "referenceNumber": 50, + "referenceNumber": 27, "name": "GPL Cooperation Commitment 1.0", "licenseExceptionId": "GPL-CC-1.0", "seeAlso": [ @@ -386,7 +399,7 @@ "reference": "./GStreamer-exception-2005.json", "isDeprecatedLicenseId": false, "detailsUrl": "./GStreamer-exception-2005.html", - "referenceNumber": 61, + "referenceNumber": 63, "name": "GStreamer Exception (2005)", "licenseExceptionId": "GStreamer-exception-2005", "seeAlso": [ @@ -397,7 +410,7 @@ "reference": "./GStreamer-exception-2008.json", "isDeprecatedLicenseId": false, "detailsUrl": "./GStreamer-exception-2008.html", - "referenceNumber": 10, + "referenceNumber": 30, "name": "GStreamer Exception (2008)", "licenseExceptionId": "GStreamer-exception-2008", "seeAlso": [ @@ -408,7 +421,7 @@ "reference": "./i2p-gpl-java-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./i2p-gpl-java-exception.html", - "referenceNumber": 13, + "referenceNumber": 36, "name": "i2p GPL+Java Exception", "licenseExceptionId": "i2p-gpl-java-exception", "seeAlso": [ @@ -419,7 +432,7 @@ "reference": "./KiCad-libraries-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./KiCad-libraries-exception.html", - "referenceNumber": 8, + "referenceNumber": 10, "name": "KiCad Libraries Exception", "licenseExceptionId": "KiCad-libraries-exception", "seeAlso": [ @@ -430,7 +443,7 @@ "reference": "./LGPL-3.0-linking-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./LGPL-3.0-linking-exception.html", - "referenceNumber": 7, + "referenceNumber": 31, "name": "LGPL-3.0 Linking Exception", "licenseExceptionId": "LGPL-3.0-linking-exception", "seeAlso": [ @@ -443,7 +456,7 @@ "reference": "./libpri-OpenH323-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./libpri-OpenH323-exception.html", - "referenceNumber": 5, + "referenceNumber": 15, "name": "libpri OpenH323 exception", "licenseExceptionId": "libpri-OpenH323-exception", "seeAlso": [ @@ -454,7 +467,7 @@ "reference": "./Libtool-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Libtool-exception.html", - "referenceNumber": 63, + "referenceNumber": 20, "name": "Libtool Exception", "licenseExceptionId": "Libtool-exception", "seeAlso": [ @@ -466,7 +479,7 @@ "reference": "./Linux-syscall-note.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Linux-syscall-note.html", - "referenceNumber": 54, + "referenceNumber": 52, "name": "Linux Syscall Note", "licenseExceptionId": "Linux-syscall-note", "seeAlso": [ @@ -477,7 +490,7 @@ "reference": "./LLGPL.json", "isDeprecatedLicenseId": false, "detailsUrl": "./LLGPL.html", - "referenceNumber": 19, + "referenceNumber": 37, "name": "LLGPL Preamble", "licenseExceptionId": "LLGPL", "seeAlso": [ @@ -488,7 +501,7 @@ "reference": "./LLVM-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./LLVM-exception.html", - "referenceNumber": 57, + "referenceNumber": 1, "name": "LLVM Exception", "licenseExceptionId": "LLVM-exception", "seeAlso": [ @@ -499,7 +512,7 @@ "reference": "./LZMA-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./LZMA-exception.html", - "referenceNumber": 2, + "referenceNumber": 61, "name": "LZMA exception", "licenseExceptionId": "LZMA-exception", "seeAlso": [ @@ -510,7 +523,7 @@ "reference": "./mif-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./mif-exception.html", - "referenceNumber": 6, + "referenceNumber": 7, "name": "Macros and Inline Functions Exception", "licenseExceptionId": "mif-exception", "seeAlso": [ @@ -523,7 +536,7 @@ "reference": "./Nokia-Qt-exception-1.1.json", "isDeprecatedLicenseId": true, "detailsUrl": "./Nokia-Qt-exception-1.1.html", - "referenceNumber": 51, + "referenceNumber": 13, "name": "Nokia Qt LGPL exception 1.1", "licenseExceptionId": "Nokia-Qt-exception-1.1", "seeAlso": [ @@ -534,7 +547,7 @@ "reference": "./OCaml-LGPL-linking-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./OCaml-LGPL-linking-exception.html", - "referenceNumber": 43, + "referenceNumber": 2, "name": "OCaml LGPL Linking Exception", "licenseExceptionId": "OCaml-LGPL-linking-exception", "seeAlso": [ @@ -545,7 +558,7 @@ "reference": "./OCCT-exception-1.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./OCCT-exception-1.0.html", - "referenceNumber": 56, + "referenceNumber": 49, "name": "Open CASCADE Exception 1.0", "licenseExceptionId": "OCCT-exception-1.0", "seeAlso": [ @@ -556,7 +569,7 @@ "reference": "./OpenJDK-assembly-exception-1.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./OpenJDK-assembly-exception-1.0.html", - "referenceNumber": 35, + "referenceNumber": 44, "name": "OpenJDK Assembly exception 1.0", "licenseExceptionId": "OpenJDK-assembly-exception-1.0", "seeAlso": [ @@ -567,7 +580,7 @@ "reference": "./openvpn-openssl-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./openvpn-openssl-exception.html", - "referenceNumber": 55, + "referenceNumber": 29, "name": "OpenVPN OpenSSL Exception", "licenseExceptionId": "openvpn-openssl-exception", "seeAlso": [ @@ -579,7 +592,7 @@ "reference": "./PCRE2-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./PCRE2-exception.html", - "referenceNumber": 27, + "referenceNumber": 8, "name": "PCRE2 exception", "licenseExceptionId": "PCRE2-exception", "seeAlso": [ @@ -590,7 +603,7 @@ "reference": "./PS-or-PDF-font-exception-20170817.json", "isDeprecatedLicenseId": false, "detailsUrl": "./PS-or-PDF-font-exception-20170817.html", - "referenceNumber": 67, + "referenceNumber": 16, "name": "PS/PDF font exception (2017-08-17)", "licenseExceptionId": "PS-or-PDF-font-exception-20170817", "seeAlso": [ @@ -601,7 +614,7 @@ "reference": "./QPL-1.0-INRIA-2004-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./QPL-1.0-INRIA-2004-exception.html", - "referenceNumber": 47, + "referenceNumber": 68, "name": "INRIA QPL 1.0 2004 variant exception", "licenseExceptionId": "QPL-1.0-INRIA-2004-exception", "seeAlso": [ @@ -613,7 +626,7 @@ "reference": "./Qt-GPL-exception-1.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Qt-GPL-exception-1.0.html", - "referenceNumber": 46, + "referenceNumber": 50, "name": "Qt GPL exception 1.0", "licenseExceptionId": "Qt-GPL-exception-1.0", "seeAlso": [ @@ -624,7 +637,7 @@ "reference": "./Qt-LGPL-exception-1.1.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Qt-LGPL-exception-1.1.html", - "referenceNumber": 65, + "referenceNumber": 38, "name": "Qt LGPL exception 1.1", "licenseExceptionId": "Qt-LGPL-exception-1.1", "seeAlso": [ @@ -635,18 +648,34 @@ "reference": "./Qwt-exception-1.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Qwt-exception-1.0.html", - "referenceNumber": 58, + "referenceNumber": 25, "name": "Qwt exception 1.0", "licenseExceptionId": "Qwt-exception-1.0", "seeAlso": [ "http://qwt.sourceforge.net/qwtlicense.html" ] }, + { + "reference": "./romic-exception.json", + "isDeprecatedLicenseId": false, + "detailsUrl": "./romic-exception.html", + "referenceNumber": 22, + "name": "Romic Exception", + "licenseExceptionId": "romic-exception", + "seeAlso": [ + "https://web.archive.org/web/20210124015834/http://mo.morsi.org/blog/2009/08/13/lesser_affero_gplv3/", + "https://sourceforge.net/p/romic/code/ci/3ab2856180cf0d8b007609af53154cf092efc58f/tree/COPYING", + "https://github.com/moll/node-mitm/blob/bbf24b8bd7596dc6e091e625363161ce91984fc7/LICENSE#L8-L11", + "https://github.com/zenbones/SmallMind/blob/3c62b5995fe7f27c453f140ff9b60560a0893f2a/COPYRIGHT#L25-L30", + "https://github.com/CubeArtisan/cubeartisan/blob/2c6ab53455237b88a3ea07be02a838a135c4ab79/LICENSE.LESSER#L10-L15", + "https://github.com/savearray2/py.js/blob/b781273c08c8afa89f4954de4ecf42ec01429bae/README.md#license" + ] + }, { "reference": "./RRDtool-FLOSS-exception-2.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./RRDtool-FLOSS-exception-2.0.html", - "referenceNumber": 22, + "referenceNumber": 4, "name": "RRDtool FLOSS exception 2.0", "licenseExceptionId": "RRDtool-FLOSS-exception-2.0", "seeAlso": [ @@ -658,7 +687,7 @@ "reference": "./SANE-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./SANE-exception.html", - "referenceNumber": 12, + "referenceNumber": 11, "name": "SANE Exception", "licenseExceptionId": "SANE-exception", "seeAlso": [ @@ -671,7 +700,7 @@ "reference": "./SHL-2.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./SHL-2.0.html", - "referenceNumber": 17, + "referenceNumber": 56, "name": "Solderpad Hardware License v2.0", "licenseExceptionId": "SHL-2.0", "seeAlso": [ @@ -682,7 +711,7 @@ "reference": "./SHL-2.1.json", "isDeprecatedLicenseId": false, "detailsUrl": "./SHL-2.1.html", - "referenceNumber": 32, + "referenceNumber": 65, "name": "Solderpad Hardware License v2.1", "licenseExceptionId": "SHL-2.1", "seeAlso": [ @@ -693,7 +722,7 @@ "reference": "./stunnel-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./stunnel-exception.html", - "referenceNumber": 66, + "referenceNumber": 70, "name": "stunnel Exception", "licenseExceptionId": "stunnel-exception", "seeAlso": [ @@ -704,7 +733,7 @@ "reference": "./SWI-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./SWI-exception.html", - "referenceNumber": 36, + "referenceNumber": 41, "name": "SWI exception", "licenseExceptionId": "SWI-exception", "seeAlso": [ @@ -715,7 +744,7 @@ "reference": "./Swift-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Swift-exception.html", - "referenceNumber": 37, + "referenceNumber": 33, "name": "Swift Exception", "licenseExceptionId": "Swift-exception", "seeAlso": [ @@ -727,7 +756,7 @@ "reference": "./Texinfo-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Texinfo-exception.html", - "referenceNumber": 23, + "referenceNumber": 67, "name": "Texinfo exception", "licenseExceptionId": "Texinfo-exception", "seeAlso": [ @@ -738,7 +767,7 @@ "reference": "./u-boot-exception-2.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./u-boot-exception-2.0.html", - "referenceNumber": 69, + "referenceNumber": 3, "name": "U-Boot exception 2.0", "licenseExceptionId": "u-boot-exception-2.0", "seeAlso": [ @@ -749,7 +778,7 @@ "reference": "./UBDL-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./UBDL-exception.html", - "referenceNumber": 53, + "referenceNumber": 62, "name": "Unmodified Binary Distribution exception", "licenseExceptionId": "UBDL-exception", "seeAlso": [ @@ -760,7 +789,7 @@ "reference": "./Universal-FOSS-exception-1.0.json", "isDeprecatedLicenseId": false, "detailsUrl": "./Universal-FOSS-exception-1.0.html", - "referenceNumber": 38, + "referenceNumber": 42, "name": "Universal FOSS Exception, Version 1.0", "licenseExceptionId": "Universal-FOSS-exception-1.0", "seeAlso": [ @@ -771,7 +800,7 @@ "reference": "./vsftpd-openssl-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./vsftpd-openssl-exception.html", - "referenceNumber": 33, + "referenceNumber": 32, "name": "vsftpd OpenSSL exception", "licenseExceptionId": "vsftpd-openssl-exception", "seeAlso": [ @@ -784,7 +813,7 @@ "reference": "./WxWindows-exception-3.1.json", "isDeprecatedLicenseId": false, "detailsUrl": "./WxWindows-exception-3.1.html", - "referenceNumber": 28, + "referenceNumber": 57, "name": "WxWindows Library Exception 3.1", "licenseExceptionId": "WxWindows-exception-3.1", "seeAlso": [ @@ -795,7 +824,7 @@ "reference": "./x11vnc-openssl-exception.json", "isDeprecatedLicenseId": false, "detailsUrl": "./x11vnc-openssl-exception.html", - "referenceNumber": 34, + "referenceNumber": 47, "name": "x11vnc OpenSSL Exception", "licenseExceptionId": "x11vnc-openssl-exception", "seeAlso": [ @@ -803,5 +832,5 @@ ] } ], - "releaseDate": "2024-05-22" + "releaseDate": "2024-08-19" } \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/exceptions/erlang-otp-linking-exception.json b/src/main/resources/license-list-data/json/exceptions/erlang-otp-linking-exception.json new file mode 100644 index 0000000000..a8234ee41f --- /dev/null +++ b/src/main/resources/license-list-data/json/exceptions/erlang-otp-linking-exception.json @@ -0,0 +1,14 @@ +{ + "isDeprecatedLicenseId": false, + "licenseExceptionText": "If you modify this Program, or any covered work, by linking or\ncombining it with runtime libraries of Erlang/OTP as released by\nEricsson on https://www.erlang.org (or a modified version of these\nlibraries), containing parts covered by the terms of the Erlang Public\nLicense (https://www.erlang.org/EPLICENSE), the licensors of this\nProgram grant you additional permission to convey the resulting work\nwithout the need to license the runtime libraries of Erlang/OTP under\nthe GNU Affero General Public License. Corresponding Source for a\nnon-source form of such a combination shall include the source code\nfor the parts of the runtime libraries of Erlang/OTP used as well as\nthat of the covered work.\n", + "name": "Erlang/OTP Linking Exception", + "licenseComments": "This exception is based on the suggested template from the Free Software Foundation\u0027s FAQ about the GPL.", + "seeAlso": [ + "https://www.gnu.org/licenses/gpl-faq.en.html#GPLIncompatibleLibs", + "https://erlang.org/pipermail/erlang-questions/2012-May/066355.html", + "https://gitea.osmocom.org/erlang/osmo_ss7/src/commit/2286c1b8738d715950026650bf53f19a69d6ed0e/src/ss7_links.erl#L20" + ], + "licenseExceptionId": "erlang-otp-linking-exception", + "licenseExceptionTemplate": "\u003c\u003cbeginOptional\u003e\u003eAdditional permission under GNU AGPL version 3 section 7\n\n\u003c\u003cendOptional\u003e\u003e\n\nIf you modify this Program, or any covered work, by linking or combining it with runtime libraries of Erlang/OTP as released by Ericsson on https://www.erlang.org (or a modified version of these libraries), containing parts covered by the terms of the Erlang Public License (https://www.erlang.org/EPLICENSE), \u003c\u003cvar;name\u003d\"licensors\";original\u003d\"the licensors of this Program\";match\u003d\".+\"\u003e\u003e grant you additional permission to convey the resulting work without the need to license the runtime libraries of Erlang/OTP under the \u003c\u003cvar;name\u003d\"libraryLicense2\";original\u003d\"[name of library\u0027s license]\";match\u003d\".+\"\u003e\u003e . Corresponding Source for a non-source form of such a combination shall include the source code for the parts of the runtime libraries of Erlang/OTP used as well as that of the covered work.\n\n", + "exceptionTextHtml": "\n\t\u003cdiv class\u003d\"optional-license-text\"\u003e \n\t\u003cp\u003e\n\t Additional permission under GNU AGPL version 3 section 7\n\t\u003c/p\u003e\n\n\t\u003c/div\u003e\n \u003cp\u003e\n\tIf you modify this Program, or any covered work, by linking or\n\tcombining it with runtime libraries of Erlang/OTP as released by\n\tEricsson on https://www.erlang.org (or a modified version of these\n\tlibraries), containing parts covered by the terms of the Erlang Public\n\tLicense (https://www.erlang.org/EPLICENSE),\n\t\u003cvar class\u003d\"replaceable-license-text\"\u003e the licensors of this Program\u003c/var\u003e\n\tgrant you additional permission to convey the resulting work\n\twithout the need to license the runtime libraries of Erlang/OTP under\n\tthe \u003cvar class\u003d\"replaceable-license-text\"\u003e [name of library\u0026apos;s license]\u003c/var\u003e.\n\tCorresponding Source for a non-source form of such a combination shall include\n\tthe source code for the parts of the runtime libraries of Erlang/OTP used as\n\twell as that of the covered work.\n \u003c/p\u003e\n\n " +} \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/exceptions/romic-exception.json b/src/main/resources/license-list-data/json/exceptions/romic-exception.json new file mode 100644 index 0000000000..834171c101 --- /dev/null +++ b/src/main/resources/license-list-data/json/exceptions/romic-exception.json @@ -0,0 +1,16 @@ +{ + "isDeprecatedLicenseId": false, + "licenseExceptionText": "Additional permission under the GNU Affero GPL version 3 section 7:\n\nIf you modify this Program, or any covered work, by linking or\ncombining it with other code, such other code is not for that reason\nalone subject to any of the requirements of the GNU Affero GPL\nversion 3.\n", + "name": "Romic Exception", + "seeAlso": [ + "https://web.archive.org/web/20210124015834/http://mo.morsi.org/blog/2009/08/13/lesser_affero_gplv3/", + "https://sourceforge.net/p/romic/code/ci/3ab2856180cf0d8b007609af53154cf092efc58f/tree/COPYING", + "https://github.com/moll/node-mitm/blob/bbf24b8bd7596dc6e091e625363161ce91984fc7/LICENSE#L8-L11", + "https://github.com/zenbones/SmallMind/blob/3c62b5995fe7f27c453f140ff9b60560a0893f2a/COPYRIGHT#L25-L30", + "https://github.com/CubeArtisan/cubeartisan/blob/2c6ab53455237b88a3ea07be02a838a135c4ab79/LICENSE.LESSER#L10-L15", + "https://github.com/savearray2/py.js/blob/b781273c08c8afa89f4954de4ecf42ec01429bae/README.md#license" + ], + "licenseExceptionId": "romic-exception", + "licenseExceptionTemplate": "Additional permission under the GNU Affero GPL version 3 section 7:\n\nIf you modify this Program, or any covered work, by linking or combining it with other code, such other code is not for that reason alone subject to any of the requirements of the GNU Affero GPL version 3.\n\n", + "exceptionTextHtml": "\n \u003cp\u003e\n Additional permission under the GNU Affero GPL version 3 section 7:\n \u003c/p\u003e\n\n \u003cp\u003e\n If you modify this Program, or any covered work, by linking or\n combining it with other code, such other code is not for that reason\n alone subject to any of the requirements of the GNU Affero GPL\n version 3.\n \u003c/p\u003e\n\n " +} \ No newline at end of file diff --git a/src/main/resources/license-list-data/json/licenses.json b/src/main/resources/license-list-data/json/licenses.json index 9596a3b098..6c1a1e1337 100644 --- a/src/main/resources/license-list-data/json/licenses.json +++ b/src/main/resources/license-list-data/json/licenses.json @@ -1,11 +1,11 @@ { - "licenseListVersion": "3.24.0", + "licenseListVersion": "3.25.0", "licenses": [ { "reference": "https://spdx.org/licenses/0BSD.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/0BSD.json", - "referenceNumber": 537, + "referenceNumber": 582, "name": "BSD Zero Clause License", "licenseId": "0BSD", "seeAlso": [ @@ -18,7 +18,7 @@ "reference": "https://spdx.org/licenses/3D-Slicer-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/3D-Slicer-1.0.json", - "referenceNumber": 200, + "referenceNumber": 466, "name": "3D Slicer License v1.0", "licenseId": "3D-Slicer-1.0", "seeAlso": [ @@ -31,7 +31,7 @@ "reference": "https://spdx.org/licenses/AAL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AAL.json", - "referenceNumber": 406, + "referenceNumber": 252, "name": "Attribution Assurance License", "licenseId": "AAL", "seeAlso": [ @@ -43,7 +43,7 @@ "reference": "https://spdx.org/licenses/Abstyles.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Abstyles.json", - "referenceNumber": 526, + "referenceNumber": 456, "name": "Abstyles License", "licenseId": "Abstyles", "seeAlso": [ @@ -55,7 +55,7 @@ "reference": "https://spdx.org/licenses/AdaCore-doc.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AdaCore-doc.json", - "referenceNumber": 382, + "referenceNumber": 355, "name": "AdaCore Doc License", "licenseId": "AdaCore-doc", "seeAlso": [ @@ -69,7 +69,7 @@ "reference": "https://spdx.org/licenses/Adobe-2006.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Adobe-2006.json", - "referenceNumber": 558, + "referenceNumber": 128, "name": "Adobe Systems Incorporated Source Code License Agreement", "licenseId": "Adobe-2006", "seeAlso": [ @@ -81,7 +81,7 @@ "reference": "https://spdx.org/licenses/Adobe-Display-PostScript.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Adobe-Display-PostScript.json", - "referenceNumber": 431, + "referenceNumber": 433, "name": "Adobe Display PostScript License", "licenseId": "Adobe-Display-PostScript", "seeAlso": [ @@ -93,7 +93,7 @@ "reference": "https://spdx.org/licenses/Adobe-Glyph.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Adobe-Glyph.json", - "referenceNumber": 297, + "referenceNumber": 125, "name": "Adobe Glyph List License", "licenseId": "Adobe-Glyph", "seeAlso": [ @@ -105,7 +105,7 @@ "reference": "https://spdx.org/licenses/Adobe-Utopia.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Adobe-Utopia.json", - "referenceNumber": 532, + "referenceNumber": 495, "name": "Adobe Utopia Font License", "licenseId": "Adobe-Utopia", "seeAlso": [ @@ -117,7 +117,7 @@ "reference": "https://spdx.org/licenses/ADSL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ADSL.json", - "referenceNumber": 463, + "referenceNumber": 560, "name": "Amazon Digital Services License", "licenseId": "ADSL", "seeAlso": [ @@ -129,7 +129,7 @@ "reference": "https://spdx.org/licenses/AFL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AFL-1.1.json", - "referenceNumber": 601, + "referenceNumber": 14, "name": "Academic Free License v1.1", "licenseId": "AFL-1.1", "seeAlso": [ @@ -143,7 +143,7 @@ "reference": "https://spdx.org/licenses/AFL-1.2.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AFL-1.2.json", - "referenceNumber": 72, + "referenceNumber": 622, "name": "Academic Free License v1.2", "licenseId": "AFL-1.2", "seeAlso": [ @@ -157,7 +157,7 @@ "reference": "https://spdx.org/licenses/AFL-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AFL-2.0.json", - "referenceNumber": 187, + "referenceNumber": 559, "name": "Academic Free License v2.0", "licenseId": "AFL-2.0", "seeAlso": [ @@ -170,7 +170,7 @@ "reference": "https://spdx.org/licenses/AFL-2.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AFL-2.1.json", - "referenceNumber": 383, + "referenceNumber": 570, "name": "Academic Free License v2.1", "licenseId": "AFL-2.1", "seeAlso": [ @@ -183,7 +183,7 @@ "reference": "https://spdx.org/licenses/AFL-3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AFL-3.0.json", - "referenceNumber": 369, + "referenceNumber": 332, "name": "Academic Free License v3.0", "licenseId": "AFL-3.0", "seeAlso": [ @@ -197,7 +197,7 @@ "reference": "https://spdx.org/licenses/Afmparse.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Afmparse.json", - "referenceNumber": 345, + "referenceNumber": 163, "name": "Afmparse License", "licenseId": "Afmparse", "seeAlso": [ @@ -209,7 +209,7 @@ "reference": "https://spdx.org/licenses/AGPL-1.0.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/AGPL-1.0.json", - "referenceNumber": 221, + "referenceNumber": 657, "name": "Affero General Public License v1.0", "licenseId": "AGPL-1.0", "seeAlso": [ @@ -222,7 +222,7 @@ "reference": "https://spdx.org/licenses/AGPL-1.0-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AGPL-1.0-only.json", - "referenceNumber": 334, + "referenceNumber": 142, "name": "Affero General Public License v1.0 only", "licenseId": "AGPL-1.0-only", "seeAlso": [ @@ -234,7 +234,7 @@ "reference": "https://spdx.org/licenses/AGPL-1.0-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AGPL-1.0-or-later.json", - "referenceNumber": 527, + "referenceNumber": 155, "name": "Affero General Public License v1.0 or later", "licenseId": "AGPL-1.0-or-later", "seeAlso": [ @@ -246,7 +246,7 @@ "reference": "https://spdx.org/licenses/AGPL-3.0.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/AGPL-3.0.json", - "referenceNumber": 394, + "referenceNumber": 70, "name": "GNU Affero General Public License v3.0", "licenseId": "AGPL-3.0", "seeAlso": [ @@ -260,7 +260,7 @@ "reference": "https://spdx.org/licenses/AGPL-3.0-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AGPL-3.0-only.json", - "referenceNumber": 123, + "referenceNumber": 330, "name": "GNU Affero General Public License v3.0 only", "licenseId": "AGPL-3.0-only", "seeAlso": [ @@ -274,7 +274,7 @@ "reference": "https://spdx.org/licenses/AGPL-3.0-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AGPL-3.0-or-later.json", - "referenceNumber": 105, + "referenceNumber": 366, "name": "GNU Affero General Public License v3.0 or later", "licenseId": "AGPL-3.0-or-later", "seeAlso": [ @@ -288,7 +288,7 @@ "reference": "https://spdx.org/licenses/Aladdin.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Aladdin.json", - "referenceNumber": 168, + "referenceNumber": 557, "name": "Aladdin Free Public License", "licenseId": "Aladdin", "seeAlso": [ @@ -301,7 +301,7 @@ "reference": "https://spdx.org/licenses/AMD-newlib.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AMD-newlib.json", - "referenceNumber": 222, + "referenceNumber": 340, "name": "AMD newlib License", "licenseId": "AMD-newlib", "seeAlso": [ @@ -313,7 +313,7 @@ "reference": "https://spdx.org/licenses/AMDPLPA.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AMDPLPA.json", - "referenceNumber": 149, + "referenceNumber": 467, "name": "AMD\u0027s plpa_map.c License", "licenseId": "AMDPLPA", "seeAlso": [ @@ -325,7 +325,7 @@ "reference": "https://spdx.org/licenses/AML.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AML.json", - "referenceNumber": 13, + "referenceNumber": 299, "name": "Apple MIT License", "licenseId": "AML", "seeAlso": [ @@ -337,7 +337,7 @@ "reference": "https://spdx.org/licenses/AML-glslang.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AML-glslang.json", - "referenceNumber": 1, + "referenceNumber": 567, "name": "AML glslang variant License", "licenseId": "AML-glslang", "seeAlso": [ @@ -350,7 +350,7 @@ "reference": "https://spdx.org/licenses/AMPAS.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/AMPAS.json", - "referenceNumber": 420, + "referenceNumber": 414, "name": "Academy of Motion Picture Arts and Sciences BSD", "licenseId": "AMPAS", "seeAlso": [ @@ -362,7 +362,7 @@ "reference": "https://spdx.org/licenses/ANTLR-PD.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ANTLR-PD.json", - "referenceNumber": 576, + "referenceNumber": 460, "name": "ANTLR Software Rights Notice", "licenseId": "ANTLR-PD", "seeAlso": [ @@ -374,7 +374,7 @@ "reference": "https://spdx.org/licenses/ANTLR-PD-fallback.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ANTLR-PD-fallback.json", - "referenceNumber": 194, + "referenceNumber": 65, "name": "ANTLR Software Rights Notice with license fallback", "licenseId": "ANTLR-PD-fallback", "seeAlso": [ @@ -386,7 +386,7 @@ "reference": "https://spdx.org/licenses/any-OSI.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/any-OSI.json", - "referenceNumber": 121, + "referenceNumber": 310, "name": "Any OSI License", "licenseId": "any-OSI", "seeAlso": [ @@ -398,7 +398,7 @@ "reference": "https://spdx.org/licenses/Apache-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Apache-1.0.json", - "referenceNumber": 616, + "referenceNumber": 250, "name": "Apache License 1.0", "licenseId": "Apache-1.0", "seeAlso": [ @@ -411,7 +411,7 @@ "reference": "https://spdx.org/licenses/Apache-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Apache-1.1.json", - "referenceNumber": 313, + "referenceNumber": 288, "name": "Apache License 1.1", "licenseId": "Apache-1.1", "seeAlso": [ @@ -425,7 +425,7 @@ "reference": "https://spdx.org/licenses/Apache-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Apache-2.0.json", - "referenceNumber": 564, + "referenceNumber": 143, "name": "Apache License 2.0", "licenseId": "Apache-2.0", "seeAlso": [ @@ -439,7 +439,7 @@ "reference": "https://spdx.org/licenses/APAFML.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/APAFML.json", - "referenceNumber": 136, + "referenceNumber": 636, "name": "Adobe Postscript AFM License", "licenseId": "APAFML", "seeAlso": [ @@ -451,7 +451,7 @@ "reference": "https://spdx.org/licenses/APL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/APL-1.0.json", - "referenceNumber": 515, + "referenceNumber": 85, "name": "Adaptive Public License 1.0", "licenseId": "APL-1.0", "seeAlso": [ @@ -463,7 +463,7 @@ "reference": "https://spdx.org/licenses/App-s2p.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/App-s2p.json", - "referenceNumber": 470, + "referenceNumber": 238, "name": "App::s2p License", "licenseId": "App-s2p", "seeAlso": [ @@ -475,7 +475,7 @@ "reference": "https://spdx.org/licenses/APSL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/APSL-1.0.json", - "referenceNumber": 39, + "referenceNumber": 335, "name": "Apple Public Source License 1.0", "licenseId": "APSL-1.0", "seeAlso": [ @@ -488,7 +488,7 @@ "reference": "https://spdx.org/licenses/APSL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/APSL-1.1.json", - "referenceNumber": 582, + "referenceNumber": 308, "name": "Apple Public Source License 1.1", "licenseId": "APSL-1.1", "seeAlso": [ @@ -500,7 +500,7 @@ "reference": "https://spdx.org/licenses/APSL-1.2.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/APSL-1.2.json", - "referenceNumber": 628, + "referenceNumber": 280, "name": "Apple Public Source License 1.2", "licenseId": "APSL-1.2", "seeAlso": [ @@ -512,7 +512,7 @@ "reference": "https://spdx.org/licenses/APSL-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/APSL-2.0.json", - "referenceNumber": 144, + "referenceNumber": 592, "name": "Apple Public Source License 2.0", "licenseId": "APSL-2.0", "seeAlso": [ @@ -525,7 +525,7 @@ "reference": "https://spdx.org/licenses/Arphic-1999.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Arphic-1999.json", - "referenceNumber": 131, + "referenceNumber": 32, "name": "Arphic Public License", "licenseId": "Arphic-1999", "seeAlso": [ @@ -537,7 +537,7 @@ "reference": "https://spdx.org/licenses/Artistic-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Artistic-1.0.json", - "referenceNumber": 388, + "referenceNumber": 138, "name": "Artistic License 1.0", "licenseId": "Artistic-1.0", "seeAlso": [ @@ -550,7 +550,7 @@ "reference": "https://spdx.org/licenses/Artistic-1.0-cl8.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Artistic-1.0-cl8.json", - "referenceNumber": 321, + "referenceNumber": 353, "name": "Artistic License 1.0 w/clause 8", "licenseId": "Artistic-1.0-cl8", "seeAlso": [ @@ -562,7 +562,7 @@ "reference": "https://spdx.org/licenses/Artistic-1.0-Perl.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Artistic-1.0-Perl.json", - "referenceNumber": 652, + "referenceNumber": 660, "name": "Artistic License 1.0 (Perl)", "licenseId": "Artistic-1.0-Perl", "seeAlso": [ @@ -574,7 +574,7 @@ "reference": "https://spdx.org/licenses/Artistic-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Artistic-2.0.json", - "referenceNumber": 355, + "referenceNumber": 277, "name": "Artistic License 2.0", "licenseId": "Artistic-2.0", "seeAlso": [ @@ -589,7 +589,7 @@ "reference": "https://spdx.org/licenses/ASWF-Digital-Assets-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ASWF-Digital-Assets-1.0.json", - "referenceNumber": 330, + "referenceNumber": 166, "name": "ASWF Digital Assets License version 1.0", "licenseId": "ASWF-Digital-Assets-1.0", "seeAlso": [ @@ -601,7 +601,7 @@ "reference": "https://spdx.org/licenses/ASWF-Digital-Assets-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ASWF-Digital-Assets-1.1.json", - "referenceNumber": 447, + "referenceNumber": 29, "name": "ASWF Digital Assets License 1.1", "licenseId": "ASWF-Digital-Assets-1.1", "seeAlso": [ @@ -613,7 +613,7 @@ "reference": "https://spdx.org/licenses/Baekmuk.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Baekmuk.json", - "referenceNumber": 436, + "referenceNumber": 380, "name": "Baekmuk License", "licenseId": "Baekmuk", "seeAlso": [ @@ -625,7 +625,7 @@ "reference": "https://spdx.org/licenses/Bahyph.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Bahyph.json", - "referenceNumber": 494, + "referenceNumber": 368, "name": "Bahyph License", "licenseId": "Bahyph", "seeAlso": [ @@ -637,7 +637,7 @@ "reference": "https://spdx.org/licenses/Barr.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Barr.json", - "referenceNumber": 48, + "referenceNumber": 195, "name": "Barr License", "licenseId": "Barr", "seeAlso": [ @@ -649,7 +649,7 @@ "reference": "https://spdx.org/licenses/bcrypt-Solar-Designer.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/bcrypt-Solar-Designer.json", - "referenceNumber": 27, + "referenceNumber": 478, "name": "bcrypt Solar Designer License", "licenseId": "bcrypt-Solar-Designer", "seeAlso": [ @@ -661,7 +661,7 @@ "reference": "https://spdx.org/licenses/Beerware.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Beerware.json", - "referenceNumber": 143, + "referenceNumber": 616, "name": "Beerware License", "licenseId": "Beerware", "seeAlso": [ @@ -674,7 +674,7 @@ "reference": "https://spdx.org/licenses/Bitstream-Charter.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Bitstream-Charter.json", - "referenceNumber": 560, + "referenceNumber": 455, "name": "Bitstream Charter Font License", "licenseId": "Bitstream-Charter", "seeAlso": [ @@ -687,7 +687,7 @@ "reference": "https://spdx.org/licenses/Bitstream-Vera.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Bitstream-Vera.json", - "referenceNumber": 581, + "referenceNumber": 370, "name": "Bitstream Vera Font License", "licenseId": "Bitstream-Vera", "seeAlso": [ @@ -700,7 +700,7 @@ "reference": "https://spdx.org/licenses/BitTorrent-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BitTorrent-1.0.json", - "referenceNumber": 373, + "referenceNumber": 106, "name": "BitTorrent Open Source License v1.0", "licenseId": "BitTorrent-1.0", "seeAlso": [ @@ -712,7 +712,7 @@ "reference": "https://spdx.org/licenses/BitTorrent-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BitTorrent-1.1.json", - "referenceNumber": 288, + "referenceNumber": 541, "name": "BitTorrent Open Source License v1.1", "licenseId": "BitTorrent-1.1", "seeAlso": [ @@ -725,7 +725,7 @@ "reference": "https://spdx.org/licenses/blessing.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/blessing.json", - "referenceNumber": 469, + "referenceNumber": 359, "name": "SQLite Blessing", "licenseId": "blessing", "seeAlso": [ @@ -738,7 +738,7 @@ "reference": "https://spdx.org/licenses/BlueOak-1.0.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BlueOak-1.0.0.json", - "referenceNumber": 60, + "referenceNumber": 606, "name": "Blue Oak Model License 1.0.0", "licenseId": "BlueOak-1.0.0", "seeAlso": [ @@ -750,7 +750,7 @@ "reference": "https://spdx.org/licenses/Boehm-GC.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Boehm-GC.json", - "referenceNumber": 322, + "referenceNumber": 127, "name": "Boehm-Demers-Weiser GC License", "licenseId": "Boehm-GC", "seeAlso": [ @@ -764,7 +764,7 @@ "reference": "https://spdx.org/licenses/Borceux.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Borceux.json", - "referenceNumber": 552, + "referenceNumber": 571, "name": "Borceux license", "licenseId": "Borceux", "seeAlso": [ @@ -776,7 +776,7 @@ "reference": "https://spdx.org/licenses/Brian-Gladman-2-Clause.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Brian-Gladman-2-Clause.json", - "referenceNumber": 457, + "referenceNumber": 416, "name": "Brian Gladman 2-Clause License", "licenseId": "Brian-Gladman-2-Clause", "seeAlso": [ @@ -789,7 +789,7 @@ "reference": "https://spdx.org/licenses/Brian-Gladman-3-Clause.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Brian-Gladman-3-Clause.json", - "referenceNumber": 409, + "referenceNumber": 290, "name": "Brian Gladman 3-Clause License", "licenseId": "Brian-Gladman-3-Clause", "seeAlso": [ @@ -801,7 +801,7 @@ "reference": "https://spdx.org/licenses/BSD-1-Clause.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-1-Clause.json", - "referenceNumber": 567, + "referenceNumber": 419, "name": "BSD 1-Clause License", "licenseId": "BSD-1-Clause", "seeAlso": [ @@ -813,7 +813,7 @@ "reference": "https://spdx.org/licenses/BSD-2-Clause.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause.json", - "referenceNumber": 264, + "referenceNumber": 229, "name": "BSD 2-Clause \"Simplified\" License", "licenseId": "BSD-2-Clause", "seeAlso": [ @@ -826,7 +826,7 @@ "reference": "https://spdx.org/licenses/BSD-2-Clause-Darwin.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause-Darwin.json", - "referenceNumber": 231, + "referenceNumber": 296, "name": "BSD 2-Clause - Ian Darwin variant", "licenseId": "BSD-2-Clause-Darwin", "seeAlso": [ @@ -838,7 +838,7 @@ "reference": "https://spdx.org/licenses/BSD-2-Clause-first-lines.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause-first-lines.json", - "referenceNumber": 245, + "referenceNumber": 217, "name": "BSD 2-Clause - first lines requirement", "licenseId": "BSD-2-Clause-first-lines", "seeAlso": [ @@ -851,7 +851,7 @@ "reference": "https://spdx.org/licenses/BSD-2-Clause-FreeBSD.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause-FreeBSD.json", - "referenceNumber": 192, + "referenceNumber": 564, "name": "BSD 2-Clause FreeBSD License", "licenseId": "BSD-2-Clause-FreeBSD", "seeAlso": [ @@ -864,7 +864,7 @@ "reference": "https://spdx.org/licenses/BSD-2-Clause-NetBSD.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause-NetBSD.json", - "referenceNumber": 449, + "referenceNumber": 376, "name": "BSD 2-Clause NetBSD License", "licenseId": "BSD-2-Clause-NetBSD", "seeAlso": [ @@ -877,7 +877,7 @@ "reference": "https://spdx.org/licenses/BSD-2-Clause-Patent.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause-Patent.json", - "referenceNumber": 612, + "referenceNumber": 4, "name": "BSD-2-Clause Plus Patent License", "licenseId": "BSD-2-Clause-Patent", "seeAlso": [ @@ -889,7 +889,7 @@ "reference": "https://spdx.org/licenses/BSD-2-Clause-Views.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-2-Clause-Views.json", - "referenceNumber": 657, + "referenceNumber": 514, "name": "BSD 2-Clause with views sentence", "licenseId": "BSD-2-Clause-Views", "seeAlso": [ @@ -903,7 +903,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause.json", - "referenceNumber": 216, + "referenceNumber": 584, "name": "BSD 3-Clause \"New\" or \"Revised\" License", "licenseId": "BSD-3-Clause", "seeAlso": [ @@ -917,7 +917,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause-acpica.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-acpica.json", - "referenceNumber": 408, + "referenceNumber": 341, "name": "BSD 3-Clause acpica variant", "licenseId": "BSD-3-Clause-acpica", "seeAlso": [ @@ -929,7 +929,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause-Attribution.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-Attribution.json", - "referenceNumber": 14, + "referenceNumber": 71, "name": "BSD with attribution", "licenseId": "BSD-3-Clause-Attribution", "seeAlso": [ @@ -941,7 +941,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause-Clear.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-Clear.json", - "referenceNumber": 347, + "referenceNumber": 253, "name": "BSD 3-Clause Clear License", "licenseId": "BSD-3-Clause-Clear", "seeAlso": [ @@ -954,7 +954,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause-flex.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-flex.json", - "referenceNumber": 211, + "referenceNumber": 52, "name": "BSD 3-Clause Flex variant", "licenseId": "BSD-3-Clause-flex", "seeAlso": [ @@ -966,7 +966,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause-HP.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-HP.json", - "referenceNumber": 210, + "referenceNumber": 215, "name": "Hewlett-Packard BSD variant license", "licenseId": "BSD-3-Clause-HP", "seeAlso": [ @@ -978,7 +978,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause-LBNL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-LBNL.json", - "referenceNumber": 597, + "referenceNumber": 301, "name": "Lawrence Berkeley National Labs BSD variant license", "licenseId": "BSD-3-Clause-LBNL", "seeAlso": [ @@ -990,7 +990,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause-Modification.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-Modification.json", - "referenceNumber": 364, + "referenceNumber": 47, "name": "BSD 3-Clause Modification", "licenseId": "BSD-3-Clause-Modification", "seeAlso": [ @@ -1002,7 +1002,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause-No-Military-License.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-No-Military-License.json", - "referenceNumber": 30, + "referenceNumber": 615, "name": "BSD 3-Clause No Military License", "licenseId": "BSD-3-Clause-No-Military-License", "seeAlso": [ @@ -1015,7 +1015,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.json", - "referenceNumber": 21, + "referenceNumber": 647, "name": "BSD 3-Clause No Nuclear License", "licenseId": "BSD-3-Clause-No-Nuclear-License", "seeAlso": [ @@ -1027,7 +1027,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.json", - "referenceNumber": 543, + "referenceNumber": 377, "name": "BSD 3-Clause No Nuclear License 2014", "licenseId": "BSD-3-Clause-No-Nuclear-License-2014", "seeAlso": [ @@ -1039,7 +1039,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.json", - "referenceNumber": 402, + "referenceNumber": 54, "name": "BSD 3-Clause No Nuclear Warranty", "licenseId": "BSD-3-Clause-No-Nuclear-Warranty", "seeAlso": [ @@ -1051,7 +1051,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause-Open-MPI.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-Open-MPI.json", - "referenceNumber": 376, + "referenceNumber": 633, "name": "BSD 3-Clause Open MPI variant", "licenseId": "BSD-3-Clause-Open-MPI", "seeAlso": [ @@ -1064,7 +1064,7 @@ "reference": "https://spdx.org/licenses/BSD-3-Clause-Sun.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-3-Clause-Sun.json", - "referenceNumber": 554, + "referenceNumber": 270, "name": "BSD 3-Clause Sun Microsystems", "licenseId": "BSD-3-Clause-Sun", "seeAlso": [ @@ -1076,7 +1076,7 @@ "reference": "https://spdx.org/licenses/BSD-4-Clause.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-4-Clause.json", - "referenceNumber": 650, + "referenceNumber": 470, "name": "BSD 4-Clause \"Original\" or \"Old\" License", "licenseId": "BSD-4-Clause", "seeAlso": [ @@ -1089,7 +1089,7 @@ "reference": "https://spdx.org/licenses/BSD-4-Clause-Shortened.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-4-Clause-Shortened.json", - "referenceNumber": 252, + "referenceNumber": 220, "name": "BSD 4 Clause Shortened", "licenseId": "BSD-4-Clause-Shortened", "seeAlso": [ @@ -1101,7 +1101,7 @@ "reference": "https://spdx.org/licenses/BSD-4-Clause-UC.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-4-Clause-UC.json", - "referenceNumber": 117, + "referenceNumber": 175, "name": "BSD-4-Clause (University of California-Specific)", "licenseId": "BSD-4-Clause-UC", "seeAlso": [ @@ -1113,7 +1113,7 @@ "reference": "https://spdx.org/licenses/BSD-4.3RENO.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-4.3RENO.json", - "referenceNumber": 298, + "referenceNumber": 361, "name": "BSD 4.3 RENO License", "licenseId": "BSD-4.3RENO", "seeAlso": [ @@ -1126,7 +1126,7 @@ "reference": "https://spdx.org/licenses/BSD-4.3TAHOE.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-4.3TAHOE.json", - "referenceNumber": 0, + "referenceNumber": 46, "name": "BSD 4.3 TAHOE License", "licenseId": "BSD-4.3TAHOE", "seeAlso": [ @@ -1139,7 +1139,7 @@ "reference": "https://spdx.org/licenses/BSD-Advertising-Acknowledgement.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-Advertising-Acknowledgement.json", - "referenceNumber": 423, + "referenceNumber": 297, "name": "BSD Advertising Acknowledgement License", "licenseId": "BSD-Advertising-Acknowledgement", "seeAlso": [ @@ -1151,7 +1151,7 @@ "reference": "https://spdx.org/licenses/BSD-Attribution-HPND-disclaimer.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-Attribution-HPND-disclaimer.json", - "referenceNumber": 171, + "referenceNumber": 86, "name": "BSD with Attribution and HPND disclaimer", "licenseId": "BSD-Attribution-HPND-disclaimer", "seeAlso": [ @@ -1163,7 +1163,7 @@ "reference": "https://spdx.org/licenses/BSD-Inferno-Nettverk.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-Inferno-Nettverk.json", - "referenceNumber": 401, + "referenceNumber": 89, "name": "BSD-Inferno-Nettverk", "licenseId": "BSD-Inferno-Nettverk", "seeAlso": [ @@ -1175,7 +1175,7 @@ "reference": "https://spdx.org/licenses/BSD-Protection.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-Protection.json", - "referenceNumber": 403, + "referenceNumber": 394, "name": "BSD Protection License", "licenseId": "BSD-Protection", "seeAlso": [ @@ -1187,7 +1187,7 @@ "reference": "https://spdx.org/licenses/BSD-Source-beginning-file.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-Source-beginning-file.json", - "referenceNumber": 97, + "referenceNumber": 378, "name": "BSD Source Code Attribution - beginning of file variant", "licenseId": "BSD-Source-beginning-file", "seeAlso": [ @@ -1199,7 +1199,7 @@ "reference": "https://spdx.org/licenses/BSD-Source-Code.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-Source-Code.json", - "referenceNumber": 22, + "referenceNumber": 605, "name": "BSD Source Code Attribution", "licenseId": "BSD-Source-Code", "seeAlso": [ @@ -1211,7 +1211,7 @@ "reference": "https://spdx.org/licenses/BSD-Systemics.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-Systemics.json", - "referenceNumber": 178, + "referenceNumber": 327, "name": "Systemics BSD variant license", "licenseId": "BSD-Systemics", "seeAlso": [ @@ -1223,7 +1223,7 @@ "reference": "https://spdx.org/licenses/BSD-Systemics-W3Works.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSD-Systemics-W3Works.json", - "referenceNumber": 350, + "referenceNumber": 427, "name": "Systemics W3Works BSD variant license", "licenseId": "BSD-Systemics-W3Works", "seeAlso": [ @@ -1235,7 +1235,7 @@ "reference": "https://spdx.org/licenses/BSL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BSL-1.0.json", - "referenceNumber": 514, + "referenceNumber": 334, "name": "Boost Software License 1.0", "licenseId": "BSL-1.0", "seeAlso": [ @@ -1249,7 +1249,7 @@ "reference": "https://spdx.org/licenses/BUSL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/BUSL-1.1.json", - "referenceNumber": 549, + "referenceNumber": 285, "name": "Business Source License 1.1", "licenseId": "BUSL-1.1", "seeAlso": [ @@ -1261,7 +1261,7 @@ "reference": "https://spdx.org/licenses/bzip2-1.0.5.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/bzip2-1.0.5.json", - "referenceNumber": 419, + "referenceNumber": 574, "name": "bzip2 and libbzip2 License v1.0.5", "licenseId": "bzip2-1.0.5", "seeAlso": [ @@ -1274,7 +1274,7 @@ "reference": "https://spdx.org/licenses/bzip2-1.0.6.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/bzip2-1.0.6.json", - "referenceNumber": 396, + "referenceNumber": 534, "name": "bzip2 and libbzip2 License v1.0.6", "licenseId": "bzip2-1.0.6", "seeAlso": [ @@ -1288,7 +1288,7 @@ "reference": "https://spdx.org/licenses/C-UDA-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/C-UDA-1.0.json", - "referenceNumber": 432, + "referenceNumber": 162, "name": "Computational Use of Data Agreement v1.0", "licenseId": "C-UDA-1.0", "seeAlso": [ @@ -1301,7 +1301,7 @@ "reference": "https://spdx.org/licenses/CAL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CAL-1.0.json", - "referenceNumber": 653, + "referenceNumber": 99, "name": "Cryptographic Autonomy License 1.0", "licenseId": "CAL-1.0", "seeAlso": [ @@ -1314,7 +1314,7 @@ "reference": "https://spdx.org/licenses/CAL-1.0-Combined-Work-Exception.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CAL-1.0-Combined-Work-Exception.json", - "referenceNumber": 217, + "referenceNumber": 333, "name": "Cryptographic Autonomy License 1.0 (Combined Work Exception)", "licenseId": "CAL-1.0-Combined-Work-Exception", "seeAlso": [ @@ -1327,7 +1327,7 @@ "reference": "https://spdx.org/licenses/Caldera.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Caldera.json", - "referenceNumber": 622, + "referenceNumber": 528, "name": "Caldera License", "licenseId": "Caldera", "seeAlso": [ @@ -1339,7 +1339,7 @@ "reference": "https://spdx.org/licenses/Caldera-no-preamble.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Caldera-no-preamble.json", - "referenceNumber": 585, + "referenceNumber": 233, "name": "Caldera License (without preamble)", "licenseId": "Caldera-no-preamble", "seeAlso": [ @@ -1351,7 +1351,7 @@ "reference": "https://spdx.org/licenses/Catharon.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Catharon.json", - "referenceNumber": 45, + "referenceNumber": 337, "name": "Catharon License", "licenseId": "Catharon", "seeAlso": [ @@ -1363,7 +1363,7 @@ "reference": "https://spdx.org/licenses/CATOSL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CATOSL-1.1.json", - "referenceNumber": 193, + "referenceNumber": 134, "name": "Computer Associates Trusted Open Source License 1.1", "licenseId": "CATOSL-1.1", "seeAlso": [ @@ -1375,7 +1375,7 @@ "reference": "https://spdx.org/licenses/CC-BY-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-1.0.json", - "referenceNumber": 37, + "referenceNumber": 415, "name": "Creative Commons Attribution 1.0 Generic", "licenseId": "CC-BY-1.0", "seeAlso": [ @@ -1387,7 +1387,7 @@ "reference": "https://spdx.org/licenses/CC-BY-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-2.0.json", - "referenceNumber": 241, + "referenceNumber": 428, "name": "Creative Commons Attribution 2.0 Generic", "licenseId": "CC-BY-2.0", "seeAlso": [ @@ -1399,7 +1399,7 @@ "reference": "https://spdx.org/licenses/CC-BY-2.5.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-2.5.json", - "referenceNumber": 129, + "referenceNumber": 573, "name": "Creative Commons Attribution 2.5 Generic", "licenseId": "CC-BY-2.5", "seeAlso": [ @@ -1411,7 +1411,7 @@ "reference": "https://spdx.org/licenses/CC-BY-2.5-AU.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-2.5-AU.json", - "referenceNumber": 583, + "referenceNumber": 388, "name": "Creative Commons Attribution 2.5 Australia", "licenseId": "CC-BY-2.5-AU", "seeAlso": [ @@ -1423,7 +1423,7 @@ "reference": "https://spdx.org/licenses/CC-BY-3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0.json", - "referenceNumber": 302, + "referenceNumber": 132, "name": "Creative Commons Attribution 3.0 Unported", "licenseId": "CC-BY-3.0", "seeAlso": [ @@ -1435,7 +1435,7 @@ "reference": "https://spdx.org/licenses/CC-BY-3.0-AT.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0-AT.json", - "referenceNumber": 324, + "referenceNumber": 25, "name": "Creative Commons Attribution 3.0 Austria", "licenseId": "CC-BY-3.0-AT", "seeAlso": [ @@ -1447,7 +1447,7 @@ "reference": "https://spdx.org/licenses/CC-BY-3.0-AU.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0-AU.json", - "referenceNumber": 342, + "referenceNumber": 392, "name": "Creative Commons Attribution 3.0 Australia", "licenseId": "CC-BY-3.0-AU", "seeAlso": [ @@ -1459,7 +1459,7 @@ "reference": "https://spdx.org/licenses/CC-BY-3.0-DE.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0-DE.json", - "referenceNumber": 239, + "referenceNumber": 21, "name": "Creative Commons Attribution 3.0 Germany", "licenseId": "CC-BY-3.0-DE", "seeAlso": [ @@ -1471,7 +1471,7 @@ "reference": "https://spdx.org/licenses/CC-BY-3.0-IGO.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0-IGO.json", - "referenceNumber": 19, + "referenceNumber": 596, "name": "Creative Commons Attribution 3.0 IGO", "licenseId": "CC-BY-3.0-IGO", "seeAlso": [ @@ -1483,7 +1483,7 @@ "reference": "https://spdx.org/licenses/CC-BY-3.0-NL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0-NL.json", - "referenceNumber": 501, + "referenceNumber": 157, "name": "Creative Commons Attribution 3.0 Netherlands", "licenseId": "CC-BY-3.0-NL", "seeAlso": [ @@ -1495,7 +1495,7 @@ "reference": "https://spdx.org/licenses/CC-BY-3.0-US.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-3.0-US.json", - "referenceNumber": 569, + "referenceNumber": 395, "name": "Creative Commons Attribution 3.0 United States", "licenseId": "CC-BY-3.0-US", "seeAlso": [ @@ -1507,7 +1507,7 @@ "reference": "https://spdx.org/licenses/CC-BY-4.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-4.0.json", - "referenceNumber": 265, + "referenceNumber": 435, "name": "Creative Commons Attribution 4.0 International", "licenseId": "CC-BY-4.0", "seeAlso": [ @@ -1520,7 +1520,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-1.0.json", - "referenceNumber": 167, + "referenceNumber": 641, "name": "Creative Commons Attribution Non Commercial 1.0 Generic", "licenseId": "CC-BY-NC-1.0", "seeAlso": [ @@ -1533,7 +1533,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-2.0.json", - "referenceNumber": 92, + "referenceNumber": 91, "name": "Creative Commons Attribution Non Commercial 2.0 Generic", "licenseId": "CC-BY-NC-2.0", "seeAlso": [ @@ -1546,7 +1546,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-2.5.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-2.5.json", - "referenceNumber": 253, + "referenceNumber": 465, "name": "Creative Commons Attribution Non Commercial 2.5 Generic", "licenseId": "CC-BY-NC-2.5", "seeAlso": [ @@ -1559,7 +1559,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-3.0.json", - "referenceNumber": 199, + "referenceNumber": 234, "name": "Creative Commons Attribution Non Commercial 3.0 Unported", "licenseId": "CC-BY-NC-3.0", "seeAlso": [ @@ -1572,7 +1572,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-3.0-DE.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-3.0-DE.json", - "referenceNumber": 429, + "referenceNumber": 354, "name": "Creative Commons Attribution Non Commercial 3.0 Germany", "licenseId": "CC-BY-NC-3.0-DE", "seeAlso": [ @@ -1584,7 +1584,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-4.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-4.0.json", - "referenceNumber": 188, + "referenceNumber": 53, "name": "Creative Commons Attribution Non Commercial 4.0 International", "licenseId": "CC-BY-NC-4.0", "seeAlso": [ @@ -1597,7 +1597,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-ND-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-1.0.json", - "referenceNumber": 365, + "referenceNumber": 88, "name": "Creative Commons Attribution Non Commercial No Derivatives 1.0 Generic", "licenseId": "CC-BY-NC-ND-1.0", "seeAlso": [ @@ -1609,7 +1609,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-ND-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-2.0.json", - "referenceNumber": 416, + "referenceNumber": 426, "name": "Creative Commons Attribution Non Commercial No Derivatives 2.0 Generic", "licenseId": "CC-BY-NC-ND-2.0", "seeAlso": [ @@ -1621,7 +1621,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-ND-2.5.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-2.5.json", - "referenceNumber": 58, + "referenceNumber": 441, "name": "Creative Commons Attribution Non Commercial No Derivatives 2.5 Generic", "licenseId": "CC-BY-NC-ND-2.5", "seeAlso": [ @@ -1633,7 +1633,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-ND-3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-3.0.json", - "referenceNumber": 213, + "referenceNumber": 304, "name": "Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported", "licenseId": "CC-BY-NC-ND-3.0", "seeAlso": [ @@ -1645,7 +1645,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-ND-3.0-DE.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-3.0-DE.json", - "referenceNumber": 84, + "referenceNumber": 121, "name": "Creative Commons Attribution Non Commercial No Derivatives 3.0 Germany", "licenseId": "CC-BY-NC-ND-3.0-DE", "seeAlso": [ @@ -1657,7 +1657,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-ND-3.0-IGO.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-3.0-IGO.json", - "referenceNumber": 587, + "referenceNumber": 171, "name": "Creative Commons Attribution Non Commercial No Derivatives 3.0 IGO", "licenseId": "CC-BY-NC-ND-3.0-IGO", "seeAlso": [ @@ -1669,7 +1669,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-ND-4.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-ND-4.0.json", - "referenceNumber": 296, + "referenceNumber": 183, "name": "Creative Commons Attribution Non Commercial No Derivatives 4.0 International", "licenseId": "CC-BY-NC-ND-4.0", "seeAlso": [ @@ -1681,7 +1681,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-SA-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-1.0.json", - "referenceNumber": 170, + "referenceNumber": 501, "name": "Creative Commons Attribution Non Commercial Share Alike 1.0 Generic", "licenseId": "CC-BY-NC-SA-1.0", "seeAlso": [ @@ -1693,7 +1693,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-SA-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-2.0.json", - "referenceNumber": 484, + "referenceNumber": 358, "name": "Creative Commons Attribution Non Commercial Share Alike 2.0 Generic", "licenseId": "CC-BY-NC-SA-2.0", "seeAlso": [ @@ -1705,7 +1705,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-SA-2.0-DE.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-2.0-DE.json", - "referenceNumber": 184, + "referenceNumber": 260, "name": "Creative Commons Attribution Non Commercial Share Alike 2.0 Germany", "licenseId": "CC-BY-NC-SA-2.0-DE", "seeAlso": [ @@ -1717,7 +1717,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-SA-2.0-FR.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-2.0-FR.json", - "referenceNumber": 116, + "referenceNumber": 158, "name": "Creative Commons Attribution-NonCommercial-ShareAlike 2.0 France", "licenseId": "CC-BY-NC-SA-2.0-FR", "seeAlso": [ @@ -1729,7 +1729,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-SA-2.0-UK.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-2.0-UK.json", - "referenceNumber": 415, + "referenceNumber": 33, "name": "Creative Commons Attribution Non Commercial Share Alike 2.0 England and Wales", "licenseId": "CC-BY-NC-SA-2.0-UK", "seeAlso": [ @@ -1741,7 +1741,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-SA-2.5.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-2.5.json", - "referenceNumber": 106, + "referenceNumber": 222, "name": "Creative Commons Attribution Non Commercial Share Alike 2.5 Generic", "licenseId": "CC-BY-NC-SA-2.5", "seeAlso": [ @@ -1753,7 +1753,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-SA-3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-3.0.json", - "referenceNumber": 323, + "referenceNumber": 255, "name": "Creative Commons Attribution Non Commercial Share Alike 3.0 Unported", "licenseId": "CC-BY-NC-SA-3.0", "seeAlso": [ @@ -1765,7 +1765,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-SA-3.0-DE.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-3.0-DE.json", - "referenceNumber": 150, + "referenceNumber": 525, "name": "Creative Commons Attribution Non Commercial Share Alike 3.0 Germany", "licenseId": "CC-BY-NC-SA-3.0-DE", "seeAlso": [ @@ -1777,7 +1777,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-SA-3.0-IGO.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-3.0-IGO.json", - "referenceNumber": 295, + "referenceNumber": 244, "name": "Creative Commons Attribution Non Commercial Share Alike 3.0 IGO", "licenseId": "CC-BY-NC-SA-3.0-IGO", "seeAlso": [ @@ -1789,7 +1789,7 @@ "reference": "https://spdx.org/licenses/CC-BY-NC-SA-4.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-NC-SA-4.0.json", - "referenceNumber": 351, + "referenceNumber": 513, "name": "Creative Commons Attribution Non Commercial Share Alike 4.0 International", "licenseId": "CC-BY-NC-SA-4.0", "seeAlso": [ @@ -1801,7 +1801,7 @@ "reference": "https://spdx.org/licenses/CC-BY-ND-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-ND-1.0.json", - "referenceNumber": 56, + "referenceNumber": 474, "name": "Creative Commons Attribution No Derivatives 1.0 Generic", "licenseId": "CC-BY-ND-1.0", "seeAlso": [ @@ -1814,7 +1814,7 @@ "reference": "https://spdx.org/licenses/CC-BY-ND-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-ND-2.0.json", - "referenceNumber": 640, + "referenceNumber": 356, "name": "Creative Commons Attribution No Derivatives 2.0 Generic", "licenseId": "CC-BY-ND-2.0", "seeAlso": [ @@ -1827,7 +1827,7 @@ "reference": "https://spdx.org/licenses/CC-BY-ND-2.5.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-ND-2.5.json", - "referenceNumber": 276, + "referenceNumber": 259, "name": "Creative Commons Attribution No Derivatives 2.5 Generic", "licenseId": "CC-BY-ND-2.5", "seeAlso": [ @@ -1840,7 +1840,7 @@ "reference": "https://spdx.org/licenses/CC-BY-ND-3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-ND-3.0.json", - "referenceNumber": 173, + "referenceNumber": 527, "name": "Creative Commons Attribution No Derivatives 3.0 Unported", "licenseId": "CC-BY-ND-3.0", "seeAlso": [ @@ -1853,7 +1853,7 @@ "reference": "https://spdx.org/licenses/CC-BY-ND-3.0-DE.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-ND-3.0-DE.json", - "referenceNumber": 525, + "referenceNumber": 214, "name": "Creative Commons Attribution No Derivatives 3.0 Germany", "licenseId": "CC-BY-ND-3.0-DE", "seeAlso": [ @@ -1865,7 +1865,7 @@ "reference": "https://spdx.org/licenses/CC-BY-ND-4.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-ND-4.0.json", - "referenceNumber": 328, + "referenceNumber": 481, "name": "Creative Commons Attribution No Derivatives 4.0 International", "licenseId": "CC-BY-ND-4.0", "seeAlso": [ @@ -1878,7 +1878,7 @@ "reference": "https://spdx.org/licenses/CC-BY-SA-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-1.0.json", - "referenceNumber": 453, + "referenceNumber": 588, "name": "Creative Commons Attribution Share Alike 1.0 Generic", "licenseId": "CC-BY-SA-1.0", "seeAlso": [ @@ -1890,7 +1890,7 @@ "reference": "https://spdx.org/licenses/CC-BY-SA-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-2.0.json", - "referenceNumber": 174, + "referenceNumber": 180, "name": "Creative Commons Attribution Share Alike 2.0 Generic", "licenseId": "CC-BY-SA-2.0", "seeAlso": [ @@ -1902,7 +1902,7 @@ "reference": "https://spdx.org/licenses/CC-BY-SA-2.0-UK.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-2.0-UK.json", - "referenceNumber": 387, + "referenceNumber": 385, "name": "Creative Commons Attribution Share Alike 2.0 England and Wales", "licenseId": "CC-BY-SA-2.0-UK", "seeAlso": [ @@ -1914,7 +1914,7 @@ "reference": "https://spdx.org/licenses/CC-BY-SA-2.1-JP.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-2.1-JP.json", - "referenceNumber": 471, + "referenceNumber": 17, "name": "Creative Commons Attribution Share Alike 2.1 Japan", "licenseId": "CC-BY-SA-2.1-JP", "seeAlso": [ @@ -1926,7 +1926,7 @@ "reference": "https://spdx.org/licenses/CC-BY-SA-2.5.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-2.5.json", - "referenceNumber": 461, + "referenceNumber": 607, "name": "Creative Commons Attribution Share Alike 2.5 Generic", "licenseId": "CC-BY-SA-2.5", "seeAlso": [ @@ -1938,7 +1938,7 @@ "reference": "https://spdx.org/licenses/CC-BY-SA-3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-3.0.json", - "referenceNumber": 621, + "referenceNumber": 26, "name": "Creative Commons Attribution Share Alike 3.0 Unported", "licenseId": "CC-BY-SA-3.0", "seeAlso": [ @@ -1950,7 +1950,7 @@ "reference": "https://spdx.org/licenses/CC-BY-SA-3.0-AT.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-3.0-AT.json", - "referenceNumber": 31, + "referenceNumber": 398, "name": "Creative Commons Attribution Share Alike 3.0 Austria", "licenseId": "CC-BY-SA-3.0-AT", "seeAlso": [ @@ -1962,7 +1962,7 @@ "reference": "https://spdx.org/licenses/CC-BY-SA-3.0-DE.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-3.0-DE.json", - "referenceNumber": 325, + "referenceNumber": 120, "name": "Creative Commons Attribution Share Alike 3.0 Germany", "licenseId": "CC-BY-SA-3.0-DE", "seeAlso": [ @@ -1974,7 +1974,7 @@ "reference": "https://spdx.org/licenses/CC-BY-SA-3.0-IGO.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-3.0-IGO.json", - "referenceNumber": 20, + "referenceNumber": 519, "name": "Creative Commons Attribution-ShareAlike 3.0 IGO", "licenseId": "CC-BY-SA-3.0-IGO", "seeAlso": [ @@ -1986,7 +1986,7 @@ "reference": "https://spdx.org/licenses/CC-BY-SA-4.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-BY-SA-4.0.json", - "referenceNumber": 155, + "referenceNumber": 13, "name": "Creative Commons Attribution Share Alike 4.0 International", "licenseId": "CC-BY-SA-4.0", "seeAlso": [ @@ -1999,7 +1999,7 @@ "reference": "https://spdx.org/licenses/CC-PDDC.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC-PDDC.json", - "referenceNumber": 349, + "referenceNumber": 169, "name": "Creative Commons Public Domain Dedication and Certification", "licenseId": "CC-PDDC", "seeAlso": [ @@ -2011,7 +2011,7 @@ "reference": "https://spdx.org/licenses/CC0-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CC0-1.0.json", - "referenceNumber": 70, + "referenceNumber": 491, "name": "Creative Commons Zero v1.0 Universal", "licenseId": "CC0-1.0", "seeAlso": [ @@ -2024,7 +2024,7 @@ "reference": "https://spdx.org/licenses/CDDL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CDDL-1.0.json", - "referenceNumber": 282, + "referenceNumber": 185, "name": "Common Development and Distribution License 1.0", "licenseId": "CDDL-1.0", "seeAlso": [ @@ -2037,7 +2037,7 @@ "reference": "https://spdx.org/licenses/CDDL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CDDL-1.1.json", - "referenceNumber": 130, + "referenceNumber": 476, "name": "Common Development and Distribution License 1.1", "licenseId": "CDDL-1.1", "seeAlso": [ @@ -2050,7 +2050,7 @@ "reference": "https://spdx.org/licenses/CDL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CDL-1.0.json", - "referenceNumber": 592, + "referenceNumber": 305, "name": "Common Documentation License 1.0", "licenseId": "CDL-1.0", "seeAlso": [ @@ -2064,7 +2064,7 @@ "reference": "https://spdx.org/licenses/CDLA-Permissive-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CDLA-Permissive-1.0.json", - "referenceNumber": 551, + "referenceNumber": 386, "name": "Community Data License Agreement Permissive 1.0", "licenseId": "CDLA-Permissive-1.0", "seeAlso": [ @@ -2076,7 +2076,7 @@ "reference": "https://spdx.org/licenses/CDLA-Permissive-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CDLA-Permissive-2.0.json", - "referenceNumber": 319, + "referenceNumber": 590, "name": "Community Data License Agreement Permissive 2.0", "licenseId": "CDLA-Permissive-2.0", "seeAlso": [ @@ -2088,7 +2088,7 @@ "reference": "https://spdx.org/licenses/CDLA-Sharing-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CDLA-Sharing-1.0.json", - "referenceNumber": 445, + "referenceNumber": 190, "name": "Community Data License Agreement Sharing 1.0", "licenseId": "CDLA-Sharing-1.0", "seeAlso": [ @@ -2100,7 +2100,7 @@ "reference": "https://spdx.org/licenses/CECILL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CECILL-1.0.json", - "referenceNumber": 219, + "referenceNumber": 625, "name": "CeCILL Free Software License Agreement v1.0", "licenseId": "CECILL-1.0", "seeAlso": [ @@ -2112,7 +2112,7 @@ "reference": "https://spdx.org/licenses/CECILL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CECILL-1.1.json", - "referenceNumber": 38, + "referenceNumber": 326, "name": "CeCILL Free Software License Agreement v1.1", "licenseId": "CECILL-1.1", "seeAlso": [ @@ -2124,7 +2124,7 @@ "reference": "https://spdx.org/licenses/CECILL-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CECILL-2.0.json", - "referenceNumber": 73, + "referenceNumber": 463, "name": "CeCILL Free Software License Agreement v2.0", "licenseId": "CECILL-2.0", "seeAlso": [ @@ -2137,7 +2137,7 @@ "reference": "https://spdx.org/licenses/CECILL-2.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CECILL-2.1.json", - "referenceNumber": 393, + "referenceNumber": 170, "name": "CeCILL Free Software License Agreement v2.1", "licenseId": "CECILL-2.1", "seeAlso": [ @@ -2149,7 +2149,7 @@ "reference": "https://spdx.org/licenses/CECILL-B.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CECILL-B.json", - "referenceNumber": 354, + "referenceNumber": 196, "name": "CeCILL-B Free Software License Agreement", "licenseId": "CECILL-B", "seeAlso": [ @@ -2162,7 +2162,7 @@ "reference": "https://spdx.org/licenses/CECILL-C.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CECILL-C.json", - "referenceNumber": 271, + "referenceNumber": 178, "name": "CeCILL-C Free Software License Agreement", "licenseId": "CECILL-C", "seeAlso": [ @@ -2175,7 +2175,7 @@ "reference": "https://spdx.org/licenses/CERN-OHL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CERN-OHL-1.1.json", - "referenceNumber": 32, + "referenceNumber": 148, "name": "CERN Open Hardware Licence v1.1", "licenseId": "CERN-OHL-1.1", "seeAlso": [ @@ -2187,7 +2187,7 @@ "reference": "https://spdx.org/licenses/CERN-OHL-1.2.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CERN-OHL-1.2.json", - "referenceNumber": 95, + "referenceNumber": 651, "name": "CERN Open Hardware Licence v1.2", "licenseId": "CERN-OHL-1.2", "seeAlso": [ @@ -2199,7 +2199,7 @@ "reference": "https://spdx.org/licenses/CERN-OHL-P-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CERN-OHL-P-2.0.json", - "referenceNumber": 198, + "referenceNumber": 543, "name": "CERN Open Hardware Licence Version 2 - Permissive", "licenseId": "CERN-OHL-P-2.0", "seeAlso": [ @@ -2211,7 +2211,7 @@ "reference": "https://spdx.org/licenses/CERN-OHL-S-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CERN-OHL-S-2.0.json", - "referenceNumber": 370, + "referenceNumber": 396, "name": "CERN Open Hardware Licence Version 2 - Strongly Reciprocal", "licenseId": "CERN-OHL-S-2.0", "seeAlso": [ @@ -2223,7 +2223,7 @@ "reference": "https://spdx.org/licenses/CERN-OHL-W-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CERN-OHL-W-2.0.json", - "referenceNumber": 82, + "referenceNumber": 614, "name": "CERN Open Hardware Licence Version 2 - Weakly Reciprocal", "licenseId": "CERN-OHL-W-2.0", "seeAlso": [ @@ -2235,7 +2235,7 @@ "reference": "https://spdx.org/licenses/CFITSIO.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CFITSIO.json", - "referenceNumber": 96, + "referenceNumber": 568, "name": "CFITSIO License", "licenseId": "CFITSIO", "seeAlso": [ @@ -2248,7 +2248,7 @@ "reference": "https://spdx.org/licenses/check-cvs.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/check-cvs.json", - "referenceNumber": 521, + "referenceNumber": 324, "name": "check-cvs License", "licenseId": "check-cvs", "seeAlso": [ @@ -2260,7 +2260,7 @@ "reference": "https://spdx.org/licenses/checkmk.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/checkmk.json", - "referenceNumber": 272, + "referenceNumber": 464, "name": "Checkmk License", "licenseId": "checkmk", "seeAlso": [ @@ -2272,7 +2272,7 @@ "reference": "https://spdx.org/licenses/ClArtistic.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ClArtistic.json", - "referenceNumber": 66, + "referenceNumber": 230, "name": "Clarified Artistic License", "licenseId": "ClArtistic", "seeAlso": [ @@ -2286,7 +2286,7 @@ "reference": "https://spdx.org/licenses/Clips.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Clips.json", - "referenceNumber": 450, + "referenceNumber": 424, "name": "Clips License", "licenseId": "Clips", "seeAlso": [ @@ -2298,7 +2298,7 @@ "reference": "https://spdx.org/licenses/CMU-Mach.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CMU-Mach.json", - "referenceNumber": 410, + "referenceNumber": 73, "name": "CMU Mach License", "licenseId": "CMU-Mach", "seeAlso": [ @@ -2310,7 +2310,7 @@ "reference": "https://spdx.org/licenses/CMU-Mach-nodoc.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CMU-Mach-nodoc.json", - "referenceNumber": 111, + "referenceNumber": 8, "name": "CMU Mach - no notices-in-documentation variant", "licenseId": "CMU-Mach-nodoc", "seeAlso": [ @@ -2323,7 +2323,7 @@ "reference": "https://spdx.org/licenses/CNRI-Jython.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CNRI-Jython.json", - "referenceNumber": 509, + "referenceNumber": 293, "name": "CNRI Jython License", "licenseId": "CNRI-Jython", "seeAlso": [ @@ -2335,7 +2335,7 @@ "reference": "https://spdx.org/licenses/CNRI-Python.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CNRI-Python.json", - "referenceNumber": 611, + "referenceNumber": 402, "name": "CNRI Python License", "licenseId": "CNRI-Python", "seeAlso": [ @@ -2347,7 +2347,7 @@ "reference": "https://spdx.org/licenses/CNRI-Python-GPL-Compatible.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CNRI-Python-GPL-Compatible.json", - "referenceNumber": 504, + "referenceNumber": 224, "name": "CNRI Python Open Source GPL Compatible License Agreement", "licenseId": "CNRI-Python-GPL-Compatible", "seeAlso": [ @@ -2359,7 +2359,7 @@ "reference": "https://spdx.org/licenses/COIL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/COIL-1.0.json", - "referenceNumber": 286, + "referenceNumber": 345, "name": "Copyfree Open Innovation License", "licenseId": "COIL-1.0", "seeAlso": [ @@ -2371,7 +2371,7 @@ "reference": "https://spdx.org/licenses/Community-Spec-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Community-Spec-1.0.json", - "referenceNumber": 631, + "referenceNumber": 56, "name": "Community Specification License 1.0", "licenseId": "Community-Spec-1.0", "seeAlso": [ @@ -2383,7 +2383,7 @@ "reference": "https://spdx.org/licenses/Condor-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Condor-1.1.json", - "referenceNumber": 251, + "referenceNumber": 77, "name": "Condor Public License v1.1", "licenseId": "Condor-1.1", "seeAlso": [ @@ -2397,7 +2397,7 @@ "reference": "https://spdx.org/licenses/copyleft-next-0.3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/copyleft-next-0.3.0.json", - "referenceNumber": 421, + "referenceNumber": 322, "name": "copyleft-next 0.3.0", "licenseId": "copyleft-next-0.3.0", "seeAlso": [ @@ -2409,7 +2409,7 @@ "reference": "https://spdx.org/licenses/copyleft-next-0.3.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/copyleft-next-0.3.1.json", - "referenceNumber": 119, + "referenceNumber": 403, "name": "copyleft-next 0.3.1", "licenseId": "copyleft-next-0.3.1", "seeAlso": [ @@ -2421,7 +2421,7 @@ "reference": "https://spdx.org/licenses/Cornell-Lossless-JPEG.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Cornell-Lossless-JPEG.json", - "referenceNumber": 632, + "referenceNumber": 98, "name": "Cornell Lossless JPEG License", "licenseId": "Cornell-Lossless-JPEG", "seeAlso": [ @@ -2435,7 +2435,7 @@ "reference": "https://spdx.org/licenses/CPAL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CPAL-1.0.json", - "referenceNumber": 315, + "referenceNumber": 548, "name": "Common Public Attribution License 1.0", "licenseId": "CPAL-1.0", "seeAlso": [ @@ -2448,7 +2448,7 @@ "reference": "https://spdx.org/licenses/CPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CPL-1.0.json", - "referenceNumber": 135, + "referenceNumber": 114, "name": "Common Public License 1.0", "licenseId": "CPL-1.0", "seeAlso": [ @@ -2461,7 +2461,7 @@ "reference": "https://spdx.org/licenses/CPOL-1.02.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CPOL-1.02.json", - "referenceNumber": 479, + "referenceNumber": 6, "name": "Code Project Open License 1.02", "licenseId": "CPOL-1.02", "seeAlso": [ @@ -2474,7 +2474,7 @@ "reference": "https://spdx.org/licenses/Cronyx.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Cronyx.json", - "referenceNumber": 377, + "referenceNumber": 649, "name": "Cronyx License", "licenseId": "Cronyx", "seeAlso": [ @@ -2489,7 +2489,7 @@ "reference": "https://spdx.org/licenses/Crossword.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Crossword.json", - "referenceNumber": 340, + "referenceNumber": 593, "name": "Crossword License", "licenseId": "Crossword", "seeAlso": [ @@ -2501,7 +2501,7 @@ "reference": "https://spdx.org/licenses/CrystalStacker.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CrystalStacker.json", - "referenceNumber": 593, + "referenceNumber": 241, "name": "CrystalStacker License", "licenseId": "CrystalStacker", "seeAlso": [ @@ -2513,7 +2513,7 @@ "reference": "https://spdx.org/licenses/CUA-OPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/CUA-OPL-1.0.json", - "referenceNumber": 553, + "referenceNumber": 409, "name": "CUA Office Public License v1.0", "licenseId": "CUA-OPL-1.0", "seeAlso": [ @@ -2525,7 +2525,7 @@ "reference": "https://spdx.org/licenses/Cube.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Cube.json", - "referenceNumber": 404, + "referenceNumber": 141, "name": "Cube License", "licenseId": "Cube", "seeAlso": [ @@ -2537,7 +2537,7 @@ "reference": "https://spdx.org/licenses/curl.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/curl.json", - "referenceNumber": 604, + "referenceNumber": 602, "name": "curl License", "licenseId": "curl", "seeAlso": [ @@ -2549,7 +2549,7 @@ "reference": "https://spdx.org/licenses/cve-tou.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/cve-tou.json", - "referenceNumber": 306, + "referenceNumber": 656, "name": "Common Vulnerability Enumeration ToU License", "licenseId": "cve-tou", "seeAlso": [ @@ -2561,7 +2561,7 @@ "reference": "https://spdx.org/licenses/D-FSL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/D-FSL-1.0.json", - "referenceNumber": 154, + "referenceNumber": 116, "name": "Deutsche Freie Software Lizenz", "licenseId": "D-FSL-1.0", "seeAlso": [ @@ -2580,7 +2580,7 @@ "reference": "https://spdx.org/licenses/DEC-3-Clause.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/DEC-3-Clause.json", - "referenceNumber": 15, + "referenceNumber": 512, "name": "DEC 3-Clause License", "licenseId": "DEC-3-Clause", "seeAlso": [ @@ -2592,7 +2592,7 @@ "reference": "https://spdx.org/licenses/diffmark.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/diffmark.json", - "referenceNumber": 292, + "referenceNumber": 480, "name": "diffmark license", "licenseId": "diffmark", "seeAlso": [ @@ -2604,7 +2604,7 @@ "reference": "https://spdx.org/licenses/DL-DE-BY-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/DL-DE-BY-2.0.json", - "referenceNumber": 225, + "referenceNumber": 84, "name": "Data licence Germany – attribution – version 2.0", "licenseId": "DL-DE-BY-2.0", "seeAlso": [ @@ -2616,7 +2616,7 @@ "reference": "https://spdx.org/licenses/DL-DE-ZERO-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/DL-DE-ZERO-2.0.json", - "referenceNumber": 341, + "referenceNumber": 522, "name": "Data licence Germany – zero – version 2.0", "licenseId": "DL-DE-ZERO-2.0", "seeAlso": [ @@ -2628,7 +2628,7 @@ "reference": "https://spdx.org/licenses/DOC.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/DOC.json", - "referenceNumber": 397, + "referenceNumber": 646, "name": "DOC License", "licenseId": "DOC", "seeAlso": [ @@ -2637,11 +2637,35 @@ ], "isOsiApproved": false }, + { + "reference": "https://spdx.org/licenses/DocBook-Schema.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/DocBook-Schema.json", + "referenceNumber": 153, + "name": "DocBook Schema License", + "licenseId": "DocBook-Schema", + "seeAlso": [ + "https://github.com/docbook/xslt10-stylesheets/blob/efd62655c11cc8773708df7a843613fa1e932bf8/xsl/assembly/schema/docbook51b7.rnc" + ], + "isOsiApproved": false + }, + { + "reference": "https://spdx.org/licenses/DocBook-XML.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/DocBook-XML.json", + "referenceNumber": 493, + "name": "DocBook XML License", + "licenseId": "DocBook-XML", + "seeAlso": [ + "https://github.com/docbook/xslt10-stylesheets/blob/efd62655c11cc8773708df7a843613fa1e932bf8/xsl/COPYING#L27" + ], + "isOsiApproved": false + }, { "reference": "https://spdx.org/licenses/Dotseqn.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Dotseqn.json", - "referenceNumber": 132, + "referenceNumber": 533, "name": "Dotseqn License", "licenseId": "Dotseqn", "seeAlso": [ @@ -2653,7 +2677,7 @@ "reference": "https://spdx.org/licenses/DRL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/DRL-1.0.json", - "referenceNumber": 16, + "referenceNumber": 410, "name": "Detection Rule License 1.0", "licenseId": "DRL-1.0", "seeAlso": [ @@ -2665,7 +2689,7 @@ "reference": "https://spdx.org/licenses/DRL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/DRL-1.1.json", - "referenceNumber": 278, + "referenceNumber": 268, "name": "Detection Rule License 1.1", "licenseId": "DRL-1.1", "seeAlso": [ @@ -2677,7 +2701,7 @@ "reference": "https://spdx.org/licenses/DSDP.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/DSDP.json", - "referenceNumber": 485, + "referenceNumber": 164, "name": "DSDP License", "licenseId": "DSDP", "seeAlso": [ @@ -2689,7 +2713,7 @@ "reference": "https://spdx.org/licenses/dtoa.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/dtoa.json", - "referenceNumber": 358, + "referenceNumber": 263, "name": "David M. Gay dtoa License", "licenseId": "dtoa", "seeAlso": [ @@ -2702,7 +2726,7 @@ "reference": "https://spdx.org/licenses/dvipdfm.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/dvipdfm.json", - "referenceNumber": 100, + "referenceNumber": 61, "name": "dvipdfm License", "licenseId": "dvipdfm", "seeAlso": [ @@ -2714,7 +2738,7 @@ "reference": "https://spdx.org/licenses/ECL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ECL-1.0.json", - "referenceNumber": 124, + "referenceNumber": 264, "name": "Educational Community License v1.0", "licenseId": "ECL-1.0", "seeAlso": [ @@ -2726,7 +2750,7 @@ "reference": "https://spdx.org/licenses/ECL-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ECL-2.0.json", - "referenceNumber": 361, + "referenceNumber": 363, "name": "Educational Community License v2.0", "licenseId": "ECL-2.0", "seeAlso": [ @@ -2739,7 +2763,7 @@ "reference": "https://spdx.org/licenses/eCos-2.0.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/eCos-2.0.json", - "referenceNumber": 372, + "referenceNumber": 298, "name": "eCos license version 2.0", "licenseId": "eCos-2.0", "seeAlso": [ @@ -2752,7 +2776,7 @@ "reference": "https://spdx.org/licenses/EFL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/EFL-1.0.json", - "referenceNumber": 335, + "referenceNumber": 137, "name": "Eiffel Forum License v1.0", "licenseId": "EFL-1.0", "seeAlso": [ @@ -2765,7 +2789,7 @@ "reference": "https://spdx.org/licenses/EFL-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/EFL-2.0.json", - "referenceNumber": 88, + "referenceNumber": 447, "name": "Eiffel Forum License v2.0", "licenseId": "EFL-2.0", "seeAlso": [ @@ -2779,7 +2803,7 @@ "reference": "https://spdx.org/licenses/eGenix.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/eGenix.json", - "referenceNumber": 261, + "referenceNumber": 348, "name": "eGenix.com Public License 1.1.0", "licenseId": "eGenix", "seeAlso": [ @@ -2792,7 +2816,7 @@ "reference": "https://spdx.org/licenses/Elastic-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Elastic-2.0.json", - "referenceNumber": 147, + "referenceNumber": 404, "name": "Elastic License 2.0", "licenseId": "Elastic-2.0", "seeAlso": [ @@ -2805,7 +2829,7 @@ "reference": "https://spdx.org/licenses/Entessa.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Entessa.json", - "referenceNumber": 546, + "referenceNumber": 198, "name": "Entessa Public License v1.0", "licenseId": "Entessa", "seeAlso": [ @@ -2817,7 +2841,7 @@ "reference": "https://spdx.org/licenses/EPICS.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/EPICS.json", - "referenceNumber": 120, + "referenceNumber": 532, "name": "EPICS Open License", "licenseId": "EPICS", "seeAlso": [ @@ -2829,7 +2853,7 @@ "reference": "https://spdx.org/licenses/EPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/EPL-1.0.json", - "referenceNumber": 500, + "referenceNumber": 115, "name": "Eclipse Public License 1.0", "licenseId": "EPL-1.0", "seeAlso": [ @@ -2843,7 +2867,7 @@ "reference": "https://spdx.org/licenses/EPL-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/EPL-2.0.json", - "referenceNumber": 407, + "referenceNumber": 282, "name": "Eclipse Public License 2.0", "licenseId": "EPL-2.0", "seeAlso": [ @@ -2857,7 +2881,7 @@ "reference": "https://spdx.org/licenses/ErlPL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ErlPL-1.1.json", - "referenceNumber": 466, + "referenceNumber": 530, "name": "Erlang Public License v1.1", "licenseId": "ErlPL-1.1", "seeAlso": [ @@ -2869,7 +2893,7 @@ "reference": "https://spdx.org/licenses/etalab-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/etalab-2.0.json", - "referenceNumber": 636, + "referenceNumber": 129, "name": "Etalab Open License 2.0", "licenseId": "etalab-2.0", "seeAlso": [ @@ -2882,7 +2906,7 @@ "reference": "https://spdx.org/licenses/EUDatagrid.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/EUDatagrid.json", - "referenceNumber": 228, + "referenceNumber": 393, "name": "EU DataGrid Software License", "licenseId": "EUDatagrid", "seeAlso": [ @@ -2896,7 +2920,7 @@ "reference": "https://spdx.org/licenses/EUPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/EUPL-1.0.json", - "referenceNumber": 227, + "referenceNumber": 389, "name": "European Union Public License 1.0", "licenseId": "EUPL-1.0", "seeAlso": [ @@ -2909,7 +2933,7 @@ "reference": "https://spdx.org/licenses/EUPL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/EUPL-1.1.json", - "referenceNumber": 266, + "referenceNumber": 503, "name": "European Union Public License 1.1", "licenseId": "EUPL-1.1", "seeAlso": [ @@ -2924,7 +2948,7 @@ "reference": "https://spdx.org/licenses/EUPL-1.2.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/EUPL-1.2.json", - "referenceNumber": 559, + "referenceNumber": 12, "name": "European Union Public License 1.2", "licenseId": "EUPL-1.2", "seeAlso": [ @@ -2942,7 +2966,7 @@ "reference": "https://spdx.org/licenses/Eurosym.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Eurosym.json", - "referenceNumber": 63, + "referenceNumber": 621, "name": "Eurosym License", "licenseId": "Eurosym", "seeAlso": [ @@ -2954,7 +2978,7 @@ "reference": "https://spdx.org/licenses/Fair.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Fair.json", - "referenceNumber": 570, + "referenceNumber": 258, "name": "Fair License", "licenseId": "Fair", "seeAlso": [ @@ -2967,7 +2991,7 @@ "reference": "https://spdx.org/licenses/FBM.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/FBM.json", - "referenceNumber": 175, + "referenceNumber": 626, "name": "Fuzzy Bitmap License", "licenseId": "FBM", "seeAlso": [ @@ -2979,7 +3003,7 @@ "reference": "https://spdx.org/licenses/FDK-AAC.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/FDK-AAC.json", - "referenceNumber": 49, + "referenceNumber": 344, "name": "Fraunhofer FDK AAC Codec Library", "licenseId": "FDK-AAC", "seeAlso": [ @@ -2992,7 +3016,7 @@ "reference": "https://spdx.org/licenses/Ferguson-Twofish.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Ferguson-Twofish.json", - "referenceNumber": 617, + "referenceNumber": 362, "name": "Ferguson Twofish License", "licenseId": "Ferguson-Twofish", "seeAlso": [ @@ -3004,7 +3028,7 @@ "reference": "https://spdx.org/licenses/Frameworx-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Frameworx-1.0.json", - "referenceNumber": 259, + "referenceNumber": 188, "name": "Frameworx Open License 1.0", "licenseId": "Frameworx-1.0", "seeAlso": [ @@ -3016,7 +3040,7 @@ "reference": "https://spdx.org/licenses/FreeBSD-DOC.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/FreeBSD-DOC.json", - "referenceNumber": 333, + "referenceNumber": 151, "name": "FreeBSD Documentation License", "licenseId": "FreeBSD-DOC", "seeAlso": [ @@ -3028,7 +3052,7 @@ "reference": "https://spdx.org/licenses/FreeImage.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/FreeImage.json", - "referenceNumber": 181, + "referenceNumber": 232, "name": "FreeImage Public License v1.0", "licenseId": "FreeImage", "seeAlso": [ @@ -3040,7 +3064,7 @@ "reference": "https://spdx.org/licenses/FSFAP.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/FSFAP.json", - "referenceNumber": 36, + "referenceNumber": 436, "name": "FSF All Permissive License", "licenseId": "FSFAP", "seeAlso": [ @@ -3053,7 +3077,7 @@ "reference": "https://spdx.org/licenses/FSFAP-no-warranty-disclaimer.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/FSFAP-no-warranty-disclaimer.json", - "referenceNumber": 536, + "referenceNumber": 547, "name": "FSF All Permissive License (without Warranty)", "licenseId": "FSFAP-no-warranty-disclaimer", "seeAlso": [ @@ -3065,7 +3089,7 @@ "reference": "https://spdx.org/licenses/FSFUL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/FSFUL.json", - "referenceNumber": 454, + "referenceNumber": 2, "name": "FSF Unlimited License", "licenseId": "FSFUL", "seeAlso": [ @@ -3077,7 +3101,7 @@ "reference": "https://spdx.org/licenses/FSFULLR.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/FSFULLR.json", - "referenceNumber": 422, + "referenceNumber": 508, "name": "FSF Unlimited License (with License Retention)", "licenseId": "FSFULLR", "seeAlso": [ @@ -3089,7 +3113,7 @@ "reference": "https://spdx.org/licenses/FSFULLRWD.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/FSFULLRWD.json", - "referenceNumber": 197, + "referenceNumber": 640, "name": "FSF Unlimited License (With License Retention and Warranty Disclaimer)", "licenseId": "FSFULLRWD", "seeAlso": [ @@ -3101,7 +3125,7 @@ "reference": "https://spdx.org/licenses/FTL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/FTL.json", - "referenceNumber": 438, + "referenceNumber": 249, "name": "Freetype Project License", "licenseId": "FTL", "seeAlso": [ @@ -3116,7 +3140,7 @@ "reference": "https://spdx.org/licenses/Furuseth.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Furuseth.json", - "referenceNumber": 380, + "referenceNumber": 273, "name": "Furuseth License", "licenseId": "Furuseth", "seeAlso": [ @@ -3128,7 +3152,7 @@ "reference": "https://spdx.org/licenses/fwlw.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/fwlw.json", - "referenceNumber": 529, + "referenceNumber": 50, "name": "fwlw License", "licenseId": "fwlw", "seeAlso": [ @@ -3140,7 +3164,7 @@ "reference": "https://spdx.org/licenses/GCR-docs.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GCR-docs.json", - "referenceNumber": 115, + "referenceNumber": 272, "name": "Gnome GCR Documentation License", "licenseId": "GCR-docs", "seeAlso": [ @@ -3152,7 +3176,7 @@ "reference": "https://spdx.org/licenses/GD.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GD.json", - "referenceNumber": 291, + "referenceNumber": 624, "name": "GD License", "licenseId": "GD", "seeAlso": [ @@ -3164,7 +3188,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.1.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GFDL-1.1.json", - "referenceNumber": 589, + "referenceNumber": 177, "name": "GNU Free Documentation License v1.1", "licenseId": "GFDL-1.1", "seeAlso": [ @@ -3177,7 +3201,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.1-invariants-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.1-invariants-only.json", - "referenceNumber": 307, + "referenceNumber": 216, "name": "GNU Free Documentation License v1.1 only - invariants", "licenseId": "GFDL-1.1-invariants-only", "seeAlso": [ @@ -3189,7 +3213,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.1-invariants-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.1-invariants-or-later.json", - "referenceNumber": 98, + "referenceNumber": 57, "name": "GNU Free Documentation License v1.1 or later - invariants", "licenseId": "GFDL-1.1-invariants-or-later", "seeAlso": [ @@ -3201,7 +3225,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.1-no-invariants-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.1-no-invariants-only.json", - "referenceNumber": 47, + "referenceNumber": 38, "name": "GNU Free Documentation License v1.1 only - no invariants", "licenseId": "GFDL-1.1-no-invariants-only", "seeAlso": [ @@ -3213,7 +3237,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.1-no-invariants-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.1-no-invariants-or-later.json", - "referenceNumber": 273, + "referenceNumber": 289, "name": "GNU Free Documentation License v1.1 or later - no invariants", "licenseId": "GFDL-1.1-no-invariants-or-later", "seeAlso": [ @@ -3225,7 +3249,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.1-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.1-only.json", - "referenceNumber": 626, + "referenceNumber": 654, "name": "GNU Free Documentation License v1.1 only", "licenseId": "GFDL-1.1-only", "seeAlso": [ @@ -3238,7 +3262,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.1-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.1-or-later.json", - "referenceNumber": 644, + "referenceNumber": 569, "name": "GNU Free Documentation License v1.1 or later", "licenseId": "GFDL-1.1-or-later", "seeAlso": [ @@ -3251,7 +3275,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.2.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GFDL-1.2.json", - "referenceNumber": 520, + "referenceNumber": 235, "name": "GNU Free Documentation License v1.2", "licenseId": "GFDL-1.2", "seeAlso": [ @@ -3264,7 +3288,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.2-invariants-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.2-invariants-only.json", - "referenceNumber": 495, + "referenceNumber": 461, "name": "GNU Free Documentation License v1.2 only - invariants", "licenseId": "GFDL-1.2-invariants-only", "seeAlso": [ @@ -3276,7 +3300,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.2-invariants-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.2-invariants-or-later.json", - "referenceNumber": 6, + "referenceNumber": 391, "name": "GNU Free Documentation License v1.2 or later - invariants", "licenseId": "GFDL-1.2-invariants-or-later", "seeAlso": [ @@ -3288,7 +3312,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.2-no-invariants-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.2-no-invariants-only.json", - "referenceNumber": 77, + "referenceNumber": 187, "name": "GNU Free Documentation License v1.2 only - no invariants", "licenseId": "GFDL-1.2-no-invariants-only", "seeAlso": [ @@ -3300,7 +3324,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.2-no-invariants-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.2-no-invariants-or-later.json", - "referenceNumber": 279, + "referenceNumber": 240, "name": "GNU Free Documentation License v1.2 or later - no invariants", "licenseId": "GFDL-1.2-no-invariants-or-later", "seeAlso": [ @@ -3312,7 +3336,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.2-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.2-only.json", - "referenceNumber": 648, + "referenceNumber": 302, "name": "GNU Free Documentation License v1.2 only", "licenseId": "GFDL-1.2-only", "seeAlso": [ @@ -3325,7 +3349,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.2-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.2-or-later.json", - "referenceNumber": 318, + "referenceNumber": 562, "name": "GNU Free Documentation License v1.2 or later", "licenseId": "GFDL-1.2-or-later", "seeAlso": [ @@ -3338,7 +3362,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.3.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GFDL-1.3.json", - "referenceNumber": 287, + "referenceNumber": 60, "name": "GNU Free Documentation License v1.3", "licenseId": "GFDL-1.3", "seeAlso": [ @@ -3351,7 +3375,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.3-invariants-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.3-invariants-only.json", - "referenceNumber": 289, + "referenceNumber": 184, "name": "GNU Free Documentation License v1.3 only - invariants", "licenseId": "GFDL-1.3-invariants-only", "seeAlso": [ @@ -3363,7 +3387,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.3-invariants-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.3-invariants-or-later.json", - "referenceNumber": 497, + "referenceNumber": 346, "name": "GNU Free Documentation License v1.3 or later - invariants", "licenseId": "GFDL-1.3-invariants-or-later", "seeAlso": [ @@ -3375,7 +3399,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.3-no-invariants-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.3-no-invariants-only.json", - "referenceNumber": 254, + "referenceNumber": 59, "name": "GNU Free Documentation License v1.3 only - no invariants", "licenseId": "GFDL-1.3-no-invariants-only", "seeAlso": [ @@ -3387,7 +3411,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.3-no-invariants-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.3-no-invariants-or-later.json", - "referenceNumber": 207, + "referenceNumber": 281, "name": "GNU Free Documentation License v1.3 or later - no invariants", "licenseId": "GFDL-1.3-no-invariants-or-later", "seeAlso": [ @@ -3399,7 +3423,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.3-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.3-only.json", - "referenceNumber": 635, + "referenceNumber": 642, "name": "GNU Free Documentation License v1.3 only", "licenseId": "GFDL-1.3-only", "seeAlso": [ @@ -3412,7 +3436,7 @@ "reference": "https://spdx.org/licenses/GFDL-1.3-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GFDL-1.3-or-later.json", - "referenceNumber": 448, + "referenceNumber": 494, "name": "GNU Free Documentation License v1.3 or later", "licenseId": "GFDL-1.3-or-later", "seeAlso": [ @@ -3425,7 +3449,7 @@ "reference": "https://spdx.org/licenses/Giftware.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Giftware.json", - "referenceNumber": 172, + "referenceNumber": 618, "name": "Giftware License", "licenseId": "Giftware", "seeAlso": [ @@ -3437,7 +3461,7 @@ "reference": "https://spdx.org/licenses/GL2PS.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GL2PS.json", - "referenceNumber": 434, + "referenceNumber": 231, "name": "GL2PS License", "licenseId": "GL2PS", "seeAlso": [ @@ -3449,7 +3473,7 @@ "reference": "https://spdx.org/licenses/Glide.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Glide.json", - "referenceNumber": 189, + "referenceNumber": 620, "name": "3dfx Glide License", "licenseId": "Glide", "seeAlso": [ @@ -3461,7 +3485,7 @@ "reference": "https://spdx.org/licenses/Glulxe.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Glulxe.json", - "referenceNumber": 85, + "referenceNumber": 429, "name": "Glulxe License", "licenseId": "Glulxe", "seeAlso": [ @@ -3473,7 +3497,7 @@ "reference": "https://spdx.org/licenses/GLWTPL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GLWTPL.json", - "referenceNumber": 190, + "referenceNumber": 154, "name": "Good Luck With That Public License", "licenseId": "GLWTPL", "seeAlso": [ @@ -3485,7 +3509,7 @@ "reference": "https://spdx.org/licenses/gnuplot.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/gnuplot.json", - "referenceNumber": 110, + "referenceNumber": 271, "name": "gnuplot License", "licenseId": "gnuplot", "seeAlso": [ @@ -3498,7 +3522,7 @@ "reference": "https://spdx.org/licenses/GPL-1.0.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GPL-1.0.json", - "referenceNumber": 630, + "referenceNumber": 365, "name": "GNU General Public License v1.0 only", "licenseId": "GPL-1.0", "seeAlso": [ @@ -3510,7 +3534,7 @@ "reference": "https://spdx.org/licenses/GPL-1.0+.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GPL-1.0+.json", - "referenceNumber": 26, + "referenceNumber": 66, "name": "GNU General Public License v1.0 or later", "licenseId": "GPL-1.0+", "seeAlso": [ @@ -3522,7 +3546,7 @@ "reference": "https://spdx.org/licenses/GPL-1.0-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GPL-1.0-only.json", - "referenceNumber": 12, + "referenceNumber": 563, "name": "GNU General Public License v1.0 only", "licenseId": "GPL-1.0-only", "seeAlso": [ @@ -3534,7 +3558,7 @@ "reference": "https://spdx.org/licenses/GPL-1.0-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GPL-1.0-or-later.json", - "referenceNumber": 642, + "referenceNumber": 558, "name": "GNU General Public License v1.0 or later", "licenseId": "GPL-1.0-or-later", "seeAlso": [ @@ -3546,7 +3570,7 @@ "reference": "https://spdx.org/licenses/GPL-2.0.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GPL-2.0.json", - "referenceNumber": 524, + "referenceNumber": 613, "name": "GNU General Public License v2.0 only", "licenseId": "GPL-2.0", "seeAlso": [ @@ -3560,7 +3584,7 @@ "reference": "https://spdx.org/licenses/GPL-2.0+.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GPL-2.0+.json", - "referenceNumber": 25, + "referenceNumber": 83, "name": "GNU General Public License v2.0 or later", "licenseId": "GPL-2.0+", "seeAlso": [ @@ -3574,7 +3598,7 @@ "reference": "https://spdx.org/licenses/GPL-2.0-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GPL-2.0-only.json", - "referenceNumber": 618, + "referenceNumber": 483, "name": "GNU General Public License v2.0 only", "licenseId": "GPL-2.0-only", "seeAlso": [ @@ -3589,7 +3613,7 @@ "reference": "https://spdx.org/licenses/GPL-2.0-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GPL-2.0-or-later.json", - "referenceNumber": 164, + "referenceNumber": 349, "name": "GNU General Public License v2.0 or later", "licenseId": "GPL-2.0-or-later", "seeAlso": [ @@ -3603,7 +3627,7 @@ "reference": "https://spdx.org/licenses/GPL-2.0-with-autoconf-exception.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GPL-2.0-with-autoconf-exception.json", - "referenceNumber": 146, + "referenceNumber": 475, "name": "GNU General Public License v2.0 w/Autoconf exception", "licenseId": "GPL-2.0-with-autoconf-exception", "seeAlso": [ @@ -3615,7 +3639,7 @@ "reference": "https://spdx.org/licenses/GPL-2.0-with-bison-exception.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GPL-2.0-with-bison-exception.json", - "referenceNumber": 374, + "referenceNumber": 492, "name": "GNU General Public License v2.0 w/Bison exception", "licenseId": "GPL-2.0-with-bison-exception", "seeAlso": [ @@ -3627,7 +3651,7 @@ "reference": "https://spdx.org/licenses/GPL-2.0-with-classpath-exception.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GPL-2.0-with-classpath-exception.json", - "referenceNumber": 331, + "referenceNumber": 144, "name": "GNU General Public License v2.0 w/Classpath exception", "licenseId": "GPL-2.0-with-classpath-exception", "seeAlso": [ @@ -3639,7 +3663,7 @@ "reference": "https://spdx.org/licenses/GPL-2.0-with-font-exception.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GPL-2.0-with-font-exception.json", - "referenceNumber": 542, + "referenceNumber": 579, "name": "GNU General Public License v2.0 w/Font exception", "licenseId": "GPL-2.0-with-font-exception", "seeAlso": [ @@ -3651,7 +3675,7 @@ "reference": "https://spdx.org/licenses/GPL-2.0-with-GCC-exception.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GPL-2.0-with-GCC-exception.json", - "referenceNumber": 68, + "referenceNumber": 449, "name": "GNU General Public License v2.0 w/GCC Runtime Library exception", "licenseId": "GPL-2.0-with-GCC-exception", "seeAlso": [ @@ -3663,7 +3687,7 @@ "reference": "https://spdx.org/licenses/GPL-3.0.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GPL-3.0.json", - "referenceNumber": 442, + "referenceNumber": 434, "name": "GNU General Public License v3.0 only", "licenseId": "GPL-3.0", "seeAlso": [ @@ -3677,7 +3701,7 @@ "reference": "https://spdx.org/licenses/GPL-3.0+.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GPL-3.0+.json", - "referenceNumber": 270, + "referenceNumber": 586, "name": "GNU General Public License v3.0 or later", "licenseId": "GPL-3.0+", "seeAlso": [ @@ -3691,7 +3715,7 @@ "reference": "https://spdx.org/licenses/GPL-3.0-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GPL-3.0-only.json", - "referenceNumber": 133, + "referenceNumber": 42, "name": "GNU General Public License v3.0 only", "licenseId": "GPL-3.0-only", "seeAlso": [ @@ -3705,7 +3729,7 @@ "reference": "https://spdx.org/licenses/GPL-3.0-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/GPL-3.0-or-later.json", - "referenceNumber": 390, + "referenceNumber": 269, "name": "GNU General Public License v3.0 or later", "licenseId": "GPL-3.0-or-later", "seeAlso": [ @@ -3719,7 +3743,7 @@ "reference": "https://spdx.org/licenses/GPL-3.0-with-autoconf-exception.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GPL-3.0-with-autoconf-exception.json", - "referenceNumber": 458, + "referenceNumber": 200, "name": "GNU General Public License v3.0 w/Autoconf exception", "licenseId": "GPL-3.0-with-autoconf-exception", "seeAlso": [ @@ -3731,7 +3755,7 @@ "reference": "https://spdx.org/licenses/GPL-3.0-with-GCC-exception.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/GPL-3.0-with-GCC-exception.json", - "referenceNumber": 356, + "referenceNumber": 546, "name": "GNU General Public License v3.0 w/GCC Runtime Library exception", "licenseId": "GPL-3.0-with-GCC-exception", "seeAlso": [ @@ -3743,7 +3767,7 @@ "reference": "https://spdx.org/licenses/Graphics-Gems.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Graphics-Gems.json", - "referenceNumber": 574, + "referenceNumber": 437, "name": "Graphics Gems License", "licenseId": "Graphics-Gems", "seeAlso": [ @@ -3755,7 +3779,7 @@ "reference": "https://spdx.org/licenses/gSOAP-1.3b.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/gSOAP-1.3b.json", - "referenceNumber": 655, + "referenceNumber": 658, "name": "gSOAP Public License v1.3b", "licenseId": "gSOAP-1.3b", "seeAlso": [ @@ -3767,7 +3791,7 @@ "reference": "https://spdx.org/licenses/gtkbook.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/gtkbook.json", - "referenceNumber": 237, + "referenceNumber": 397, "name": "gtkbook License", "licenseId": "gtkbook", "seeAlso": [ @@ -3780,7 +3804,7 @@ "reference": "https://spdx.org/licenses/Gutmann.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Gutmann.json", - "referenceNumber": 441, + "referenceNumber": 103, "name": "Gutmann License", "licenseId": "Gutmann", "seeAlso": [ @@ -3792,7 +3816,7 @@ "reference": "https://spdx.org/licenses/HaskellReport.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HaskellReport.json", - "referenceNumber": 625, + "referenceNumber": 357, "name": "Haskell Language Report License", "licenseId": "HaskellReport", "seeAlso": [ @@ -3804,7 +3828,7 @@ "reference": "https://spdx.org/licenses/hdparm.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/hdparm.json", - "referenceNumber": 81, + "referenceNumber": 351, "name": "hdparm License", "licenseId": "hdparm", "seeAlso": [ @@ -3812,11 +3836,23 @@ ], "isOsiApproved": false }, + { + "reference": "https://spdx.org/licenses/HIDAPI.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HIDAPI.json", + "referenceNumber": 318, + "name": "HIDAPI License", + "licenseId": "HIDAPI", + "seeAlso": [ + "https://github.com/signal11/hidapi/blob/master/LICENSE-orig.txt" + ], + "isOsiApproved": false + }, { "reference": "https://spdx.org/licenses/Hippocratic-2.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Hippocratic-2.1.json", - "referenceNumber": 299, + "referenceNumber": 425, "name": "Hippocratic License 2.1", "licenseId": "Hippocratic-2.1", "seeAlso": [ @@ -3829,7 +3865,7 @@ "reference": "https://spdx.org/licenses/HP-1986.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HP-1986.json", - "referenceNumber": 277, + "referenceNumber": 477, "name": "Hewlett-Packard 1986 License", "licenseId": "HP-1986", "seeAlso": [ @@ -3841,7 +3877,7 @@ "reference": "https://spdx.org/licenses/HP-1989.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HP-1989.json", - "referenceNumber": 639, + "referenceNumber": 653, "name": "Hewlett-Packard 1989 License", "licenseId": "HP-1989", "seeAlso": [ @@ -3853,7 +3889,7 @@ "reference": "https://spdx.org/licenses/HPND.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND.json", - "referenceNumber": 281, + "referenceNumber": 75, "name": "Historical Permission Notice and Disclaimer", "licenseId": "HPND", "seeAlso": [ @@ -3867,7 +3903,7 @@ "reference": "https://spdx.org/licenses/HPND-DEC.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-DEC.json", - "referenceNumber": 577, + "referenceNumber": 597, "name": "Historical Permission Notice and Disclaimer - DEC variant", "licenseId": "HPND-DEC", "seeAlso": [ @@ -3879,7 +3915,7 @@ "reference": "https://spdx.org/licenses/HPND-doc.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-doc.json", - "referenceNumber": 391, + "referenceNumber": 384, "name": "Historical Permission Notice and Disclaimer - documentation variant", "licenseId": "HPND-doc", "seeAlso": [ @@ -3892,7 +3928,7 @@ "reference": "https://spdx.org/licenses/HPND-doc-sell.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-doc-sell.json", - "referenceNumber": 163, + "referenceNumber": 648, "name": "Historical Permission Notice and Disclaimer - documentation sell variant", "licenseId": "HPND-doc-sell", "seeAlso": [ @@ -3905,7 +3941,7 @@ "reference": "https://spdx.org/licenses/HPND-export-US.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-export-US.json", - "referenceNumber": 214, + "referenceNumber": 551, "name": "HPND with US Government export control warning", "licenseId": "HPND-export-US", "seeAlso": [ @@ -3917,7 +3953,7 @@ "reference": "https://spdx.org/licenses/HPND-export-US-acknowledgement.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-export-US-acknowledgement.json", - "referenceNumber": 610, + "referenceNumber": 661, "name": "HPND with US Government export control warning and acknowledgment", "licenseId": "HPND-export-US-acknowledgement", "seeAlso": [ @@ -3930,7 +3966,7 @@ "reference": "https://spdx.org/licenses/HPND-export-US-modify.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-export-US-modify.json", - "referenceNumber": 498, + "referenceNumber": 119, "name": "HPND with US Government export control warning and modification rqmt", "licenseId": "HPND-export-US-modify", "seeAlso": [ @@ -3943,7 +3979,7 @@ "reference": "https://spdx.org/licenses/HPND-export2-US.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-export2-US.json", - "referenceNumber": 33, + "referenceNumber": 450, "name": "HPND with US Government export control and 2 disclaimers", "licenseId": "HPND-export2-US", "seeAlso": [ @@ -3956,7 +3992,7 @@ "reference": "https://spdx.org/licenses/HPND-Fenneberg-Livingston.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-Fenneberg-Livingston.json", - "referenceNumber": 145, + "referenceNumber": 643, "name": "Historical Permission Notice and Disclaimer - Fenneberg-Livingston variant", "licenseId": "HPND-Fenneberg-Livingston", "seeAlso": [ @@ -3969,7 +4005,7 @@ "reference": "https://spdx.org/licenses/HPND-INRIA-IMAG.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-INRIA-IMAG.json", - "referenceNumber": 614, + "referenceNumber": 583, "name": "Historical Permission Notice and Disclaimer - INRIA-IMAG variant", "licenseId": "HPND-INRIA-IMAG", "seeAlso": [ @@ -3981,7 +4017,7 @@ "reference": "https://spdx.org/licenses/HPND-Intel.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-Intel.json", - "referenceNumber": 195, + "referenceNumber": 20, "name": "Historical Permission Notice and Disclaimer - Intel variant", "licenseId": "HPND-Intel", "seeAlso": [ @@ -3993,7 +4029,7 @@ "reference": "https://spdx.org/licenses/HPND-Kevlin-Henney.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-Kevlin-Henney.json", - "referenceNumber": 428, + "referenceNumber": 637, "name": "Historical Permission Notice and Disclaimer - Kevlin Henney variant", "licenseId": "HPND-Kevlin-Henney", "seeAlso": [ @@ -4005,7 +4041,7 @@ "reference": "https://spdx.org/licenses/HPND-Markus-Kuhn.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-Markus-Kuhn.json", - "referenceNumber": 8, + "referenceNumber": 172, "name": "Historical Permission Notice and Disclaimer - Markus Kuhn variant", "licenseId": "HPND-Markus-Kuhn", "seeAlso": [ @@ -4018,7 +4054,7 @@ "reference": "https://spdx.org/licenses/HPND-merchantability-variant.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-merchantability-variant.json", - "referenceNumber": 540, + "referenceNumber": 572, "name": "Historical Permission Notice and Disclaimer - merchantability variant", "licenseId": "HPND-merchantability-variant", "seeAlso": [ @@ -4030,7 +4066,7 @@ "reference": "https://spdx.org/licenses/HPND-MIT-disclaimer.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-MIT-disclaimer.json", - "referenceNumber": 185, + "referenceNumber": 609, "name": "Historical Permission Notice and Disclaimer with MIT disclaimer", "licenseId": "HPND-MIT-disclaimer", "seeAlso": [ @@ -4038,11 +4074,21 @@ ], "isOsiApproved": false }, + { + "reference": "https://spdx.org/licenses/HPND-Netrek.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/HPND-Netrek.json", + "referenceNumber": 126, + "name": "Historical Permission Notice and Disclaimer - Netrek variant", + "licenseId": "HPND-Netrek", + "seeAlso": [], + "isOsiApproved": false + }, { "reference": "https://spdx.org/licenses/HPND-Pbmplus.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-Pbmplus.json", - "referenceNumber": 603, + "referenceNumber": 242, "name": "Historical Permission Notice and Disclaimer - Pbmplus variant", "licenseId": "HPND-Pbmplus", "seeAlso": [ @@ -4054,7 +4100,7 @@ "reference": "https://spdx.org/licenses/HPND-sell-MIT-disclaimer-xserver.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-sell-MIT-disclaimer-xserver.json", - "referenceNumber": 125, + "referenceNumber": 160, "name": "Historical Permission Notice and Disclaimer - sell xserver variant with MIT disclaimer", "licenseId": "HPND-sell-MIT-disclaimer-xserver", "seeAlso": [ @@ -4066,7 +4112,7 @@ "reference": "https://spdx.org/licenses/HPND-sell-regexpr.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-sell-regexpr.json", - "referenceNumber": 633, + "referenceNumber": 44, "name": "Historical Permission Notice and Disclaimer - sell regexpr variant", "licenseId": "HPND-sell-regexpr", "seeAlso": [ @@ -4078,7 +4124,7 @@ "reference": "https://spdx.org/licenses/HPND-sell-variant.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-sell-variant.json", - "referenceNumber": 344, + "referenceNumber": 485, "name": "Historical Permission Notice and Disclaimer - sell variant", "licenseId": "HPND-sell-variant", "seeAlso": [ @@ -4090,7 +4136,7 @@ "reference": "https://spdx.org/licenses/HPND-sell-variant-MIT-disclaimer.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-sell-variant-MIT-disclaimer.json", - "referenceNumber": 160, + "referenceNumber": 430, "name": "HPND sell variant with MIT disclaimer", "licenseId": "HPND-sell-variant-MIT-disclaimer", "seeAlso": [ @@ -4102,7 +4148,7 @@ "reference": "https://spdx.org/licenses/HPND-sell-variant-MIT-disclaimer-rev.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-sell-variant-MIT-disclaimer-rev.json", - "referenceNumber": 609, + "referenceNumber": 10, "name": "HPND sell variant with MIT disclaimer - reverse", "licenseId": "HPND-sell-variant-MIT-disclaimer-rev", "seeAlso": [ @@ -4114,7 +4160,7 @@ "reference": "https://spdx.org/licenses/HPND-UC.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-UC.json", - "referenceNumber": 386, + "referenceNumber": 423, "name": "Historical Permission Notice and Disclaimer - University of California variant", "licenseId": "HPND-UC", "seeAlso": [ @@ -4126,7 +4172,7 @@ "reference": "https://spdx.org/licenses/HPND-UC-export-US.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HPND-UC-export-US.json", - "referenceNumber": 118, + "referenceNumber": 82, "name": "Historical Permission Notice and Disclaimer - University of California, US export warning", "licenseId": "HPND-UC-export-US", "seeAlso": [ @@ -4138,7 +4184,7 @@ "reference": "https://spdx.org/licenses/HTMLTIDY.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/HTMLTIDY.json", - "referenceNumber": 134, + "referenceNumber": 439, "name": "HTML Tidy License", "licenseId": "HTMLTIDY", "seeAlso": [ @@ -4150,7 +4196,7 @@ "reference": "https://spdx.org/licenses/IBM-pibs.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/IBM-pibs.json", - "referenceNumber": 102, + "referenceNumber": 604, "name": "IBM PowerPC Initialization and Boot Software", "licenseId": "IBM-pibs", "seeAlso": [ @@ -4162,7 +4208,7 @@ "reference": "https://spdx.org/licenses/ICU.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ICU.json", - "referenceNumber": 67, + "referenceNumber": 375, "name": "ICU License", "licenseId": "ICU", "seeAlso": [ @@ -4174,7 +4220,7 @@ "reference": "https://spdx.org/licenses/IEC-Code-Components-EULA.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/IEC-Code-Components-EULA.json", - "referenceNumber": 359, + "referenceNumber": 18, "name": "IEC Code Components End-user licence agreement", "licenseId": "IEC-Code-Components-EULA", "seeAlso": [ @@ -4188,7 +4234,7 @@ "reference": "https://spdx.org/licenses/IJG.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/IJG.json", - "referenceNumber": 641, + "referenceNumber": 374, "name": "Independent JPEG Group License", "licenseId": "IJG", "seeAlso": [ @@ -4201,7 +4247,7 @@ "reference": "https://spdx.org/licenses/IJG-short.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/IJG-short.json", - "referenceNumber": 615, + "referenceNumber": 152, "name": "Independent JPEG Group License - short", "licenseId": "IJG-short", "seeAlso": [ @@ -4213,7 +4259,7 @@ "reference": "https://spdx.org/licenses/ImageMagick.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ImageMagick.json", - "referenceNumber": 478, + "referenceNumber": 608, "name": "ImageMagick License", "licenseId": "ImageMagick", "seeAlso": [ @@ -4225,7 +4271,7 @@ "reference": "https://spdx.org/licenses/iMatix.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/iMatix.json", - "referenceNumber": 327, + "referenceNumber": 645, "name": "iMatix Standard Function Library Agreement", "licenseId": "iMatix", "seeAlso": [ @@ -4238,7 +4284,7 @@ "reference": "https://spdx.org/licenses/Imlib2.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Imlib2.json", - "referenceNumber": 169, + "referenceNumber": 96, "name": "Imlib2 License", "licenseId": "Imlib2", "seeAlso": [ @@ -4252,7 +4298,7 @@ "reference": "https://spdx.org/licenses/Info-ZIP.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Info-ZIP.json", - "referenceNumber": 269, + "referenceNumber": 451, "name": "Info-ZIP License", "licenseId": "Info-ZIP", "seeAlso": [ @@ -4264,7 +4310,7 @@ "reference": "https://spdx.org/licenses/Inner-Net-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Inner-Net-2.0.json", - "referenceNumber": 572, + "referenceNumber": 58, "name": "Inner Net License v2.0", "licenseId": "Inner-Net-2.0", "seeAlso": [ @@ -4277,7 +4323,7 @@ "reference": "https://spdx.org/licenses/Intel.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Intel.json", - "referenceNumber": 623, + "referenceNumber": 316, "name": "Intel Open Source License", "licenseId": "Intel", "seeAlso": [ @@ -4290,7 +4336,7 @@ "reference": "https://spdx.org/licenses/Intel-ACPI.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Intel-ACPI.json", - "referenceNumber": 571, + "referenceNumber": 309, "name": "Intel ACPI Software License Agreement", "licenseId": "Intel-ACPI", "seeAlso": [ @@ -4302,7 +4348,7 @@ "reference": "https://spdx.org/licenses/Interbase-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Interbase-1.0.json", - "referenceNumber": 654, + "referenceNumber": 665, "name": "Interbase Public License v1.0", "licenseId": "Interbase-1.0", "seeAlso": [ @@ -4314,7 +4360,7 @@ "reference": "https://spdx.org/licenses/IPA.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/IPA.json", - "referenceNumber": 94, + "referenceNumber": 237, "name": "IPA Font License", "licenseId": "IPA", "seeAlso": [ @@ -4327,7 +4373,7 @@ "reference": "https://spdx.org/licenses/IPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/IPL-1.0.json", - "referenceNumber": 332, + "referenceNumber": 443, "name": "IBM Public License v1.0", "licenseId": "IPL-1.0", "seeAlso": [ @@ -4340,7 +4386,7 @@ "reference": "https://spdx.org/licenses/ISC.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ISC.json", - "referenceNumber": 488, + "referenceNumber": 131, "name": "ISC License", "licenseId": "ISC", "seeAlso": [ @@ -4355,7 +4401,7 @@ "reference": "https://spdx.org/licenses/ISC-Veillard.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ISC-Veillard.json", - "referenceNumber": 513, + "referenceNumber": 554, "name": "ISC Veillard variant", "licenseId": "ISC-Veillard", "seeAlso": [ @@ -4369,7 +4415,7 @@ "reference": "https://spdx.org/licenses/Jam.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Jam.json", - "referenceNumber": 108, + "referenceNumber": 338, "name": "Jam License", "licenseId": "Jam", "seeAlso": [ @@ -4382,7 +4428,7 @@ "reference": "https://spdx.org/licenses/JasPer-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/JasPer-2.0.json", - "referenceNumber": 487, + "referenceNumber": 591, "name": "JasPer License", "licenseId": "JasPer-2.0", "seeAlso": [ @@ -4394,7 +4440,7 @@ "reference": "https://spdx.org/licenses/JPL-image.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/JPL-image.json", - "referenceNumber": 363, + "referenceNumber": 343, "name": "JPL Image Use Policy", "licenseId": "JPL-image", "seeAlso": [ @@ -4406,7 +4452,7 @@ "reference": "https://spdx.org/licenses/JPNIC.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/JPNIC.json", - "referenceNumber": 83, + "referenceNumber": 117, "name": "Japan Network Information Center License", "licenseId": "JPNIC", "seeAlso": [ @@ -4418,7 +4464,7 @@ "reference": "https://spdx.org/licenses/JSON.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/JSON.json", - "referenceNumber": 65, + "referenceNumber": 174, "name": "JSON License", "licenseId": "JSON", "seeAlso": [ @@ -4431,7 +4477,7 @@ "reference": "https://spdx.org/licenses/Kastrup.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Kastrup.json", - "referenceNumber": 226, + "referenceNumber": 181, "name": "Kastrup License", "licenseId": "Kastrup", "seeAlso": [ @@ -4443,7 +4489,7 @@ "reference": "https://spdx.org/licenses/Kazlib.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Kazlib.json", - "referenceNumber": 232, + "referenceNumber": 197, "name": "Kazlib License", "licenseId": "Kazlib", "seeAlso": [ @@ -4455,7 +4501,7 @@ "reference": "https://spdx.org/licenses/Knuth-CTAN.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Knuth-CTAN.json", - "referenceNumber": 290, + "referenceNumber": 22, "name": "Knuth CTAN License", "licenseId": "Knuth-CTAN", "seeAlso": [ @@ -4467,7 +4513,7 @@ "reference": "https://spdx.org/licenses/LAL-1.2.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LAL-1.2.json", - "referenceNumber": 165, + "referenceNumber": 261, "name": "Licence Art Libre 1.2", "licenseId": "LAL-1.2", "seeAlso": [ @@ -4479,7 +4525,7 @@ "reference": "https://spdx.org/licenses/LAL-1.3.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LAL-1.3.json", - "referenceNumber": 600, + "referenceNumber": 526, "name": "Licence Art Libre 1.3", "licenseId": "LAL-1.3", "seeAlso": [ @@ -4491,7 +4537,7 @@ "reference": "https://spdx.org/licenses/Latex2e.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Latex2e.json", - "referenceNumber": 439, + "referenceNumber": 3, "name": "Latex2e License", "licenseId": "Latex2e", "seeAlso": [ @@ -4503,7 +4549,7 @@ "reference": "https://spdx.org/licenses/Latex2e-translated-notice.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Latex2e-translated-notice.json", - "referenceNumber": 620, + "referenceNumber": 104, "name": "Latex2e with translated notice permission", "licenseId": "Latex2e-translated-notice", "seeAlso": [ @@ -4515,7 +4561,7 @@ "reference": "https://spdx.org/licenses/Leptonica.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Leptonica.json", - "referenceNumber": 103, + "referenceNumber": 221, "name": "Leptonica License", "licenseId": "Leptonica", "seeAlso": [ @@ -4527,7 +4573,7 @@ "reference": "https://spdx.org/licenses/LGPL-2.0.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/LGPL-2.0.json", - "referenceNumber": 353, + "referenceNumber": 81, "name": "GNU Library General Public License v2 only", "licenseId": "LGPL-2.0", "seeAlso": [ @@ -4539,7 +4585,7 @@ "reference": "https://spdx.org/licenses/LGPL-2.0+.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/LGPL-2.0+.json", - "referenceNumber": 62, + "referenceNumber": 265, "name": "GNU Library General Public License v2 or later", "licenseId": "LGPL-2.0+", "seeAlso": [ @@ -4551,7 +4597,7 @@ "reference": "https://spdx.org/licenses/LGPL-2.0-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LGPL-2.0-only.json", - "referenceNumber": 519, + "referenceNumber": 517, "name": "GNU Library General Public License v2 only", "licenseId": "LGPL-2.0-only", "seeAlso": [ @@ -4563,7 +4609,7 @@ "reference": "https://spdx.org/licenses/LGPL-2.0-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LGPL-2.0-or-later.json", - "referenceNumber": 366, + "referenceNumber": 458, "name": "GNU Library General Public License v2 or later", "licenseId": "LGPL-2.0-or-later", "seeAlso": [ @@ -4575,7 +4621,7 @@ "reference": "https://spdx.org/licenses/LGPL-2.1.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/LGPL-2.1.json", - "referenceNumber": 656, + "referenceNumber": 659, "name": "GNU Lesser General Public License v2.1 only", "licenseId": "LGPL-2.1", "seeAlso": [ @@ -4589,7 +4635,7 @@ "reference": "https://spdx.org/licenses/LGPL-2.1+.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/LGPL-2.1+.json", - "referenceNumber": 64, + "referenceNumber": 69, "name": "GNU Lesser General Public License v2.1 or later", "licenseId": "LGPL-2.1+", "seeAlso": [ @@ -4603,7 +4649,7 @@ "reference": "https://spdx.org/licenses/LGPL-2.1-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LGPL-2.1-only.json", - "referenceNumber": 177, + "referenceNumber": 524, "name": "GNU Lesser General Public License v2.1 only", "licenseId": "LGPL-2.1-only", "seeAlso": [ @@ -4617,7 +4663,7 @@ "reference": "https://spdx.org/licenses/LGPL-2.1-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LGPL-2.1-or-later.json", - "referenceNumber": 24, + "referenceNumber": 336, "name": "GNU Lesser General Public License v2.1 or later", "licenseId": "LGPL-2.1-or-later", "seeAlso": [ @@ -4631,7 +4677,7 @@ "reference": "https://spdx.org/licenses/LGPL-3.0.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/LGPL-3.0.json", - "referenceNumber": 578, + "referenceNumber": 381, "name": "GNU Lesser General Public License v3.0 only", "licenseId": "LGPL-3.0", "seeAlso": [ @@ -4646,7 +4692,7 @@ "reference": "https://spdx.org/licenses/LGPL-3.0+.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/LGPL-3.0+.json", - "referenceNumber": 233, + "referenceNumber": 303, "name": "GNU Lesser General Public License v3.0 or later", "licenseId": "LGPL-3.0+", "seeAlso": [ @@ -4661,7 +4707,7 @@ "reference": "https://spdx.org/licenses/LGPL-3.0-only.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LGPL-3.0-only.json", - "referenceNumber": 3, + "referenceNumber": 225, "name": "GNU Lesser General Public License v3.0 only", "licenseId": "LGPL-3.0-only", "seeAlso": [ @@ -4676,7 +4722,7 @@ "reference": "https://spdx.org/licenses/LGPL-3.0-or-later.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LGPL-3.0-or-later.json", - "referenceNumber": 262, + "referenceNumber": 411, "name": "GNU Lesser General Public License v3.0 or later", "licenseId": "LGPL-3.0-or-later", "seeAlso": [ @@ -4691,7 +4737,7 @@ "reference": "https://spdx.org/licenses/LGPLLR.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LGPLLR.json", - "referenceNumber": 477, + "referenceNumber": 87, "name": "Lesser General Public License For Linguistic Resources", "licenseId": "LGPLLR", "seeAlso": [ @@ -4703,7 +4749,7 @@ "reference": "https://spdx.org/licenses/Libpng.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Libpng.json", - "referenceNumber": 186, + "referenceNumber": 531, "name": "libpng License", "licenseId": "Libpng", "seeAlso": [ @@ -4715,7 +4761,7 @@ "reference": "https://spdx.org/licenses/libpng-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/libpng-2.0.json", - "referenceNumber": 257, + "referenceNumber": 149, "name": "PNG Reference Library version 2", "licenseId": "libpng-2.0", "seeAlso": [ @@ -4727,7 +4773,7 @@ "reference": "https://spdx.org/licenses/libselinux-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/libselinux-1.0.json", - "referenceNumber": 556, + "referenceNumber": 320, "name": "libselinux public domain notice", "licenseId": "libselinux-1.0", "seeAlso": [ @@ -4739,7 +4785,7 @@ "reference": "https://spdx.org/licenses/libtiff.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/libtiff.json", - "referenceNumber": 392, + "referenceNumber": 24, "name": "libtiff License", "licenseId": "libtiff", "seeAlso": [ @@ -4751,7 +4797,7 @@ "reference": "https://spdx.org/licenses/libutil-David-Nugent.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/libutil-David-Nugent.json", - "referenceNumber": 400, + "referenceNumber": 662, "name": "libutil David Nugent License", "licenseId": "libutil-David-Nugent", "seeAlso": [ @@ -4764,7 +4810,7 @@ "reference": "https://spdx.org/licenses/LiLiQ-P-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LiLiQ-P-1.1.json", - "referenceNumber": 43, + "referenceNumber": 150, "name": "Licence Libre du Québec – Permissive version 1.1", "licenseId": "LiLiQ-P-1.1", "seeAlso": [ @@ -4777,7 +4823,7 @@ "reference": "https://spdx.org/licenses/LiLiQ-R-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LiLiQ-R-1.1.json", - "referenceNumber": 74, + "referenceNumber": 203, "name": "Licence Libre du Québec – Réciprocité version 1.1", "licenseId": "LiLiQ-R-1.1", "seeAlso": [ @@ -4790,7 +4836,7 @@ "reference": "https://spdx.org/licenses/LiLiQ-Rplus-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LiLiQ-Rplus-1.1.json", - "referenceNumber": 40, + "referenceNumber": 314, "name": "Licence Libre du Québec – Réciprocité forte version 1.1", "licenseId": "LiLiQ-Rplus-1.1", "seeAlso": [ @@ -4803,7 +4849,7 @@ "reference": "https://spdx.org/licenses/Linux-man-pages-1-para.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Linux-man-pages-1-para.json", - "referenceNumber": 339, + "referenceNumber": 577, "name": "Linux man-pages - 1 paragraph", "licenseId": "Linux-man-pages-1-para", "seeAlso": [ @@ -4815,7 +4861,7 @@ "reference": "https://spdx.org/licenses/Linux-man-pages-copyleft.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Linux-man-pages-copyleft.json", - "referenceNumber": 590, + "referenceNumber": 213, "name": "Linux man-pages Copyleft", "licenseId": "Linux-man-pages-copyleft", "seeAlso": [ @@ -4827,7 +4873,7 @@ "reference": "https://spdx.org/licenses/Linux-man-pages-copyleft-2-para.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Linux-man-pages-copyleft-2-para.json", - "referenceNumber": 86, + "referenceNumber": 352, "name": "Linux man-pages Copyleft - 2 paragraphs", "licenseId": "Linux-man-pages-copyleft-2-para", "seeAlso": [ @@ -4840,7 +4886,7 @@ "reference": "https://spdx.org/licenses/Linux-man-pages-copyleft-var.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Linux-man-pages-copyleft-var.json", - "referenceNumber": 337, + "referenceNumber": 186, "name": "Linux man-pages Copyleft Variant", "licenseId": "Linux-man-pages-copyleft-var", "seeAlso": [ @@ -4852,7 +4898,7 @@ "reference": "https://spdx.org/licenses/Linux-OpenIB.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Linux-OpenIB.json", - "referenceNumber": 613, + "referenceNumber": 278, "name": "Linux Kernel Variant of OpenIB.org license", "licenseId": "Linux-OpenIB", "seeAlso": [ @@ -4864,7 +4910,7 @@ "reference": "https://spdx.org/licenses/LOOP.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LOOP.json", - "referenceNumber": 607, + "referenceNumber": 521, "name": "Common Lisp LOOP License", "licenseId": "LOOP", "seeAlso": [ @@ -4881,7 +4927,7 @@ "reference": "https://spdx.org/licenses/LPD-document.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LPD-document.json", - "referenceNumber": 522, + "referenceNumber": 561, "name": "LPD Documentation License", "licenseId": "LPD-document", "seeAlso": [ @@ -4894,7 +4940,7 @@ "reference": "https://spdx.org/licenses/LPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LPL-1.0.json", - "referenceNumber": 196, + "referenceNumber": 267, "name": "Lucent Public License Version 1.0", "licenseId": "LPL-1.0", "seeAlso": [ @@ -4906,7 +4952,7 @@ "reference": "https://spdx.org/licenses/LPL-1.02.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LPL-1.02.json", - "referenceNumber": 69, + "referenceNumber": 122, "name": "Lucent Public License v1.02", "licenseId": "LPL-1.02", "seeAlso": [ @@ -4920,7 +4966,7 @@ "reference": "https://spdx.org/licenses/LPPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LPPL-1.0.json", - "referenceNumber": 215, + "referenceNumber": 133, "name": "LaTeX Project Public License v1.0", "licenseId": "LPPL-1.0", "seeAlso": [ @@ -4932,7 +4978,7 @@ "reference": "https://spdx.org/licenses/LPPL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LPPL-1.1.json", - "referenceNumber": 114, + "referenceNumber": 284, "name": "LaTeX Project Public License v1.1", "licenseId": "LPPL-1.1", "seeAlso": [ @@ -4944,7 +4990,7 @@ "reference": "https://spdx.org/licenses/LPPL-1.2.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LPPL-1.2.json", - "referenceNumber": 435, + "referenceNumber": 407, "name": "LaTeX Project Public License v1.2", "licenseId": "LPPL-1.2", "seeAlso": [ @@ -4957,7 +5003,7 @@ "reference": "https://spdx.org/licenses/LPPL-1.3a.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LPPL-1.3a.json", - "referenceNumber": 18, + "referenceNumber": 510, "name": "LaTeX Project Public License v1.3a", "licenseId": "LPPL-1.3a", "seeAlso": [ @@ -4970,7 +5016,7 @@ "reference": "https://spdx.org/licenses/LPPL-1.3c.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LPPL-1.3c.json", - "referenceNumber": 240, + "referenceNumber": 300, "name": "LaTeX Project Public License v1.3c", "licenseId": "LPPL-1.3c", "seeAlso": [ @@ -4983,7 +5029,7 @@ "reference": "https://spdx.org/licenses/lsof.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/lsof.json", - "referenceNumber": 605, + "referenceNumber": 76, "name": "lsof License", "licenseId": "lsof", "seeAlso": [ @@ -4995,7 +5041,7 @@ "reference": "https://spdx.org/licenses/Lucida-Bitmap-Fonts.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Lucida-Bitmap-Fonts.json", - "referenceNumber": 399, + "referenceNumber": 383, "name": "Lucida Bitmap Fonts License", "licenseId": "Lucida-Bitmap-Fonts", "seeAlso": [ @@ -5007,7 +5053,7 @@ "reference": "https://spdx.org/licenses/LZMA-SDK-9.11-to-9.20.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LZMA-SDK-9.11-to-9.20.json", - "referenceNumber": 430, + "referenceNumber": 239, "name": "LZMA SDK License (versions 9.11 to 9.20)", "licenseId": "LZMA-SDK-9.11-to-9.20", "seeAlso": [ @@ -5020,7 +5066,7 @@ "reference": "https://spdx.org/licenses/LZMA-SDK-9.22.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/LZMA-SDK-9.22.json", - "referenceNumber": 244, + "referenceNumber": 600, "name": "LZMA SDK License (versions 9.22 and beyond)", "licenseId": "LZMA-SDK-9.22", "seeAlso": [ @@ -5033,7 +5079,7 @@ "reference": "https://spdx.org/licenses/Mackerras-3-Clause.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Mackerras-3-Clause.json", - "referenceNumber": 59, + "referenceNumber": 540, "name": "Mackerras 3-Clause License", "licenseId": "Mackerras-3-Clause", "seeAlso": [ @@ -5045,7 +5091,7 @@ "reference": "https://spdx.org/licenses/Mackerras-3-Clause-acknowledgment.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Mackerras-3-Clause-acknowledgment.json", - "referenceNumber": 598, + "referenceNumber": 176, "name": "Mackerras 3-Clause - acknowledgment variant", "licenseId": "Mackerras-3-Clause-acknowledgment", "seeAlso": [ @@ -5057,7 +5103,7 @@ "reference": "https://spdx.org/licenses/magaz.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/magaz.json", - "referenceNumber": 516, + "referenceNumber": 93, "name": "magaz License", "licenseId": "magaz", "seeAlso": [ @@ -5069,7 +5115,7 @@ "reference": "https://spdx.org/licenses/mailprio.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/mailprio.json", - "referenceNumber": 179, + "referenceNumber": 292, "name": "mailprio License", "licenseId": "mailprio", "seeAlso": [ @@ -5081,7 +5127,7 @@ "reference": "https://spdx.org/licenses/MakeIndex.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MakeIndex.json", - "referenceNumber": 206, + "referenceNumber": 552, "name": "MakeIndex License", "licenseId": "MakeIndex", "seeAlso": [ @@ -5093,7 +5139,7 @@ "reference": "https://spdx.org/licenses/Martin-Birgmeier.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Martin-Birgmeier.json", - "referenceNumber": 535, + "referenceNumber": 364, "name": "Martin Birgmeier License", "licenseId": "Martin-Birgmeier", "seeAlso": [ @@ -5105,7 +5151,7 @@ "reference": "https://spdx.org/licenses/McPhee-slideshow.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/McPhee-slideshow.json", - "referenceNumber": 492, + "referenceNumber": 511, "name": "McPhee Slideshow License", "licenseId": "McPhee-slideshow", "seeAlso": [ @@ -5117,7 +5163,7 @@ "reference": "https://spdx.org/licenses/metamail.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/metamail.json", - "referenceNumber": 437, + "referenceNumber": 325, "name": "metamail License", "licenseId": "metamail", "seeAlso": [ @@ -5129,7 +5175,7 @@ "reference": "https://spdx.org/licenses/Minpack.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Minpack.json", - "referenceNumber": 512, + "referenceNumber": 634, "name": "Minpack License", "licenseId": "Minpack", "seeAlso": [ @@ -5142,7 +5188,7 @@ "reference": "https://spdx.org/licenses/MirOS.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MirOS.json", - "referenceNumber": 183, + "referenceNumber": 202, "name": "The MirOS Licence", "licenseId": "MirOS", "seeAlso": [ @@ -5154,7 +5200,7 @@ "reference": "https://spdx.org/licenses/MIT.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MIT.json", - "referenceNumber": 608, + "referenceNumber": 601, "name": "MIT License", "licenseId": "MIT", "seeAlso": [ @@ -5167,7 +5213,7 @@ "reference": "https://spdx.org/licenses/MIT-0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MIT-0.json", - "referenceNumber": 395, + "referenceNumber": 587, "name": "MIT No Attribution", "licenseId": "MIT-0", "seeAlso": [ @@ -5181,7 +5227,7 @@ "reference": "https://spdx.org/licenses/MIT-advertising.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MIT-advertising.json", - "referenceNumber": 293, + "referenceNumber": 39, "name": "Enlightenment License (e16)", "licenseId": "MIT-advertising", "seeAlso": [ @@ -5193,7 +5239,7 @@ "reference": "https://spdx.org/licenses/MIT-CMU.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MIT-CMU.json", - "referenceNumber": 575, + "referenceNumber": 36, "name": "CMU License", "licenseId": "MIT-CMU", "seeAlso": [ @@ -5206,7 +5252,7 @@ "reference": "https://spdx.org/licenses/MIT-enna.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MIT-enna.json", - "referenceNumber": 638, + "referenceNumber": 207, "name": "enna License", "licenseId": "MIT-enna", "seeAlso": [ @@ -5218,7 +5264,7 @@ "reference": "https://spdx.org/licenses/MIT-feh.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MIT-feh.json", - "referenceNumber": 53, + "referenceNumber": 146, "name": "feh License", "licenseId": "MIT-feh", "seeAlso": [ @@ -5230,7 +5276,7 @@ "reference": "https://spdx.org/licenses/MIT-Festival.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MIT-Festival.json", - "referenceNumber": 317, + "referenceNumber": 431, "name": "MIT Festival Variant", "licenseId": "MIT-Festival", "seeAlso": [ @@ -5243,7 +5289,7 @@ "reference": "https://spdx.org/licenses/MIT-Khronos-old.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MIT-Khronos-old.json", - "referenceNumber": 249, + "referenceNumber": 68, "name": "MIT Khronos - old variant", "licenseId": "MIT-Khronos-old", "seeAlso": [ @@ -5255,7 +5301,7 @@ "reference": "https://spdx.org/licenses/MIT-Modern-Variant.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MIT-Modern-Variant.json", - "referenceNumber": 424, + "referenceNumber": 92, "name": "MIT License Modern Variant", "licenseId": "MIT-Modern-Variant", "seeAlso": [ @@ -5269,7 +5315,7 @@ "reference": "https://spdx.org/licenses/MIT-open-group.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MIT-open-group.json", - "referenceNumber": 283, + "referenceNumber": 520, "name": "MIT Open Group variant", "licenseId": "MIT-open-group", "seeAlso": [ @@ -5284,7 +5330,7 @@ "reference": "https://spdx.org/licenses/MIT-testregex.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MIT-testregex.json", - "referenceNumber": 427, + "referenceNumber": 578, "name": "MIT testregex Variant", "licenseId": "MIT-testregex", "seeAlso": [ @@ -5296,7 +5342,7 @@ "reference": "https://spdx.org/licenses/MIT-Wu.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MIT-Wu.json", - "referenceNumber": 459, + "referenceNumber": 156, "name": "MIT Tom Wu Variant", "licenseId": "MIT-Wu", "seeAlso": [ @@ -5308,7 +5354,7 @@ "reference": "https://spdx.org/licenses/MITNFA.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MITNFA.json", - "referenceNumber": 157, + "referenceNumber": 650, "name": "MIT +no-false-attribs license", "licenseId": "MITNFA", "seeAlso": [ @@ -5320,7 +5366,7 @@ "reference": "https://spdx.org/licenses/MMIXware.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MMIXware.json", - "referenceNumber": 474, + "referenceNumber": 444, "name": "MMIXware License", "licenseId": "MMIXware", "seeAlso": [ @@ -5332,7 +5378,7 @@ "reference": "https://spdx.org/licenses/Motosoto.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Motosoto.json", - "referenceNumber": 627, + "referenceNumber": 31, "name": "Motosoto License", "licenseId": "Motosoto", "seeAlso": [ @@ -5344,7 +5390,7 @@ "reference": "https://spdx.org/licenses/MPEG-SSG.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MPEG-SSG.json", - "referenceNumber": 417, + "referenceNumber": 323, "name": "MPEG Software Simulation", "licenseId": "MPEG-SSG", "seeAlso": [ @@ -5356,7 +5402,7 @@ "reference": "https://spdx.org/licenses/mpi-permissive.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/mpi-permissive.json", - "referenceNumber": 80, + "referenceNumber": 459, "name": "mpi Permissive License", "licenseId": "mpi-permissive", "seeAlso": [ @@ -5368,7 +5414,7 @@ "reference": "https://spdx.org/licenses/mpich2.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/mpich2.json", - "referenceNumber": 482, + "referenceNumber": 448, "name": "mpich2 License", "licenseId": "mpich2", "seeAlso": [ @@ -5380,7 +5426,7 @@ "reference": "https://spdx.org/licenses/MPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MPL-1.0.json", - "referenceNumber": 28, + "referenceNumber": 248, "name": "Mozilla Public License 1.0", "licenseId": "MPL-1.0", "seeAlso": [ @@ -5393,7 +5439,7 @@ "reference": "https://spdx.org/licenses/MPL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MPL-1.1.json", - "referenceNumber": 619, + "referenceNumber": 219, "name": "Mozilla Public License 1.1", "licenseId": "MPL-1.1", "seeAlso": [ @@ -5407,7 +5453,7 @@ "reference": "https://spdx.org/licenses/MPL-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MPL-2.0.json", - "referenceNumber": 263, + "referenceNumber": 147, "name": "Mozilla Public License 2.0", "licenseId": "MPL-2.0", "seeAlso": [ @@ -5421,7 +5467,7 @@ "reference": "https://spdx.org/licenses/MPL-2.0-no-copyleft-exception.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MPL-2.0-no-copyleft-exception.json", - "referenceNumber": 455, + "referenceNumber": 529, "name": "Mozilla Public License 2.0 (no copyleft exception)", "licenseId": "MPL-2.0-no-copyleft-exception", "seeAlso": [ @@ -5434,7 +5480,7 @@ "reference": "https://spdx.org/licenses/mplus.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/mplus.json", - "referenceNumber": 541, + "referenceNumber": 553, "name": "mplus Font License", "licenseId": "mplus", "seeAlso": [ @@ -5446,7 +5492,7 @@ "reference": "https://spdx.org/licenses/MS-LPL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MS-LPL.json", - "referenceNumber": 528, + "referenceNumber": 412, "name": "Microsoft Limited Public License", "licenseId": "MS-LPL", "seeAlso": [ @@ -5460,7 +5506,7 @@ "reference": "https://spdx.org/licenses/MS-PL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MS-PL.json", - "referenceNumber": 499, + "referenceNumber": 360, "name": "Microsoft Public License", "licenseId": "MS-PL", "seeAlso": [ @@ -5474,7 +5520,7 @@ "reference": "https://spdx.org/licenses/MS-RL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MS-RL.json", - "referenceNumber": 343, + "referenceNumber": 212, "name": "Microsoft Reciprocal License", "licenseId": "MS-RL", "seeAlso": [ @@ -5488,7 +5534,7 @@ "reference": "https://spdx.org/licenses/MTLL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MTLL.json", - "referenceNumber": 137, + "referenceNumber": 610, "name": "Matrix Template Library License", "licenseId": "MTLL", "seeAlso": [ @@ -5500,7 +5546,7 @@ "reference": "https://spdx.org/licenses/MulanPSL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MulanPSL-1.0.json", - "referenceNumber": 107, + "referenceNumber": 236, "name": "Mulan Permissive Software License, Version 1", "licenseId": "MulanPSL-1.0", "seeAlso": [ @@ -5513,7 +5559,7 @@ "reference": "https://spdx.org/licenses/MulanPSL-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/MulanPSL-2.0.json", - "referenceNumber": 490, + "referenceNumber": 523, "name": "Mulan Permissive Software License, Version 2", "licenseId": "MulanPSL-2.0", "seeAlso": [ @@ -5525,7 +5571,7 @@ "reference": "https://spdx.org/licenses/Multics.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Multics.json", - "referenceNumber": 573, + "referenceNumber": 462, "name": "Multics License", "licenseId": "Multics", "seeAlso": [ @@ -5537,7 +5583,7 @@ "reference": "https://spdx.org/licenses/Mup.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Mup.json", - "referenceNumber": 440, + "referenceNumber": 515, "name": "Mup License", "licenseId": "Mup", "seeAlso": [ @@ -5549,7 +5595,7 @@ "reference": "https://spdx.org/licenses/NAIST-2003.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NAIST-2003.json", - "referenceNumber": 104, + "referenceNumber": 118, "name": "Nara Institute of Science and Technology License (2003)", "licenseId": "NAIST-2003", "seeAlso": [ @@ -5562,7 +5608,7 @@ "reference": "https://spdx.org/licenses/NASA-1.3.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NASA-1.3.json", - "referenceNumber": 127, + "referenceNumber": 488, "name": "NASA Open Source Agreement 1.3", "licenseId": "NASA-1.3", "seeAlso": [ @@ -5576,7 +5622,7 @@ "reference": "https://spdx.org/licenses/Naumen.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Naumen.json", - "referenceNumber": 128, + "referenceNumber": 400, "name": "Naumen Public License", "licenseId": "Naumen", "seeAlso": [ @@ -5588,7 +5634,7 @@ "reference": "https://spdx.org/licenses/NBPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NBPL-1.0.json", - "referenceNumber": 41, + "referenceNumber": 168, "name": "Net Boolean Public License v1", "licenseId": "NBPL-1.0", "seeAlso": [ @@ -5600,7 +5646,7 @@ "reference": "https://spdx.org/licenses/NCBI-PD.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NCBI-PD.json", - "referenceNumber": 362, + "referenceNumber": 599, "name": "NCBI Public Domain Notice", "licenseId": "NCBI-PD", "seeAlso": [ @@ -5616,7 +5662,7 @@ "reference": "https://spdx.org/licenses/NCGL-UK-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NCGL-UK-2.0.json", - "referenceNumber": 320, + "referenceNumber": 130, "name": "Non-Commercial Government Licence", "licenseId": "NCGL-UK-2.0", "seeAlso": [ @@ -5628,7 +5674,7 @@ "reference": "https://spdx.org/licenses/NCL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NCL.json", - "referenceNumber": 153, + "referenceNumber": 516, "name": "NCL Source Code License", "licenseId": "NCL", "seeAlso": [ @@ -5640,7 +5686,7 @@ "reference": "https://spdx.org/licenses/NCSA.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NCSA.json", - "referenceNumber": 557, + "referenceNumber": 652, "name": "University of Illinois/NCSA Open Source License", "licenseId": "NCSA", "seeAlso": [ @@ -5652,9 +5698,9 @@ }, { "reference": "https://spdx.org/licenses/Net-SNMP.html", - "isDeprecatedLicenseId": false, + "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/Net-SNMP.json", - "referenceNumber": 234, + "referenceNumber": 469, "name": "Net-SNMP License", "licenseId": "Net-SNMP", "seeAlso": [ @@ -5666,7 +5712,7 @@ "reference": "https://spdx.org/licenses/NetCDF.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NetCDF.json", - "referenceNumber": 503, + "referenceNumber": 189, "name": "NetCDF license", "licenseId": "NetCDF", "seeAlso": [ @@ -5678,7 +5724,7 @@ "reference": "https://spdx.org/licenses/Newsletr.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Newsletr.json", - "referenceNumber": 412, + "referenceNumber": 499, "name": "Newsletr License", "licenseId": "Newsletr", "seeAlso": [ @@ -5690,7 +5736,7 @@ "reference": "https://spdx.org/licenses/NGPL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NGPL.json", - "referenceNumber": 275, + "referenceNumber": 167, "name": "Nethack General Public License", "licenseId": "NGPL", "seeAlso": [ @@ -5702,7 +5748,7 @@ "reference": "https://spdx.org/licenses/NICTA-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NICTA-1.0.json", - "referenceNumber": 311, + "referenceNumber": 486, "name": "NICTA Public Software License, Version 1.0", "licenseId": "NICTA-1.0", "seeAlso": [ @@ -5714,7 +5760,7 @@ "reference": "https://spdx.org/licenses/NIST-PD.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NIST-PD.json", - "referenceNumber": 309, + "referenceNumber": 194, "name": "NIST Public Domain Notice", "licenseId": "NIST-PD", "seeAlso": [ @@ -5727,7 +5773,7 @@ "reference": "https://spdx.org/licenses/NIST-PD-fallback.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NIST-PD-fallback.json", - "referenceNumber": 34, + "referenceNumber": 223, "name": "NIST Public Domain Notice with license fallback", "licenseId": "NIST-PD-fallback", "seeAlso": [ @@ -5740,7 +5786,7 @@ "reference": "https://spdx.org/licenses/NIST-Software.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NIST-Software.json", - "referenceNumber": 76, + "referenceNumber": 251, "name": "NIST Software License", "licenseId": "NIST-Software", "seeAlso": [ @@ -5752,7 +5798,7 @@ "reference": "https://spdx.org/licenses/NLOD-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NLOD-1.0.json", - "referenceNumber": 565, + "referenceNumber": 294, "name": "Norwegian Licence for Open Government Data (NLOD) 1.0", "licenseId": "NLOD-1.0", "seeAlso": [ @@ -5764,7 +5810,7 @@ "reference": "https://spdx.org/licenses/NLOD-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NLOD-2.0.json", - "referenceNumber": 483, + "referenceNumber": 566, "name": "Norwegian Licence for Open Government Data (NLOD) 2.0", "licenseId": "NLOD-2.0", "seeAlso": [ @@ -5776,7 +5822,7 @@ "reference": "https://spdx.org/licenses/NLPL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NLPL.json", - "referenceNumber": 71, + "referenceNumber": 367, "name": "No Limit Public License", "licenseId": "NLPL", "seeAlso": [ @@ -5788,7 +5834,7 @@ "reference": "https://spdx.org/licenses/Nokia.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Nokia.json", - "referenceNumber": 44, + "referenceNumber": 145, "name": "Nokia Open Source License", "licenseId": "Nokia", "seeAlso": [ @@ -5801,7 +5847,7 @@ "reference": "https://spdx.org/licenses/NOSL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NOSL.json", - "referenceNumber": 126, + "referenceNumber": 254, "name": "Netizen Open Source License", "licenseId": "NOSL", "seeAlso": [ @@ -5814,7 +5860,7 @@ "reference": "https://spdx.org/licenses/Noweb.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Noweb.json", - "referenceNumber": 534, + "referenceNumber": 30, "name": "Noweb License", "licenseId": "Noweb", "seeAlso": [ @@ -5826,7 +5872,7 @@ "reference": "https://spdx.org/licenses/NPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NPL-1.0.json", - "referenceNumber": 346, + "referenceNumber": 211, "name": "Netscape Public License v1.0", "licenseId": "NPL-1.0", "seeAlso": [ @@ -5839,7 +5885,7 @@ "reference": "https://spdx.org/licenses/NPL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NPL-1.1.json", - "referenceNumber": 418, + "referenceNumber": 595, "name": "Netscape Public License v1.1", "licenseId": "NPL-1.1", "seeAlso": [ @@ -5852,7 +5898,7 @@ "reference": "https://spdx.org/licenses/NPOSL-3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NPOSL-3.0.json", - "referenceNumber": 579, + "referenceNumber": 664, "name": "Non-Profit Open Software License 3.0", "licenseId": "NPOSL-3.0", "seeAlso": [ @@ -5864,7 +5910,7 @@ "reference": "https://spdx.org/licenses/NRL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NRL.json", - "referenceNumber": 230, + "referenceNumber": 113, "name": "NRL License", "licenseId": "NRL", "seeAlso": [ @@ -5876,7 +5922,7 @@ "reference": "https://spdx.org/licenses/NTP.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NTP.json", - "referenceNumber": 547, + "referenceNumber": 390, "name": "NTP License", "licenseId": "NTP", "seeAlso": [ @@ -5888,7 +5934,7 @@ "reference": "https://spdx.org/licenses/NTP-0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/NTP-0.json", - "referenceNumber": 460, + "referenceNumber": 64, "name": "NTP No Attribution", "licenseId": "NTP-0", "seeAlso": [ @@ -5900,7 +5946,7 @@ "reference": "https://spdx.org/licenses/Nunit.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/Nunit.json", - "referenceNumber": 634, + "referenceNumber": 27, "name": "Nunit License", "licenseId": "Nunit", "seeAlso": [ @@ -5913,7 +5959,7 @@ "reference": "https://spdx.org/licenses/O-UDA-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/O-UDA-1.0.json", - "referenceNumber": 191, + "referenceNumber": 161, "name": "Open Use of Data Agreement v1.0", "licenseId": "O-UDA-1.0", "seeAlso": [ @@ -5926,7 +5972,7 @@ "reference": "https://spdx.org/licenses/OAR.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OAR.json", - "referenceNumber": 4, + "referenceNumber": 100, "name": "OAR License", "licenseId": "OAR", "seeAlso": [ @@ -5938,7 +5984,7 @@ "reference": "https://spdx.org/licenses/OCCT-PL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OCCT-PL.json", - "referenceNumber": 596, + "referenceNumber": 408, "name": "Open CASCADE Technology Public License", "licenseId": "OCCT-PL", "seeAlso": [ @@ -5950,7 +5996,7 @@ "reference": "https://spdx.org/licenses/OCLC-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OCLC-2.0.json", - "referenceNumber": 308, + "referenceNumber": 468, "name": "OCLC Research Public License 2.0", "licenseId": "OCLC-2.0", "seeAlso": [ @@ -5963,7 +6009,7 @@ "reference": "https://spdx.org/licenses/ODbL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ODbL-1.0.json", - "referenceNumber": 243, + "referenceNumber": 611, "name": "Open Data Commons Open Database License v1.0", "licenseId": "ODbL-1.0", "seeAlso": [ @@ -5977,7 +6023,7 @@ "reference": "https://spdx.org/licenses/ODC-By-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ODC-By-1.0.json", - "referenceNumber": 7, + "referenceNumber": 15, "name": "Open Data Commons Attribution License v1.0", "licenseId": "ODC-By-1.0", "seeAlso": [ @@ -5989,7 +6035,7 @@ "reference": "https://spdx.org/licenses/OFFIS.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OFFIS.json", - "referenceNumber": 238, + "referenceNumber": 418, "name": "OFFIS License", "licenseId": "OFFIS", "seeAlso": [ @@ -6001,7 +6047,7 @@ "reference": "https://spdx.org/licenses/OFL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OFL-1.0.json", - "referenceNumber": 475, + "referenceNumber": 603, "name": "SIL Open Font License 1.0", "licenseId": "OFL-1.0", "seeAlso": [ @@ -6014,7 +6060,7 @@ "reference": "https://spdx.org/licenses/OFL-1.0-no-RFN.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OFL-1.0-no-RFN.json", - "referenceNumber": 23, + "referenceNumber": 545, "name": "SIL Open Font License 1.0 with no Reserved Font Name", "licenseId": "OFL-1.0-no-RFN", "seeAlso": [ @@ -6026,7 +6072,7 @@ "reference": "https://spdx.org/licenses/OFL-1.0-RFN.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OFL-1.0-RFN.json", - "referenceNumber": 11, + "referenceNumber": 446, "name": "SIL Open Font License 1.0 with Reserved Font Name", "licenseId": "OFL-1.0-RFN", "seeAlso": [ @@ -6038,7 +6084,7 @@ "reference": "https://spdx.org/licenses/OFL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OFL-1.1.json", - "referenceNumber": 248, + "referenceNumber": 0, "name": "SIL Open Font License 1.1", "licenseId": "OFL-1.1", "seeAlso": [ @@ -6052,7 +6098,7 @@ "reference": "https://spdx.org/licenses/OFL-1.1-no-RFN.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OFL-1.1-no-RFN.json", - "referenceNumber": 550, + "referenceNumber": 110, "name": "SIL Open Font License 1.1 with no Reserved Font Name", "licenseId": "OFL-1.1-no-RFN", "seeAlso": [ @@ -6065,7 +6111,7 @@ "reference": "https://spdx.org/licenses/OFL-1.1-RFN.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OFL-1.1-RFN.json", - "referenceNumber": 507, + "referenceNumber": 90, "name": "SIL Open Font License 1.1 with Reserved Font Name", "licenseId": "OFL-1.1-RFN", "seeAlso": [ @@ -6078,7 +6124,7 @@ "reference": "https://spdx.org/licenses/OGC-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OGC-1.0.json", - "referenceNumber": 166, + "referenceNumber": 504, "name": "OGC Software License, Version 1.0", "licenseId": "OGC-1.0", "seeAlso": [ @@ -6090,7 +6136,7 @@ "reference": "https://spdx.org/licenses/OGDL-Taiwan-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OGDL-Taiwan-1.0.json", - "referenceNumber": 468, + "referenceNumber": 23, "name": "Taiwan Open Government Data License, version 1.0", "licenseId": "OGDL-Taiwan-1.0", "seeAlso": [ @@ -6102,7 +6148,7 @@ "reference": "https://spdx.org/licenses/OGL-Canada-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OGL-Canada-2.0.json", - "referenceNumber": 464, + "referenceNumber": 40, "name": "Open Government Licence - Canada", "licenseId": "OGL-Canada-2.0", "seeAlso": [ @@ -6114,7 +6160,7 @@ "reference": "https://spdx.org/licenses/OGL-UK-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OGL-UK-1.0.json", - "referenceNumber": 489, + "referenceNumber": 497, "name": "Open Government Licence v1.0", "licenseId": "OGL-UK-1.0", "seeAlso": [ @@ -6126,7 +6172,7 @@ "reference": "https://spdx.org/licenses/OGL-UK-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OGL-UK-2.0.json", - "referenceNumber": 467, + "referenceNumber": 556, "name": "Open Government Licence v2.0", "licenseId": "OGL-UK-2.0", "seeAlso": [ @@ -6138,7 +6184,7 @@ "reference": "https://spdx.org/licenses/OGL-UK-3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OGL-UK-3.0.json", - "referenceNumber": 151, + "referenceNumber": 585, "name": "Open Government Licence v3.0", "licenseId": "OGL-UK-3.0", "seeAlso": [ @@ -6150,7 +6196,7 @@ "reference": "https://spdx.org/licenses/OGTSL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OGTSL.json", - "referenceNumber": 367, + "referenceNumber": 97, "name": "Open Group Test Suite License", "licenseId": "OGTSL", "seeAlso": [ @@ -6163,7 +6209,7 @@ "reference": "https://spdx.org/licenses/OLDAP-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-1.1.json", - "referenceNumber": 180, + "referenceNumber": 420, "name": "Open LDAP Public License v1.1", "licenseId": "OLDAP-1.1", "seeAlso": [ @@ -6175,7 +6221,7 @@ "reference": "https://spdx.org/licenses/OLDAP-1.2.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-1.2.json", - "referenceNumber": 229, + "referenceNumber": 487, "name": "Open LDAP Public License v1.2", "licenseId": "OLDAP-1.2", "seeAlso": [ @@ -6187,7 +6233,7 @@ "reference": "https://spdx.org/licenses/OLDAP-1.3.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-1.3.json", - "referenceNumber": 224, + "referenceNumber": 627, "name": "Open LDAP Public License v1.3", "licenseId": "OLDAP-1.3", "seeAlso": [ @@ -6199,7 +6245,7 @@ "reference": "https://spdx.org/licenses/OLDAP-1.4.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-1.4.json", - "referenceNumber": 255, + "referenceNumber": 45, "name": "Open LDAP Public License v1.4", "licenseId": "OLDAP-1.4", "seeAlso": [ @@ -6211,7 +6257,7 @@ "reference": "https://spdx.org/licenses/OLDAP-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-2.0.json", - "referenceNumber": 208, + "referenceNumber": 537, "name": "Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)", "licenseId": "OLDAP-2.0", "seeAlso": [ @@ -6223,7 +6269,7 @@ "reference": "https://spdx.org/licenses/OLDAP-2.0.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-2.0.1.json", - "referenceNumber": 79, + "referenceNumber": 179, "name": "Open LDAP Public License v2.0.1", "licenseId": "OLDAP-2.0.1", "seeAlso": [ @@ -6235,7 +6281,7 @@ "reference": "https://spdx.org/licenses/OLDAP-2.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-2.1.json", - "referenceNumber": 360, + "referenceNumber": 342, "name": "Open LDAP Public License v2.1", "licenseId": "OLDAP-2.1", "seeAlso": [ @@ -6247,7 +6293,7 @@ "reference": "https://spdx.org/licenses/OLDAP-2.2.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-2.2.json", - "referenceNumber": 316, + "referenceNumber": 347, "name": "Open LDAP Public License v2.2", "licenseId": "OLDAP-2.2", "seeAlso": [ @@ -6259,7 +6305,7 @@ "reference": "https://spdx.org/licenses/OLDAP-2.2.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-2.2.1.json", - "referenceNumber": 426, + "referenceNumber": 208, "name": "Open LDAP Public License v2.2.1", "licenseId": "OLDAP-2.2.1", "seeAlso": [ @@ -6271,7 +6317,7 @@ "reference": "https://spdx.org/licenses/OLDAP-2.2.2.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-2.2.2.json", - "referenceNumber": 384, + "referenceNumber": 312, "name": "Open LDAP Public License 2.2.2", "licenseId": "OLDAP-2.2.2", "seeAlso": [ @@ -6283,7 +6329,7 @@ "reference": "https://spdx.org/licenses/OLDAP-2.3.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-2.3.json", - "referenceNumber": 381, + "referenceNumber": 276, "name": "Open LDAP Public License v2.3", "licenseId": "OLDAP-2.3", "seeAlso": [ @@ -6296,7 +6342,7 @@ "reference": "https://spdx.org/licenses/OLDAP-2.4.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-2.4.json", - "referenceNumber": 93, + "referenceNumber": 108, "name": "Open LDAP Public License v2.4", "licenseId": "OLDAP-2.4", "seeAlso": [ @@ -6308,7 +6354,7 @@ "reference": "https://spdx.org/licenses/OLDAP-2.5.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-2.5.json", - "referenceNumber": 651, + "referenceNumber": 518, "name": "Open LDAP Public License v2.5", "licenseId": "OLDAP-2.5", "seeAlso": [ @@ -6320,7 +6366,7 @@ "reference": "https://spdx.org/licenses/OLDAP-2.6.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-2.6.json", - "referenceNumber": 568, + "referenceNumber": 275, "name": "Open LDAP Public License v2.6", "licenseId": "OLDAP-2.6", "seeAlso": [ @@ -6332,7 +6378,7 @@ "reference": "https://spdx.org/licenses/OLDAP-2.7.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-2.7.json", - "referenceNumber": 220, + "referenceNumber": 79, "name": "Open LDAP Public License v2.7", "licenseId": "OLDAP-2.7", "seeAlso": [ @@ -6345,7 +6391,7 @@ "reference": "https://spdx.org/licenses/OLDAP-2.8.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLDAP-2.8.json", - "referenceNumber": 5, + "referenceNumber": 72, "name": "Open LDAP Public License v2.8", "licenseId": "OLDAP-2.8", "seeAlso": [ @@ -6357,7 +6403,7 @@ "reference": "https://spdx.org/licenses/OLFL-1.3.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OLFL-1.3.json", - "referenceNumber": 142, + "referenceNumber": 204, "name": "Open Logistics Foundation License Version 1.3", "licenseId": "OLFL-1.3", "seeAlso": [ @@ -6370,7 +6416,7 @@ "reference": "https://spdx.org/licenses/OML.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OML.json", - "referenceNumber": 375, + "referenceNumber": 505, "name": "Open Market License", "licenseId": "OML", "seeAlso": [ @@ -6382,7 +6428,7 @@ "reference": "https://spdx.org/licenses/OpenPBS-2.3.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OpenPBS-2.3.json", - "referenceNumber": 314, + "referenceNumber": 159, "name": "OpenPBS v2.3 Software License", "licenseId": "OpenPBS-2.3", "seeAlso": [ @@ -6395,7 +6441,7 @@ "reference": "https://spdx.org/licenses/OpenSSL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OpenSSL.json", - "referenceNumber": 303, + "referenceNumber": 445, "name": "OpenSSL License", "licenseId": "OpenSSL", "seeAlso": [ @@ -6408,7 +6454,7 @@ "reference": "https://spdx.org/licenses/OpenSSL-standalone.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OpenSSL-standalone.json", - "referenceNumber": 602, + "referenceNumber": 635, "name": "OpenSSL License - standalone", "licenseId": "OpenSSL-standalone", "seeAlso": [ @@ -6421,7 +6467,7 @@ "reference": "https://spdx.org/licenses/OpenVision.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OpenVision.json", - "referenceNumber": 588, + "referenceNumber": 589, "name": "OpenVision License", "licenseId": "OpenVision", "seeAlso": [ @@ -6435,7 +6481,7 @@ "reference": "https://spdx.org/licenses/OPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OPL-1.0.json", - "referenceNumber": 91, + "referenceNumber": 80, "name": "Open Public License v1.0", "licenseId": "OPL-1.0", "seeAlso": [ @@ -6449,7 +6495,7 @@ "reference": "https://spdx.org/licenses/OPL-UK-3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OPL-UK-3.0.json", - "referenceNumber": 480, + "referenceNumber": 19, "name": "United Kingdom Open Parliament Licence v3.0", "licenseId": "OPL-UK-3.0", "seeAlso": [ @@ -6461,7 +6507,7 @@ "reference": "https://spdx.org/licenses/OPUBL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OPUBL-1.0.json", - "referenceNumber": 329, + "referenceNumber": 266, "name": "Open Publication License v1.0", "licenseId": "OPUBL-1.0", "seeAlso": [ @@ -6475,7 +6521,7 @@ "reference": "https://spdx.org/licenses/OSET-PL-2.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OSET-PL-2.1.json", - "referenceNumber": 517, + "referenceNumber": 307, "name": "OSET Public License version 2.1", "licenseId": "OSET-PL-2.1", "seeAlso": [ @@ -6488,7 +6534,7 @@ "reference": "https://spdx.org/licenses/OSL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OSL-1.0.json", - "referenceNumber": 162, + "referenceNumber": 306, "name": "Open Software License 1.0", "licenseId": "OSL-1.0", "seeAlso": [ @@ -6501,7 +6547,7 @@ "reference": "https://spdx.org/licenses/OSL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OSL-1.1.json", - "referenceNumber": 586, + "referenceNumber": 111, "name": "Open Software License 1.1", "licenseId": "OSL-1.1", "seeAlso": [ @@ -6514,7 +6560,7 @@ "reference": "https://spdx.org/licenses/OSL-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OSL-2.0.json", - "referenceNumber": 531, + "referenceNumber": 457, "name": "Open Software License 2.0", "licenseId": "OSL-2.0", "seeAlso": [ @@ -6527,7 +6573,7 @@ "reference": "https://spdx.org/licenses/OSL-2.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OSL-2.1.json", - "referenceNumber": 138, + "referenceNumber": 247, "name": "Open Software License 2.1", "licenseId": "OSL-2.1", "seeAlso": [ @@ -6541,7 +6587,7 @@ "reference": "https://spdx.org/licenses/OSL-3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/OSL-3.0.json", - "referenceNumber": 300, + "referenceNumber": 432, "name": "Open Software License 3.0", "licenseId": "OSL-3.0", "seeAlso": [ @@ -6555,7 +6601,7 @@ "reference": "https://spdx.org/licenses/PADL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/PADL.json", - "referenceNumber": 113, + "referenceNumber": 43, "name": "PADL License", "licenseId": "PADL", "seeAlso": [ @@ -6567,7 +6613,7 @@ "reference": "https://spdx.org/licenses/Parity-6.0.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Parity-6.0.0.json", - "referenceNumber": 246, + "referenceNumber": 49, "name": "The Parity Public License 6.0.0", "licenseId": "Parity-6.0.0", "seeAlso": [ @@ -6579,7 +6625,7 @@ "reference": "https://spdx.org/licenses/Parity-7.0.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Parity-7.0.0.json", - "referenceNumber": 212, + "referenceNumber": 482, "name": "The Parity Public License 7.0.0", "licenseId": "Parity-7.0.0", "seeAlso": [ @@ -6591,7 +6637,7 @@ "reference": "https://spdx.org/licenses/PDDL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/PDDL-1.0.json", - "referenceNumber": 493, + "referenceNumber": 210, "name": "Open Data Commons Public Domain Dedication \u0026 License 1.0", "licenseId": "PDDL-1.0", "seeAlso": [ @@ -6604,7 +6650,7 @@ "reference": "https://spdx.org/licenses/PHP-3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/PHP-3.0.json", - "referenceNumber": 584, + "referenceNumber": 580, "name": "PHP License v3.0", "licenseId": "PHP-3.0", "seeAlso": [ @@ -6617,7 +6663,7 @@ "reference": "https://spdx.org/licenses/PHP-3.01.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/PHP-3.01.json", - "referenceNumber": 538, + "referenceNumber": 594, "name": "PHP License v3.01", "licenseId": "PHP-3.01", "seeAlso": [ @@ -6630,7 +6676,7 @@ "reference": "https://spdx.org/licenses/Pixar.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Pixar.json", - "referenceNumber": 204, + "referenceNumber": 619, "name": "Pixar License", "licenseId": "Pixar", "seeAlso": [ @@ -6644,7 +6690,7 @@ "reference": "https://spdx.org/licenses/pkgconf.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/pkgconf.json", - "referenceNumber": 389, + "referenceNumber": 16, "name": "pkgconf License", "licenseId": "pkgconf", "seeAlso": [ @@ -6656,7 +6702,7 @@ "reference": "https://spdx.org/licenses/Plexus.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Plexus.json", - "referenceNumber": 141, + "referenceNumber": 442, "name": "Plexus Classworlds License", "licenseId": "Plexus", "seeAlso": [ @@ -6668,7 +6714,7 @@ "reference": "https://spdx.org/licenses/pnmstitch.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/pnmstitch.json", - "referenceNumber": 158, + "referenceNumber": 502, "name": "pnmstitch License", "licenseId": "pnmstitch", "seeAlso": [ @@ -6680,7 +6726,7 @@ "reference": "https://spdx.org/licenses/PolyForm-Noncommercial-1.0.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/PolyForm-Noncommercial-1.0.0.json", - "referenceNumber": 54, + "referenceNumber": 575, "name": "PolyForm Noncommercial License 1.0.0", "licenseId": "PolyForm-Noncommercial-1.0.0", "seeAlso": [ @@ -6692,7 +6738,7 @@ "reference": "https://spdx.org/licenses/PolyForm-Small-Business-1.0.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/PolyForm-Small-Business-1.0.0.json", - "referenceNumber": 594, + "referenceNumber": 9, "name": "PolyForm Small Business License 1.0.0", "licenseId": "PolyForm-Small-Business-1.0.0", "seeAlso": [ @@ -6704,7 +6750,7 @@ "reference": "https://spdx.org/licenses/PostgreSQL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/PostgreSQL.json", - "referenceNumber": 643, + "referenceNumber": 94, "name": "PostgreSQL License", "licenseId": "PostgreSQL", "seeAlso": [ @@ -6717,7 +6763,7 @@ "reference": "https://spdx.org/licenses/PPL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/PPL.json", - "referenceNumber": 580, + "referenceNumber": 454, "name": "Peer Production License", "licenseId": "PPL", "seeAlso": [ @@ -6731,7 +6777,7 @@ "reference": "https://spdx.org/licenses/PSF-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/PSF-2.0.json", - "referenceNumber": 55, + "referenceNumber": 62, "name": "Python Software Foundation License 2.0", "licenseId": "PSF-2.0", "seeAlso": [ @@ -6743,7 +6789,7 @@ "reference": "https://spdx.org/licenses/psfrag.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/psfrag.json", - "referenceNumber": 555, + "referenceNumber": 279, "name": "psfrag License", "licenseId": "psfrag", "seeAlso": [ @@ -6755,7 +6801,7 @@ "reference": "https://spdx.org/licenses/psutils.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/psutils.json", - "referenceNumber": 260, + "referenceNumber": 387, "name": "psutils License", "licenseId": "psutils", "seeAlso": [ @@ -6767,7 +6813,7 @@ "reference": "https://spdx.org/licenses/Python-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Python-2.0.json", - "referenceNumber": 285, + "referenceNumber": 498, "name": "Python License 2.0", "licenseId": "Python-2.0", "seeAlso": [ @@ -6780,7 +6826,7 @@ "reference": "https://spdx.org/licenses/Python-2.0.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Python-2.0.1.json", - "referenceNumber": 201, + "referenceNumber": 453, "name": "Python License 2.0.1", "licenseId": "Python-2.0.1", "seeAlso": [ @@ -6794,7 +6840,7 @@ "reference": "https://spdx.org/licenses/python-ldap.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/python-ldap.json", - "referenceNumber": 446, + "referenceNumber": 422, "name": "Python ldap License", "licenseId": "python-ldap", "seeAlso": [ @@ -6806,7 +6852,7 @@ "reference": "https://spdx.org/licenses/Qhull.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Qhull.json", - "referenceNumber": 326, + "referenceNumber": 123, "name": "Qhull License", "licenseId": "Qhull", "seeAlso": [ @@ -6818,7 +6864,7 @@ "reference": "https://spdx.org/licenses/QPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/QPL-1.0.json", - "referenceNumber": 413, + "referenceNumber": 329, "name": "Q Public License 1.0", "licenseId": "QPL-1.0", "seeAlso": [ @@ -6833,7 +6879,7 @@ "reference": "https://spdx.org/licenses/QPL-1.0-INRIA-2004.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/QPL-1.0-INRIA-2004.json", - "referenceNumber": 486, + "referenceNumber": 479, "name": "Q Public License 1.0 - INRIA 2004 variant", "licenseId": "QPL-1.0-INRIA-2004", "seeAlso": [ @@ -6845,7 +6891,7 @@ "reference": "https://spdx.org/licenses/radvd.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/radvd.json", - "referenceNumber": 433, + "referenceNumber": 182, "name": "radvd License", "licenseId": "radvd", "seeAlso": [ @@ -6857,7 +6903,7 @@ "reference": "https://spdx.org/licenses/Rdisc.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Rdisc.json", - "referenceNumber": 50, + "referenceNumber": 101, "name": "Rdisc License", "licenseId": "Rdisc", "seeAlso": [ @@ -6869,7 +6915,7 @@ "reference": "https://spdx.org/licenses/RHeCos-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/RHeCos-1.1.json", - "referenceNumber": 99, + "referenceNumber": 373, "name": "Red Hat eCos Public License v1.1", "licenseId": "RHeCos-1.1", "seeAlso": [ @@ -6882,7 +6928,7 @@ "reference": "https://spdx.org/licenses/RPL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/RPL-1.1.json", - "referenceNumber": 205, + "referenceNumber": 369, "name": "Reciprocal Public License 1.1", "licenseId": "RPL-1.1", "seeAlso": [ @@ -6894,7 +6940,7 @@ "reference": "https://spdx.org/licenses/RPL-1.5.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/RPL-1.5.json", - "referenceNumber": 52, + "referenceNumber": 102, "name": "Reciprocal Public License 1.5", "licenseId": "RPL-1.5", "seeAlso": [ @@ -6906,7 +6952,7 @@ "reference": "https://spdx.org/licenses/RPSL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/RPSL-1.0.json", - "referenceNumber": 637, + "referenceNumber": 663, "name": "RealNetworks Public Source License v1.0", "licenseId": "RPSL-1.0", "seeAlso": [ @@ -6920,7 +6966,7 @@ "reference": "https://spdx.org/licenses/RSA-MD.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/RSA-MD.json", - "referenceNumber": 496, + "referenceNumber": 139, "name": "RSA Message-Digest License", "licenseId": "RSA-MD", "seeAlso": [ @@ -6932,7 +6978,7 @@ "reference": "https://spdx.org/licenses/RSCPL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/RSCPL.json", - "referenceNumber": 235, + "referenceNumber": 405, "name": "Ricoh Source Code Public License", "licenseId": "RSCPL", "seeAlso": [ @@ -6945,7 +6991,7 @@ "reference": "https://spdx.org/licenses/Ruby.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Ruby.json", - "referenceNumber": 223, + "referenceNumber": 192, "name": "Ruby License", "licenseId": "Ruby", "seeAlso": [ @@ -6954,11 +7000,25 @@ "isOsiApproved": false, "isFsfLibre": true }, + { + "reference": "https://spdx.org/licenses/Ruby-pty.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Ruby-pty.json", + "referenceNumber": 473, + "name": "Ruby pty extension license", + "licenseId": "Ruby-pty", + "seeAlso": [ + "https://github.com/ruby/ruby/blob/9f6deaa6888a423720b4b127b5314f0ad26cc2e6/ext/pty/pty.c#L775-L786", + "https://github.com/ruby/ruby/commit/0a64817fb80016030c03518fb9459f63c11605ea#diff-ef5fa30838d6d0cecad9e675cc50b24628cfe2cb277c346053fafcc36c91c204", + "https://github.com/ruby/ruby/commit/0a64817fb80016030c03518fb9459f63c11605ea#diff-fedf217c1ce44bda01f0a678d3ff8b198bed478754d699c527a698ad933979a0" + ], + "isOsiApproved": false + }, { "reference": "https://spdx.org/licenses/SAX-PD.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SAX-PD.json", - "referenceNumber": 301, + "referenceNumber": 205, "name": "Sax Public Domain Notice", "licenseId": "SAX-PD", "seeAlso": [ @@ -6970,7 +7030,7 @@ "reference": "https://spdx.org/licenses/SAX-PD-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SAX-PD-2.0.json", - "referenceNumber": 561, + "referenceNumber": 209, "name": "Sax Public Domain Notice 2.0", "licenseId": "SAX-PD-2.0", "seeAlso": [ @@ -6982,7 +7042,7 @@ "reference": "https://spdx.org/licenses/Saxpath.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Saxpath.json", - "referenceNumber": 109, + "referenceNumber": 67, "name": "Saxpath License", "licenseId": "Saxpath", "seeAlso": [ @@ -6994,7 +7054,7 @@ "reference": "https://spdx.org/licenses/SCEA.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SCEA.json", - "referenceNumber": 35, + "referenceNumber": 617, "name": "SCEA Shared Source License", "licenseId": "SCEA", "seeAlso": [ @@ -7006,7 +7066,7 @@ "reference": "https://spdx.org/licenses/SchemeReport.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SchemeReport.json", - "referenceNumber": 425, + "referenceNumber": 472, "name": "Scheme Language Report License", "licenseId": "SchemeReport", "seeAlso": [], @@ -7016,7 +7076,7 @@ "reference": "https://spdx.org/licenses/Sendmail.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Sendmail.json", - "referenceNumber": 274, + "referenceNumber": 135, "name": "Sendmail License", "licenseId": "Sendmail", "seeAlso": [ @@ -7029,7 +7089,7 @@ "reference": "https://spdx.org/licenses/Sendmail-8.23.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Sendmail-8.23.json", - "referenceNumber": 247, + "referenceNumber": 226, "name": "Sendmail License 8.23", "licenseId": "Sendmail-8.23", "seeAlso": [ @@ -7042,7 +7102,7 @@ "reference": "https://spdx.org/licenses/SGI-B-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SGI-B-1.0.json", - "referenceNumber": 476, + "referenceNumber": 631, "name": "SGI Free Software License B v1.0", "licenseId": "SGI-B-1.0", "seeAlso": [ @@ -7054,7 +7114,7 @@ "reference": "https://spdx.org/licenses/SGI-B-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SGI-B-1.1.json", - "referenceNumber": 456, + "referenceNumber": 48, "name": "SGI Free Software License B v1.1", "licenseId": "SGI-B-1.1", "seeAlso": [ @@ -7066,7 +7126,7 @@ "reference": "https://spdx.org/licenses/SGI-B-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SGI-B-2.0.json", - "referenceNumber": 405, + "referenceNumber": 193, "name": "SGI Free Software License B v2.0", "licenseId": "SGI-B-2.0", "seeAlso": [ @@ -7079,7 +7139,7 @@ "reference": "https://spdx.org/licenses/SGI-OpenGL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SGI-OpenGL.json", - "referenceNumber": 629, + "referenceNumber": 565, "name": "SGI OpenGL License", "licenseId": "SGI-OpenGL", "seeAlso": [ @@ -7091,7 +7151,7 @@ "reference": "https://spdx.org/licenses/SGP4.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SGP4.json", - "referenceNumber": 336, + "referenceNumber": 291, "name": "SGP4 Permission Notice", "licenseId": "SGP4", "seeAlso": [ @@ -7103,7 +7163,7 @@ "reference": "https://spdx.org/licenses/SHL-0.5.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SHL-0.5.json", - "referenceNumber": 338, + "referenceNumber": 623, "name": "Solderpad Hardware License v0.5", "licenseId": "SHL-0.5", "seeAlso": [ @@ -7115,7 +7175,7 @@ "reference": "https://spdx.org/licenses/SHL-0.51.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SHL-0.51.json", - "referenceNumber": 29, + "referenceNumber": 34, "name": "Solderpad Hardware License, Version 0.51", "licenseId": "SHL-0.51", "seeAlso": [ @@ -7127,7 +7187,7 @@ "reference": "https://spdx.org/licenses/SimPL-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SimPL-2.0.json", - "referenceNumber": 444, + "referenceNumber": 630, "name": "Simple Public License 2.0", "licenseId": "SimPL-2.0", "seeAlso": [ @@ -7139,7 +7199,7 @@ "reference": "https://spdx.org/licenses/SISSL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SISSL.json", - "referenceNumber": 268, + "referenceNumber": 655, "name": "Sun Industry Standards Source License v1.1", "licenseId": "SISSL", "seeAlso": [ @@ -7153,7 +7213,7 @@ "reference": "https://spdx.org/licenses/SISSL-1.2.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SISSL-1.2.json", - "referenceNumber": 502, + "referenceNumber": 401, "name": "Sun Industry Standards Source License v1.2", "licenseId": "SISSL-1.2", "seeAlso": [ @@ -7165,7 +7225,7 @@ "reference": "https://spdx.org/licenses/SL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SL.json", - "referenceNumber": 645, + "referenceNumber": 632, "name": "SL License", "licenseId": "SL", "seeAlso": [ @@ -7177,7 +7237,7 @@ "reference": "https://spdx.org/licenses/Sleepycat.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Sleepycat.json", - "referenceNumber": 182, + "referenceNumber": 283, "name": "Sleepycat License", "licenseId": "Sleepycat", "seeAlso": [ @@ -7190,7 +7250,7 @@ "reference": "https://spdx.org/licenses/SMLNJ.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SMLNJ.json", - "referenceNumber": 148, + "referenceNumber": 413, "name": "Standard ML of New Jersey License", "licenseId": "SMLNJ", "seeAlso": [ @@ -7203,7 +7263,7 @@ "reference": "https://spdx.org/licenses/SMPPL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SMPPL.json", - "referenceNumber": 250, + "referenceNumber": 321, "name": "Secure Messaging Protocol Public License", "licenseId": "SMPPL", "seeAlso": [ @@ -7215,7 +7275,7 @@ "reference": "https://spdx.org/licenses/SNIA.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SNIA.json", - "referenceNumber": 518, + "referenceNumber": 41, "name": "SNIA Public License 1.1", "licenseId": "SNIA", "seeAlso": [ @@ -7227,7 +7287,7 @@ "reference": "https://spdx.org/licenses/snprintf.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/snprintf.json", - "referenceNumber": 161, + "referenceNumber": 507, "name": "snprintf License", "licenseId": "snprintf", "seeAlso": [ @@ -7239,7 +7299,7 @@ "reference": "https://spdx.org/licenses/softSurfer.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/softSurfer.json", - "referenceNumber": 523, + "referenceNumber": 496, "name": "softSurfer License", "licenseId": "softSurfer", "seeAlso": [ @@ -7252,7 +7312,7 @@ "reference": "https://spdx.org/licenses/Soundex.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Soundex.json", - "referenceNumber": 368, + "referenceNumber": 1, "name": "Soundex License", "licenseId": "Soundex", "seeAlso": [ @@ -7264,7 +7324,7 @@ "reference": "https://spdx.org/licenses/Spencer-86.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Spencer-86.json", - "referenceNumber": 472, + "referenceNumber": 257, "name": "Spencer License 86", "licenseId": "Spencer-86", "seeAlso": [ @@ -7276,7 +7336,7 @@ "reference": "https://spdx.org/licenses/Spencer-94.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Spencer-94.json", - "referenceNumber": 378, + "referenceNumber": 228, "name": "Spencer License 94", "licenseId": "Spencer-94", "seeAlso": [ @@ -7289,7 +7349,7 @@ "reference": "https://spdx.org/licenses/Spencer-99.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Spencer-99.json", - "referenceNumber": 139, + "referenceNumber": 287, "name": "Spencer License 99", "licenseId": "Spencer-99", "seeAlso": [ @@ -7301,7 +7361,7 @@ "reference": "https://spdx.org/licenses/SPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SPL-1.0.json", - "referenceNumber": 280, + "referenceNumber": 576, "name": "Sun Public License v1.0", "licenseId": "SPL-1.0", "seeAlso": [ @@ -7314,7 +7374,7 @@ "reference": "https://spdx.org/licenses/ssh-keyscan.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ssh-keyscan.json", - "referenceNumber": 294, + "referenceNumber": 78, "name": "ssh-keyscan License", "licenseId": "ssh-keyscan", "seeAlso": [ @@ -7326,7 +7386,7 @@ "reference": "https://spdx.org/licenses/SSH-OpenSSH.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SSH-OpenSSH.json", - "referenceNumber": 506, + "referenceNumber": 536, "name": "SSH OpenSSH license", "licenseId": "SSH-OpenSSH", "seeAlso": [ @@ -7338,7 +7398,7 @@ "reference": "https://spdx.org/licenses/SSH-short.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SSH-short.json", - "referenceNumber": 563, + "referenceNumber": 438, "name": "SSH short notice", "licenseId": "SSH-short", "seeAlso": [ @@ -7352,7 +7412,7 @@ "reference": "https://spdx.org/licenses/SSLeay-standalone.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SSLeay-standalone.json", - "referenceNumber": 591, + "referenceNumber": 421, "name": "SSLeay License - standalone", "licenseId": "SSLeay-standalone", "seeAlso": [ @@ -7364,7 +7424,7 @@ "reference": "https://spdx.org/licenses/SSPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SSPL-1.0.json", - "referenceNumber": 17, + "referenceNumber": 295, "name": "Server Side Public License, v 1", "licenseId": "SSPL-1.0", "seeAlso": [ @@ -7376,7 +7436,7 @@ "reference": "https://spdx.org/licenses/StandardML-NJ.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/StandardML-NJ.json", - "referenceNumber": 658, + "referenceNumber": 201, "name": "Standard ML of New Jersey License", "licenseId": "StandardML-NJ", "seeAlso": [ @@ -7389,7 +7449,7 @@ "reference": "https://spdx.org/licenses/SugarCRM-1.1.3.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SugarCRM-1.1.3.json", - "referenceNumber": 42, + "referenceNumber": 11, "name": "SugarCRM Public License v1.1.3", "licenseId": "SugarCRM-1.1.3", "seeAlso": [ @@ -7401,7 +7461,7 @@ "reference": "https://spdx.org/licenses/Sun-PPP.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Sun-PPP.json", - "referenceNumber": 385, + "referenceNumber": 313, "name": "Sun PPP License", "licenseId": "Sun-PPP", "seeAlso": [ @@ -7413,7 +7473,7 @@ "reference": "https://spdx.org/licenses/Sun-PPP-2000.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Sun-PPP-2000.json", - "referenceNumber": 310, + "referenceNumber": 489, "name": "Sun PPP License (2000)", "licenseId": "Sun-PPP-2000", "seeAlso": [ @@ -7425,7 +7485,7 @@ "reference": "https://spdx.org/licenses/SunPro.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SunPro.json", - "referenceNumber": 57, + "referenceNumber": 440, "name": "SunPro License", "licenseId": "SunPro", "seeAlso": [ @@ -7438,7 +7498,7 @@ "reference": "https://spdx.org/licenses/SWL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/SWL.json", - "referenceNumber": 649, + "referenceNumber": 331, "name": "Scheme Widget Library (SWL) Software License Agreement", "licenseId": "SWL", "seeAlso": [ @@ -7450,7 +7510,7 @@ "reference": "https://spdx.org/licenses/swrule.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/swrule.json", - "referenceNumber": 90, + "referenceNumber": 206, "name": "swrule License", "licenseId": "swrule", "seeAlso": [ @@ -7462,7 +7522,7 @@ "reference": "https://spdx.org/licenses/Symlinks.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Symlinks.json", - "referenceNumber": 414, + "referenceNumber": 136, "name": "Symlinks License", "licenseId": "Symlinks", "seeAlso": [ @@ -7474,7 +7534,7 @@ "reference": "https://spdx.org/licenses/TAPR-OHL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TAPR-OHL-1.0.json", - "referenceNumber": 242, + "referenceNumber": 317, "name": "TAPR Open Hardware License v1.0", "licenseId": "TAPR-OHL-1.0", "seeAlso": [ @@ -7486,7 +7546,7 @@ "reference": "https://spdx.org/licenses/TCL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TCL.json", - "referenceNumber": 2, + "referenceNumber": 644, "name": "TCL/TK License", "licenseId": "TCL", "seeAlso": [ @@ -7499,7 +7559,7 @@ "reference": "https://spdx.org/licenses/TCP-wrappers.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TCP-wrappers.json", - "referenceNumber": 9, + "referenceNumber": 245, "name": "TCP Wrappers License", "licenseId": "TCP-wrappers", "seeAlso": [ @@ -7511,7 +7571,7 @@ "reference": "https://spdx.org/licenses/TermReadKey.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TermReadKey.json", - "referenceNumber": 256, + "referenceNumber": 37, "name": "TermReadKey License", "licenseId": "TermReadKey", "seeAlso": [ @@ -7523,7 +7583,7 @@ "reference": "https://spdx.org/licenses/TGPPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TGPPL-1.0.json", - "referenceNumber": 101, + "referenceNumber": 112, "name": "Transitive Grace Period Public Licence 1.0", "licenseId": "TGPPL-1.0", "seeAlso": [ @@ -7536,7 +7596,7 @@ "reference": "https://spdx.org/licenses/threeparttable.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/threeparttable.json", - "referenceNumber": 398, + "referenceNumber": 319, "name": "threeparttable License", "licenseId": "threeparttable", "seeAlso": [ @@ -7548,7 +7608,7 @@ "reference": "https://spdx.org/licenses/TMate.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TMate.json", - "referenceNumber": 539, + "referenceNumber": 509, "name": "TMate Open Source License", "licenseId": "TMate", "seeAlso": [ @@ -7560,7 +7620,7 @@ "reference": "https://spdx.org/licenses/TORQUE-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TORQUE-1.1.json", - "referenceNumber": 61, + "referenceNumber": 105, "name": "TORQUE v2.5+ Software License v1.1", "licenseId": "TORQUE-1.1", "seeAlso": [ @@ -7572,7 +7632,7 @@ "reference": "https://spdx.org/licenses/TOSL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TOSL.json", - "referenceNumber": 267, + "referenceNumber": 107, "name": "Trusster Open Source License", "licenseId": "TOSL", "seeAlso": [ @@ -7584,7 +7644,7 @@ "reference": "https://spdx.org/licenses/TPDL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TPDL.json", - "referenceNumber": 75, + "referenceNumber": 124, "name": "Time::ParseDate License", "licenseId": "TPDL", "seeAlso": [ @@ -7596,7 +7656,7 @@ "reference": "https://spdx.org/licenses/TPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TPL-1.0.json", - "referenceNumber": 508, + "referenceNumber": 490, "name": "THOR Public License 1.0", "licenseId": "TPL-1.0", "seeAlso": [ @@ -7608,7 +7668,7 @@ "reference": "https://spdx.org/licenses/TTWL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TTWL.json", - "referenceNumber": 87, + "referenceNumber": 35, "name": "Text-Tabs+Wrap License", "licenseId": "TTWL", "seeAlso": [ @@ -7621,7 +7681,7 @@ "reference": "https://spdx.org/licenses/TTYP0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TTYP0.json", - "referenceNumber": 451, + "referenceNumber": 542, "name": "TTYP0 License", "licenseId": "TTYP0", "seeAlso": [ @@ -7633,7 +7693,7 @@ "reference": "https://spdx.org/licenses/TU-Berlin-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TU-Berlin-1.0.json", - "referenceNumber": 159, + "referenceNumber": 372, "name": "Technische Universitaet Berlin License 1.0", "licenseId": "TU-Berlin-1.0", "seeAlso": [ @@ -7645,7 +7705,7 @@ "reference": "https://spdx.org/licenses/TU-Berlin-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/TU-Berlin-2.0.json", - "referenceNumber": 624, + "referenceNumber": 246, "name": "Technische Universitaet Berlin License 2.0", "licenseId": "TU-Berlin-2.0", "seeAlso": [ @@ -7653,11 +7713,24 @@ ], "isOsiApproved": false }, + { + "reference": "https://spdx.org/licenses/Ubuntu-font-1.0.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/Ubuntu-font-1.0.json", + "referenceNumber": 191, + "name": "Ubuntu Font Licence v1.0", + "licenseId": "Ubuntu-font-1.0", + "seeAlso": [ + "https://ubuntu.com/legal/font-licence", + "https://assets.ubuntu.com/v1/81e5605d-ubuntu-font-licence-1.0.txt" + ], + "isOsiApproved": false + }, { "reference": "https://spdx.org/licenses/UCAR.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/UCAR.json", - "referenceNumber": 78, + "referenceNumber": 452, "name": "UCAR License", "licenseId": "UCAR", "seeAlso": [ @@ -7669,7 +7742,7 @@ "reference": "https://spdx.org/licenses/UCL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/UCL-1.0.json", - "referenceNumber": 646, + "referenceNumber": 550, "name": "Upstream Compatibility License v1.0", "licenseId": "UCL-1.0", "seeAlso": [ @@ -7681,7 +7754,7 @@ "reference": "https://spdx.org/licenses/ulem.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ulem.json", - "referenceNumber": 566, + "referenceNumber": 399, "name": "ulem License", "licenseId": "ulem", "seeAlso": [ @@ -7693,7 +7766,7 @@ "reference": "https://spdx.org/licenses/UMich-Merit.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/UMich-Merit.json", - "referenceNumber": 505, + "referenceNumber": 581, "name": "Michigan/Merit Networks License", "licenseId": "UMich-Merit", "seeAlso": [ @@ -7705,7 +7778,7 @@ "reference": "https://spdx.org/licenses/Unicode-3.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Unicode-3.0.json", - "referenceNumber": 46, + "referenceNumber": 262, "name": "Unicode License v3", "licenseId": "Unicode-3.0", "seeAlso": [ @@ -7717,7 +7790,7 @@ "reference": "https://spdx.org/licenses/Unicode-DFS-2015.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Unicode-DFS-2015.json", - "referenceNumber": 647, + "referenceNumber": 328, "name": "Unicode License Agreement - Data Files and Software (2015)", "licenseId": "Unicode-DFS-2015", "seeAlso": [ @@ -7729,7 +7802,7 @@ "reference": "https://spdx.org/licenses/Unicode-DFS-2016.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Unicode-DFS-2016.json", - "referenceNumber": 152, + "referenceNumber": 484, "name": "Unicode License Agreement - Data Files and Software (2016)", "licenseId": "Unicode-DFS-2016", "seeAlso": [ @@ -7743,7 +7816,7 @@ "reference": "https://spdx.org/licenses/Unicode-TOU.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Unicode-TOU.json", - "referenceNumber": 606, + "referenceNumber": 628, "name": "Unicode Terms of Use", "licenseId": "Unicode-TOU", "seeAlso": [ @@ -7756,7 +7829,7 @@ "reference": "https://spdx.org/licenses/UnixCrypt.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/UnixCrypt.json", - "referenceNumber": 462, + "referenceNumber": 95, "name": "UnixCrypt License", "licenseId": "UnixCrypt", "seeAlso": [ @@ -7770,7 +7843,7 @@ "reference": "https://spdx.org/licenses/Unlicense.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Unlicense.json", - "referenceNumber": 411, + "referenceNumber": 218, "name": "The Unlicense", "licenseId": "Unlicense", "seeAlso": [ @@ -7783,7 +7856,7 @@ "reference": "https://spdx.org/licenses/UPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/UPL-1.0.json", - "referenceNumber": 511, + "referenceNumber": 5, "name": "Universal Permissive License v1.0", "licenseId": "UPL-1.0", "seeAlso": [ @@ -7796,7 +7869,7 @@ "reference": "https://spdx.org/licenses/URT-RLE.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/URT-RLE.json", - "referenceNumber": 443, + "referenceNumber": 165, "name": "Utah Raster Toolkit Run Length Encoded License", "licenseId": "URT-RLE", "seeAlso": [ @@ -7809,7 +7882,7 @@ "reference": "https://spdx.org/licenses/Vim.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Vim.json", - "referenceNumber": 371, + "referenceNumber": 549, "name": "Vim License", "licenseId": "Vim", "seeAlso": [ @@ -7822,7 +7895,7 @@ "reference": "https://spdx.org/licenses/VOSTROM.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/VOSTROM.json", - "referenceNumber": 122, + "referenceNumber": 544, "name": "VOSTROM Public License for Open Source", "licenseId": "VOSTROM", "seeAlso": [ @@ -7834,7 +7907,7 @@ "reference": "https://spdx.org/licenses/VSL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/VSL-1.0.json", - "referenceNumber": 510, + "referenceNumber": 109, "name": "Vovida Software License v1.0", "licenseId": "VSL-1.0", "seeAlso": [ @@ -7846,7 +7919,7 @@ "reference": "https://spdx.org/licenses/W3C.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/W3C.json", - "referenceNumber": 284, + "referenceNumber": 28, "name": "W3C Software Notice and License (2002-12-31)", "licenseId": "W3C", "seeAlso": [ @@ -7860,7 +7933,7 @@ "reference": "https://spdx.org/licenses/W3C-19980720.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/W3C-19980720.json", - "referenceNumber": 156, + "referenceNumber": 629, "name": "W3C Software Notice and License (1998-07-20)", "licenseId": "W3C-19980720", "seeAlso": [ @@ -7872,7 +7945,7 @@ "reference": "https://spdx.org/licenses/W3C-20150513.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/W3C-20150513.json", - "referenceNumber": 452, + "referenceNumber": 315, "name": "W3C Software Notice and Document License (2015-05-13)", "licenseId": "W3C-20150513", "seeAlso": [ @@ -7880,13 +7953,13 @@ "https://www.w3.org/copyright/software-license-2015/", "https://www.w3.org/copyright/software-license-2023/" ], - "isOsiApproved": false + "isOsiApproved": true }, { "reference": "https://spdx.org/licenses/w3m.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/w3m.json", - "referenceNumber": 202, + "referenceNumber": 379, "name": "w3m License", "licenseId": "w3m", "seeAlso": [ @@ -7898,7 +7971,7 @@ "reference": "https://spdx.org/licenses/Watcom-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Watcom-1.0.json", - "referenceNumber": 533, + "referenceNumber": 612, "name": "Sybase Open Watcom Public License 1.0", "licenseId": "Watcom-1.0", "seeAlso": [ @@ -7911,7 +7984,7 @@ "reference": "https://spdx.org/licenses/Widget-Workshop.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Widget-Workshop.json", - "referenceNumber": 548, + "referenceNumber": 256, "name": "Widget Workshop License", "licenseId": "Widget-Workshop", "seeAlso": [ @@ -7923,7 +7996,7 @@ "reference": "https://spdx.org/licenses/Wsuipa.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Wsuipa.json", - "referenceNumber": 305, + "referenceNumber": 199, "name": "Wsuipa License", "licenseId": "Wsuipa", "seeAlso": [ @@ -7935,7 +8008,7 @@ "reference": "https://spdx.org/licenses/WTFPL.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/WTFPL.json", - "referenceNumber": 176, + "referenceNumber": 173, "name": "Do What The F*ck You Want To Public License", "licenseId": "WTFPL", "seeAlso": [ @@ -7949,7 +8022,7 @@ "reference": "https://spdx.org/licenses/wxWindows.html", "isDeprecatedLicenseId": true, "detailsUrl": "https://spdx.org/licenses/wxWindows.json", - "referenceNumber": 258, + "referenceNumber": 350, "name": "wxWindows Library License", "licenseId": "wxWindows", "seeAlso": [ @@ -7961,7 +8034,7 @@ "reference": "https://spdx.org/licenses/X11.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/X11.json", - "referenceNumber": 203, + "referenceNumber": 274, "name": "X11 License", "licenseId": "X11", "seeAlso": [ @@ -7974,7 +8047,7 @@ "reference": "https://spdx.org/licenses/X11-distribute-modifications-variant.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/X11-distribute-modifications-variant.json", - "referenceNumber": 112, + "referenceNumber": 286, "name": "X11 License Distribution Modification Variant", "licenseId": "X11-distribute-modifications-variant", "seeAlso": [ @@ -7982,11 +8055,23 @@ ], "isOsiApproved": false }, + { + "reference": "https://spdx.org/licenses/X11-swapped.html", + "isDeprecatedLicenseId": false, + "detailsUrl": "https://spdx.org/licenses/X11-swapped.json", + "referenceNumber": 7, + "name": "X11 swapped final paragraphs", + "licenseId": "X11-swapped", + "seeAlso": [ + "https://github.com/fedeinthemix/chez-srfi/blob/master/srfi/LICENSE" + ], + "isOsiApproved": false + }, { "reference": "https://spdx.org/licenses/Xdebug-1.03.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Xdebug-1.03.json", - "referenceNumber": 10, + "referenceNumber": 471, "name": "Xdebug License v 1.03", "licenseId": "Xdebug-1.03", "seeAlso": [ @@ -7998,7 +8083,7 @@ "reference": "https://spdx.org/licenses/Xerox.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Xerox.json", - "referenceNumber": 595, + "referenceNumber": 417, "name": "Xerox License", "licenseId": "Xerox", "seeAlso": [ @@ -8010,7 +8095,7 @@ "reference": "https://spdx.org/licenses/Xfig.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Xfig.json", - "referenceNumber": 89, + "referenceNumber": 63, "name": "Xfig License", "licenseId": "Xfig", "seeAlso": [ @@ -8024,7 +8109,7 @@ "reference": "https://spdx.org/licenses/XFree86-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/XFree86-1.1.json", - "referenceNumber": 562, + "referenceNumber": 311, "name": "XFree86 License 1.1", "licenseId": "XFree86-1.1", "seeAlso": [ @@ -8037,7 +8122,7 @@ "reference": "https://spdx.org/licenses/xinetd.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/xinetd.json", - "referenceNumber": 465, + "referenceNumber": 406, "name": "xinetd License", "licenseId": "xinetd", "seeAlso": [ @@ -8050,7 +8135,7 @@ "reference": "https://spdx.org/licenses/xkeyboard-config-Zinoviev.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/xkeyboard-config-Zinoviev.json", - "referenceNumber": 140, + "referenceNumber": 55, "name": "xkeyboard-config Zinoviev License", "licenseId": "xkeyboard-config-Zinoviev", "seeAlso": [ @@ -8062,7 +8147,7 @@ "reference": "https://spdx.org/licenses/xlock.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/xlock.json", - "referenceNumber": 357, + "referenceNumber": 140, "name": "xlock License", "licenseId": "xlock", "seeAlso": [ @@ -8074,7 +8159,7 @@ "reference": "https://spdx.org/licenses/Xnet.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Xnet.json", - "referenceNumber": 236, + "referenceNumber": 639, "name": "X.Net License", "licenseId": "Xnet", "seeAlso": [ @@ -8086,7 +8171,7 @@ "reference": "https://spdx.org/licenses/xpp.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/xpp.json", - "referenceNumber": 312, + "referenceNumber": 243, "name": "XPP License", "licenseId": "xpp", "seeAlso": [ @@ -8098,7 +8183,7 @@ "reference": "https://spdx.org/licenses/XSkat.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/XSkat.json", - "referenceNumber": 544, + "referenceNumber": 535, "name": "XSkat License", "licenseId": "XSkat", "seeAlso": [ @@ -8110,7 +8195,7 @@ "reference": "https://spdx.org/licenses/xzoom.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/xzoom.json", - "referenceNumber": 530, + "referenceNumber": 339, "name": "xzoom License", "licenseId": "xzoom", "seeAlso": [ @@ -8122,7 +8207,7 @@ "reference": "https://spdx.org/licenses/YPL-1.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/YPL-1.0.json", - "referenceNumber": 491, + "referenceNumber": 506, "name": "Yahoo! Public License v1.0", "licenseId": "YPL-1.0", "seeAlso": [ @@ -8134,7 +8219,7 @@ "reference": "https://spdx.org/licenses/YPL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/YPL-1.1.json", - "referenceNumber": 473, + "referenceNumber": 538, "name": "Yahoo! Public License v1.1", "licenseId": "YPL-1.1", "seeAlso": [ @@ -8147,7 +8232,7 @@ "reference": "https://spdx.org/licenses/Zed.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Zed.json", - "referenceNumber": 599, + "referenceNumber": 500, "name": "Zed License", "licenseId": "Zed", "seeAlso": [ @@ -8159,7 +8244,7 @@ "reference": "https://spdx.org/licenses/Zeeff.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Zeeff.json", - "referenceNumber": 218, + "referenceNumber": 382, "name": "Zeeff License", "licenseId": "Zeeff", "seeAlso": [ @@ -8171,7 +8256,7 @@ "reference": "https://spdx.org/licenses/Zend-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Zend-2.0.json", - "referenceNumber": 481, + "referenceNumber": 51, "name": "Zend License v2.0", "licenseId": "Zend-2.0", "seeAlso": [ @@ -8184,7 +8269,7 @@ "reference": "https://spdx.org/licenses/Zimbra-1.3.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Zimbra-1.3.json", - "referenceNumber": 379, + "referenceNumber": 555, "name": "Zimbra Public License v1.3", "licenseId": "Zimbra-1.3", "seeAlso": [ @@ -8197,7 +8282,7 @@ "reference": "https://spdx.org/licenses/Zimbra-1.4.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Zimbra-1.4.json", - "referenceNumber": 304, + "referenceNumber": 227, "name": "Zimbra Public License v1.4", "licenseId": "Zimbra-1.4", "seeAlso": [ @@ -8209,7 +8294,7 @@ "reference": "https://spdx.org/licenses/Zlib.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/Zlib.json", - "referenceNumber": 209, + "referenceNumber": 74, "name": "zlib License", "licenseId": "Zlib", "seeAlso": [ @@ -8223,7 +8308,7 @@ "reference": "https://spdx.org/licenses/zlib-acknowledgement.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/zlib-acknowledgement.json", - "referenceNumber": 348, + "referenceNumber": 371, "name": "zlib/libpng License with Acknowledgement", "licenseId": "zlib-acknowledgement", "seeAlso": [ @@ -8235,7 +8320,7 @@ "reference": "https://spdx.org/licenses/ZPL-1.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ZPL-1.1.json", - "referenceNumber": 545, + "referenceNumber": 598, "name": "Zope Public License 1.1", "licenseId": "ZPL-1.1", "seeAlso": [ @@ -8247,7 +8332,7 @@ "reference": "https://spdx.org/licenses/ZPL-2.0.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ZPL-2.0.json", - "referenceNumber": 51, + "referenceNumber": 539, "name": "Zope Public License 2.0", "licenseId": "ZPL-2.0", "seeAlso": [ @@ -8261,7 +8346,7 @@ "reference": "https://spdx.org/licenses/ZPL-2.1.html", "isDeprecatedLicenseId": false, "detailsUrl": "https://spdx.org/licenses/ZPL-2.1.json", - "referenceNumber": 352, + "referenceNumber": 638, "name": "Zope Public License 2.1", "licenseId": "ZPL-2.1", "seeAlso": [ @@ -8271,5 +8356,5 @@ "isFsfLibre": true } ], - "releaseDate": "2024-05-22" + "releaseDate": "2024-08-19" } \ No newline at end of file From b503861cd570d36bee9a5d428e46739bdce9eff7 Mon Sep 17 00:00:00 2001 From: Ross Murphy Date: Fri, 13 Sep 2024 16:59:47 +0100 Subject: [PATCH 150/429] update total license number in tests Signed-off-by: Ross Murphy --- .../persistence/DefaultObjectGeneratorTest.java | 2 +- .../org/dependencytrack/resources/v1/LicenseResourceTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/dependencytrack/persistence/DefaultObjectGeneratorTest.java b/src/test/java/org/dependencytrack/persistence/DefaultObjectGeneratorTest.java index 410657f821..decd918c30 100644 --- a/src/test/java/org/dependencytrack/persistence/DefaultObjectGeneratorTest.java +++ b/src/test/java/org/dependencytrack/persistence/DefaultObjectGeneratorTest.java @@ -48,7 +48,7 @@ public void testLoadDefaultLicenses() throws Exception { Method method = generator.getClass().getDeclaredMethod("loadDefaultLicenses"); method.setAccessible(true); method.invoke(generator); - Assert.assertEquals(729, qm.getAllLicensesConcise().size()); + Assert.assertEquals(738, qm.getAllLicensesConcise().size()); } @Test diff --git a/src/test/java/org/dependencytrack/resources/v1/LicenseResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/LicenseResourceTest.java index c5a3c5864a..9a82e15804 100644 --- a/src/test/java/org/dependencytrack/resources/v1/LicenseResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/LicenseResourceTest.java @@ -58,7 +58,7 @@ public void getLicensesTest() { .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(200, response.getStatus(), 0); - Assert.assertEquals(String.valueOf(729), response.getHeaderString(TOTAL_COUNT_HEADER)); + Assert.assertEquals(String.valueOf(738), response.getHeaderString(TOTAL_COUNT_HEADER)); JsonArray json = parseJsonArray(response); Assert.assertNotNull(json); Assert.assertEquals(100, json.size()); @@ -77,7 +77,7 @@ public void getLicensesConciseTest() { Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); JsonArray json = parseJsonArray(response); Assert.assertNotNull(json); - Assert.assertEquals(729, json.size()); + Assert.assertEquals(738, json.size()); Assert.assertNotNull(json.getJsonObject(0).getString("name")); Assert.assertNull(json.getJsonObject(0).getString("licenseText", null)); Assert.assertNull(json.getJsonObject(0).getString("licenseComments", null)); From 146b9e135fd4c5755ee596ff3bcfdad04ab68f59 Mon Sep 17 00:00:00 2001 From: nscuro Date: Fri, 13 Sep 2024 18:31:32 +0200 Subject: [PATCH 151/429] Handle empty component and service names `component.name` and `service.name` are required as per CycloneDX specification, but the schema doesn't sufficiently enforce this requirement (https://github.com/CycloneDX/specification/issues/461). Because DT trims names from the BOM during model conversion, empty or blank names end up becoming `null`. Since the respective database columns have a `NOT NULL` constraint on them, inserting or updating such components will always fail. Usually we would not want to try to "repair" data, but the name being empty appears to be so common that there's no other sensible way for us to deal with it. With this change, empty names will end up being saved as `-` instead, to signal the absence of a proper value. Fixes #2821 Signed-off-by: nscuro --- .../parser/cyclonedx/util/ModelConverter.java | 5 ++- .../tasks/BomUploadProcessingTaskTest.java | 40 ++++++++++++++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java index 2ea2bb172b..fcf75410b8 100644 --- a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java +++ b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java @@ -75,6 +75,7 @@ import java.util.function.Consumer; import java.util.function.Function; +import static java.util.Objects.requireNonNullElse; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.trim; import static org.apache.commons.lang3.StringUtils.trimToNull; @@ -167,7 +168,7 @@ public static Component convertComponent(final org.cyclonedx.model.Component cdx component.setSupplier(convert(cdxComponent.getSupplier())); component.setClassifier(convertClassifier(cdxComponent.getType()).orElse(Classifier.LIBRARY)); component.setGroup(trimToNull(cdxComponent.getGroup())); - component.setName(trimToNull(cdxComponent.getName())); + component.setName(requireNonNullElse(trimToNull(cdxComponent.getName()), "-")); component.setVersion(trimToNull(cdxComponent.getVersion())); component.setDescription(trimToNull(cdxComponent.getDescription())); component.setCopyright(trimToNull(cdxComponent.getCopyright())); @@ -325,7 +326,7 @@ public static ServiceComponent convertService(final org.cyclonedx.model.Service final var service = new ServiceComponent(); service.setBomRef(useOrGenerateRandomBomRef(cdxService.getBomRef())); service.setGroup(trimToNull(cdxService.getGroup())); - service.setName(trimToNull(cdxService.getName())); + service.setName(requireNonNullElse(trimToNull(cdxService.getName()), "-")); service.setVersion(trimToNull(cdxService.getVersion())); service.setDescription(trimToNull(cdxService.getDescription())); service.setAuthenticated(cdxService.getAuthenticated()); diff --git a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java index 03d128be9f..58147107d4 100644 --- a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java @@ -1065,6 +1065,45 @@ public void informWithLicenseResolutionByIdOrNameTest() { }); } + @Test + public void informWithEmptyComponentAndServiceNameTest() { + final var project = new Project(); + project.setName("acme-license-app"); + qm.persist(project); + + final byte[] bomBytes = """ + { + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b80", + "version": 1, + "components": [ + { + "type": "library", + "name": "" + } + ], + "services": [ + { + "name": "" + } + ] + } + """.getBytes(StandardCharsets.UTF_8); + + final var bomUploadEvent = new BomUploadEvent(qm.detach(Project.class, project.getId()), bomBytes); + new BomUploadProcessingTask().inform(bomUploadEvent); + awaitBomProcessedNotification(bomUploadEvent); + + qm.getPersistenceManager().evictAll(); + assertThat(qm.getAllComponents(project)).satisfiesExactly(component -> { + assertThat(component.getName()).isEqualTo("-"); + }); + assertThat(qm.getAllServiceComponents(project)).satisfiesExactly(service -> { + assertThat(service.getName()).isEqualTo("-"); + }); + } + @Test // https://github.com/DependencyTrack/dependency-track/issues/1905 public void informIssue1905Test() throws Exception { final var project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); @@ -1331,7 +1370,6 @@ public void informIssue3981Test() { @Test public void informIssue3936Test() throws Exception{ - final Project project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); List boms = new ArrayList<>(Arrays.asList("/unit/bom-issue3936-authors.json", "/unit/bom-issue3936-author.json", "/unit/bom-issue3936-both.json")); for(String bom : boms){ From abbeedcd6ceaa0474749d7e6b47f3fe77ad953cb Mon Sep 17 00:00:00 2001 From: nscuro Date: Fri, 13 Sep 2024 19:20:13 +0200 Subject: [PATCH 152/429] Handle existing duplicate component properties Fixes #4027 Signed-off-by: nscuro --- .../persistence/ComponentQueryManager.java | 21 ++++++ .../tasks/BomUploadProcessingTaskTest.java | 68 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java index 2f7114e606..a163599617 100644 --- a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java @@ -863,11 +863,32 @@ public void synchronizeComponentProperties(final Component component, final List // Group properties by group, name, and value. Because CycloneDX supports duplicate // property names, uniqueness can only be determined by also considering the value. + final var existingPropertyIdentitiesSeen = new HashSet(); + final var existingDuplicateProperties = new HashSet(); final var existingPropertiesByIdentity = component.getProperties().stream() + // The legacy BOM processing in <= 4.11.x allowed duplicates to be persisted. + // Collectors#toMap fails upon encounter of duplicate keys. + // Prevent existing duplicates from breaking this. + // https://github.com/DependencyTrack/dependency-track/issues/4027 + .filter(property -> { + final var identity = new ComponentProperty.Identity(property); + final boolean isUnique = existingPropertyIdentitiesSeen.add(identity); + if (!isUnique) { + existingDuplicateProperties.add(property); + } + + return isUnique; + }) .collect(Collectors.toMap(ComponentProperty.Identity::new, Function.identity())); + final var incomingPropertyIdentitiesSeen = new HashSet(); final var incomingPropertiesByIdentity = properties.stream() + .filter(property -> incomingPropertyIdentitiesSeen.add(new ComponentProperty.Identity(property))) .collect(Collectors.toMap(ComponentProperty.Identity::new, Function.identity())); + if (!existingDuplicateProperties.isEmpty()) { + pm.deletePersistentAll(existingDuplicateProperties); + } + final var propertyIdentities = new HashSet(); propertyIdentities.addAll(existingPropertiesByIdentity.keySet()); propertyIdentities.addAll(incomingPropertiesByIdentity.keySet()); diff --git a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java index 58147107d4..6384609b6a 100644 --- a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java @@ -53,6 +53,7 @@ import org.junit.Before; import org.junit.Test; +import javax.jdo.JDOObjectNotFoundException; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.ArrayList; @@ -65,6 +66,7 @@ import static org.apache.commons.io.IOUtils.resourceToByteArray; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatNoException; import static org.assertj.core.api.Assertions.fail; import static org.awaitility.Awaitility.await; @@ -979,6 +981,72 @@ public void informWithExistingComponentPropertiesAndBomWithComponentProperties() }); } + @Test + public void informWithExistingDuplicateComponentPropertiesAndBomWithDuplicateComponentProperties() { + final var project = new Project(); + project.setName("acme-app"); + qm.persist(project); + + final var component = new Component(); + component.setProject(project); + component.setName("acme-lib"); + component.setClassifier(Classifier.LIBRARY); + qm.persist(component); + + final var componentPropertyA = new ComponentProperty(); + componentPropertyA.setComponent(component); + componentPropertyA.setPropertyName("foo"); + componentPropertyA.setPropertyValue("bar"); + componentPropertyA.setPropertyType(PropertyType.STRING); + qm.persist(componentPropertyA); + + final var componentPropertyB = new ComponentProperty(); + componentPropertyB.setComponent(component); + componentPropertyB.setPropertyName("foo"); + componentPropertyB.setPropertyValue("bar"); + componentPropertyB.setPropertyType(PropertyType.STRING); + qm.persist(componentPropertyB); + + final var bomUploadEvent = new BomUploadEvent(qm.detach(Project.class, project.getId()), """ + { + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "serialNumber": "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79", + "version": 1, + "components": [ + { + "type": "library", + "name": "acme-lib", + "properties": [ + { + "name": "foo", + "value": "bar" + }, + { + "name": "foo", + "value": "bar" + } + ] + } + ] + } + """.getBytes()); + new BomUploadProcessingTask().inform(bomUploadEvent); + awaitBomProcessedNotification(bomUploadEvent); + + qm.getPersistenceManager().evictAll(); + assertThatNoException() + .isThrownBy(() -> qm.getPersistenceManager().refresh(componentPropertyA)); + assertThatExceptionOfType(JDOObjectNotFoundException.class) + .isThrownBy(() -> qm.getPersistenceManager().refresh(componentPropertyB)); + assertThat(component.getProperties()).satisfiesExactly(property -> { + assertThat(property.getGroupName()).isNull(); + assertThat(property.getPropertyName()).isEqualTo("foo"); + assertThat(property.getPropertyValue()).isEqualTo("bar"); + assertThat(property.getUuid()).isEqualTo(componentPropertyA.getUuid()); + }); + } + @Test public void informWithLicenseResolutionByNameTest() { final var license = new License(); From e5bb70e1c4ea1403431a5d3545e9248c48ef6607 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 08:21:50 +0000 Subject: [PATCH 153/429] Bump github/codeql-action from 3.26.6 to 3.26.7 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.6 to 3.26.7. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/4dd16135b69a43b6c8efb853346f8437d92d3c93...8214744c546c1e5c8f03dde8fab3a7353211988d) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index fbb146ca9b..b739b8f0f6 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -145,6 +145,6 @@ jobs: - name: Upload Trivy Scan Results to GitHub Security Tab if: ${{ inputs.publish-container }} - uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # tag=v3.26.6 + uses: github/codeql-action/upload-sarif@8214744c546c1e5c8f03dde8fab3a7353211988d # tag=v3.26.7 with: sarif_file: 'trivy-results.sarif' From 057b31dd51060346960d916882ff2406749278f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 08:21:55 +0000 Subject: [PATCH 154/429] Bump actions/setup-java from 4.2.2 to 4.3.0 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.2.2 to 4.3.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/6a0805fcefea3d4657a47ac4c165951e33482018...2dfa2011c5b2a0f1489bf9e433881c92c1631f88) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- .github/workflows/ci-release.yaml | 2 +- .github/workflows/ci-test.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index fbb146ca9b..f76c7833d2 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -31,7 +31,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 - name: Set up JDK - uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # tag=v4.2.2 + uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # tag=v4.3.0 with: distribution: 'temurin' java-version: '21' diff --git a/.github/workflows/ci-release.yaml b/.github/workflows/ci-release.yaml index 0c25acf37f..9e9401cbc4 100644 --- a/.github/workflows/ci-release.yaml +++ b/.github/workflows/ci-release.yaml @@ -54,7 +54,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 - name: Set up JDK - uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # tag=v4.2.2 + uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # tag=v4.3.0 with: distribution: 'temurin' java-version: '21' diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml index 3e7325f0cb..cbe2b73f6f 100644 --- a/.github/workflows/ci-test.yaml +++ b/.github/workflows/ci-test.yaml @@ -36,7 +36,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 - name: Set up JDK - uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # tag=v4.2.2 + uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # tag=v4.3.0 with: distribution: 'temurin' java-version: '21' From 22b091d05f0f1b31819b12cfeafc471189167563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Tue, 17 Sep 2024 13:30:51 +0200 Subject: [PATCH 155/429] Changed to language to locale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../java/org/dependencytrack/model/ConfigPropertyConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java index 77d72b3490..f36cdccf45 100644 --- a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java +++ b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java @@ -117,7 +117,7 @@ public enum ConfigPropertyConstants { BOM_VALIDATION_TAGS_EXCLUSIVE("artifact", "bom.validation.tags.exclusive", "[]", PropertyType.STRING, "JSON array of tags for which BOM validation shall NOT be performed"), WELCOME_MESSAGE("general", "welcome.message.html", "%20%3Chtml%3E%3Ch1%3EYour%20Welcome%20Message%3C%2Fh1%3E%3C%2Fhtml%3E", PropertyType.STRING, "Custom HTML Code that is displayed before login", true), IS_WELCOME_MESSAGE("general", "welcome.message.enabled", "false", PropertyType.BOOLEAN, "Bool that says wheter to show the welcome message or not", true), - DEFAULT_LANGUAGE("general", "default.language", "%20", PropertyType.STRING, "Determine the default Language to use", true); + DEFAULT_LANGUAGE("general", "default.locale", "%20", PropertyType.STRING, "Determine the default Language to use", true); private final String groupName; private final String propertyName; From 8d13510f0cd81d774721326c57c8de5c4f937cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Tue, 17 Sep 2024 15:44:31 +0200 Subject: [PATCH 156/429] Now is null, then no default locale is set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../java/org/dependencytrack/model/ConfigPropertyConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java index f36cdccf45..7515206c91 100644 --- a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java +++ b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java @@ -117,7 +117,7 @@ public enum ConfigPropertyConstants { BOM_VALIDATION_TAGS_EXCLUSIVE("artifact", "bom.validation.tags.exclusive", "[]", PropertyType.STRING, "JSON array of tags for which BOM validation shall NOT be performed"), WELCOME_MESSAGE("general", "welcome.message.html", "%20%3Chtml%3E%3Ch1%3EYour%20Welcome%20Message%3C%2Fh1%3E%3C%2Fhtml%3E", PropertyType.STRING, "Custom HTML Code that is displayed before login", true), IS_WELCOME_MESSAGE("general", "welcome.message.enabled", "false", PropertyType.BOOLEAN, "Bool that says wheter to show the welcome message or not", true), - DEFAULT_LANGUAGE("general", "default.locale", "%20", PropertyType.STRING, "Determine the default Language to use", true); + DEFAULT_LANGUAGE("general", "default.locale", null, PropertyType.STRING, "Determine the default Language to use", true); private final String groupName; private final String propertyName; From 5bd46d605b2efe632d01c928a37a16b03e48beb6 Mon Sep 17 00:00:00 2001 From: Richard Bickert Date: Tue, 17 Sep 2024 16:47:57 +0200 Subject: [PATCH 157/429] Fix merge errors Signed-off-by: Richard Bickert --- .../persistence/PolicyQueryManager.java | 3 ++- .../resources/v1/PolicyViolationResource.java | 18 +++++++------- .../v1/PolicyViolationResourceTest.java | 24 +++++++++---------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index 995dd89056..653b5aa8be 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -702,7 +702,8 @@ private void processInputFilter(Map params, List filterC } } - private void preprocessACLs(final Query query, final String inputFilter, final Map params, final boolean bypass) { + @Override + void preprocessACLs(final Query query, final String inputFilter, final Map params, final boolean bypass) { if (super.principal != null && isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED) && !bypass) { final List teams; if (super.principal instanceof UserPrincipal) { diff --git a/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java b/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java index 0e05026c08..efc4be5fb5 100644 --- a/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java @@ -86,23 +86,23 @@ public class PolicyViolationResource extends AlpineResource { @PermissionRequired(Permissions.Constants.VIEW_POLICY_VIOLATION) public Response getViolations(@Parameter(description = "Optionally includes suppressed violations") @QueryParam("suppressed") boolean suppressed, - @ApiParam(value = "Optionally includes inactive projects") + @PathParam(value = "Optionally includes inactive projects") @QueryParam("showInactive") boolean showInactive, - @ApiParam(value = "Filter by violation state") + @PathParam(value = "Filter by violation state") @QueryParam("violationState") String violationState, - @ApiParam(value = "Filter by risk type") + @PathParam(value = "Filter by risk type") @QueryParam("riskType") String riskType, - @ApiParam(value = "Filter by policy") + @PathParam(value = "Filter by policy") @QueryParam("policy") String policy, - @ApiParam(value = "Filter by analysis state") + @PathParam(value = "Filter by analysis state") @QueryParam("analysisState") String analysisState, - @ApiParam(value = "Filter occurred on from") + @PathParam(value = "Filter occurred on from") @QueryParam("occurredOnDateFrom") String occurredOnDateFrom, - @ApiParam(value = "Filter occurred on to") + @PathParam(value = "Filter occurred on to") @QueryParam("occurredOnDateTo") String occurredOnDateTo, - @ApiParam(value = "Filter the text input in these fields") + @PathParam(value = "Filter the text input in these fields") @QueryParam("textSearchField") String textSearchField, - @ApiParam(value = "Filter by this text input") + @PathParam(value = "Filter by this text input") @QueryParam("textSearchInput") String textSearchInput) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { Map filters = new HashMap<>(); diff --git a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java index 4f701631bc..8c2c130099 100644 --- a/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/PolicyViolationResourceTest.java @@ -377,7 +377,7 @@ public void getViolationsWithAclEnabledTest() { violationD.setTimestamp(new Date()); violationD = qm.persist(violationD); - final Response responseA = target(V1_POLICY_VIOLATION) + final Response responseA = jersey.target(V1_POLICY_VIOLATION) .request() .header(X_API_KEY, apiKey) .get(); @@ -393,7 +393,7 @@ public void getViolationsWithAclEnabledTest() { qm.persist(aclToggle); } - final Response responseB = target(V1_POLICY_VIOLATION) + final Response responseB = jersey.target(V1_POLICY_VIOLATION) .request() .header(X_API_KEY, team.getApiKeys().get(0).getKey()) .get(); @@ -474,7 +474,7 @@ public void getViolationsWithArrayFilter() { violationD.setTimestamp(new Date()); violationD = qm.persist(violationD); - final Response response = target(V1_POLICY_VIOLATION) + final Response response = jersey.target(V1_POLICY_VIOLATION) .request() .header(X_API_KEY, apiKey) .get(); @@ -482,7 +482,7 @@ public void getViolationsWithArrayFilter() { assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("4"); assertThat(parseJsonArray(response)).hasSize(4); - final Response responseA = target(V1_POLICY_VIOLATION).queryParam("violationState", "FAIL") + final Response responseA = jersey.target(V1_POLICY_VIOLATION).queryParam("violationState", "FAIL") .request() .header(X_API_KEY, apiKey) .get(); @@ -493,7 +493,7 @@ public void getViolationsWithArrayFilter() { assertThat(jsonArrayA.getJsonObject(0).getString("uuid")).isEqualTo(violationA.getUuid().toString()); - final Response responseB = target(V1_POLICY_VIOLATION).queryParam("riskType", "LICENSE") + final Response responseB = jersey.target(V1_POLICY_VIOLATION).queryParam("riskType", "LICENSE") .request() .header(X_API_KEY, apiKey) .get(); @@ -504,7 +504,7 @@ public void getViolationsWithArrayFilter() { assertThat(jsonArrayB.getJsonObject(0).getString("uuid")).isEqualTo(violationB.getUuid().toString()); assertThat(jsonArrayB.getJsonObject(0).getString("uuid")).isEqualTo(violationB.getUuid().toString()); - final Response responseC = target(V1_POLICY_VIOLATION).queryParam("analysisState", "REJECTED") + final Response responseC = jersey.target(V1_POLICY_VIOLATION).queryParam("analysisState", "REJECTED") .request() .header(X_API_KEY, apiKey) .get(); @@ -515,7 +515,7 @@ public void getViolationsWithArrayFilter() { assertThat(jsonArrayC.getJsonObject(0).getString("uuid")).isEqualTo(violationC.getUuid().toString()); assertThat(jsonArrayC.getJsonObject(0).getString("uuid")).isEqualTo(violationC.getUuid().toString()); - final Response responseD = target(V1_POLICY_VIOLATION).queryParam("policy", policyD.getUuid().toString()) + final Response responseD = jersey.target(V1_POLICY_VIOLATION).queryParam("policy", policyD.getUuid().toString()) .request() .header(X_API_KEY, apiKey) .get(); @@ -600,7 +600,7 @@ public void getViolationsWithInputFilter() { violationD.setTimestamp(new Date()); violationD = qm.persist(violationD); - final Response response = target(V1_POLICY_VIOLATION) + final Response response = jersey.target(V1_POLICY_VIOLATION) .request() .header(X_API_KEY, apiKey) .get(); @@ -608,7 +608,7 @@ public void getViolationsWithInputFilter() { assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isEqualTo("4"); assertThat(parseJsonArray(response)).hasSize(4); - final Response responseA = target(V1_POLICY_VIOLATION) + final Response responseA = jersey.target(V1_POLICY_VIOLATION) .queryParam("textSearchField", "policy_name") .queryParam("textSearchInput", "Policy A") .request() @@ -620,7 +620,7 @@ public void getViolationsWithInputFilter() { assertThat(jsonArrayA).hasSize(1); assertThat(jsonArrayA.getJsonObject(0).getString("uuid")).isEqualTo(violationA.getUuid().toString()); - final Response responseB = target(V1_POLICY_VIOLATION) + final Response responseB = jersey.target(V1_POLICY_VIOLATION) .queryParam("textSearchField", "component") .queryParam("textSearchInput", "Component B") .request() @@ -632,7 +632,7 @@ public void getViolationsWithInputFilter() { assertThat(jsonArrayB).hasSize(1); assertThat(jsonArrayB.getJsonObject(0).getString("uuid")).isEqualTo(violationB.getUuid().toString()); - final Response responseC = target(V1_POLICY_VIOLATION) + final Response responseC = jersey.target(V1_POLICY_VIOLATION) .queryParam("textSearchField", "license") .queryParam("textSearchInput", "License C") .request() @@ -644,7 +644,7 @@ public void getViolationsWithInputFilter() { assertThat(jsonArrayC).hasSize(1); assertThat(jsonArrayC.getJsonObject(0).getString("uuid")).isEqualTo(violationC.getUuid().toString()); - final Response responseD = target(V1_POLICY_VIOLATION) + final Response responseD = jersey.target(V1_POLICY_VIOLATION) .queryParam("textSearchField", "project_name") .queryParam("textSearchInput", "Project D") .request() From 56f453c8c442321220abc0b2bf17a01bfad4b606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Tue, 17 Sep 2024 17:30:30 +0200 Subject: [PATCH 158/429] Teams are displayed in the audit trail as well now Tests are added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../resources/v1/AnalysisResource.java | 18 +++- .../resources/v1/AnalysisResourceTest.java | 94 +++++++++++++++---- 2 files changed, 91 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/AnalysisResource.java b/src/main/java/org/dependencytrack/resources/v1/AnalysisResource.java index 6228deb3e7..701adcd024 100644 --- a/src/main/java/org/dependencytrack/resources/v1/AnalysisResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/AnalysisResource.java @@ -20,9 +20,8 @@ import alpine.common.validation.RegexSequence; import alpine.common.validation.ValidationTask; -import alpine.model.LdapUser; -import alpine.model.ManagedUser; -import alpine.model.OidcUser; +import alpine.model.ApiKey; +import alpine.model.Team; import alpine.model.UserPrincipal; import alpine.server.auth.PermissionRequired; import alpine.server.resources.AlpineResource; @@ -35,6 +34,10 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.security.SecurityRequirements; import io.swagger.v3.oas.annotations.tags.Tag; + +import java.util.ArrayList; +import java.util.List; + import org.apache.commons.lang3.StringUtils; import org.dependencytrack.auth.Permissions; import org.dependencytrack.model.Analysis; @@ -166,8 +169,13 @@ public Response updateAnalysis(AnalysisRequest request) { } String commenter = null; - if (getPrincipal() instanceof LdapUser || getPrincipal() instanceof ManagedUser || getPrincipal() instanceof OidcUser) { - commenter = ((UserPrincipal) getPrincipal()).getUsername(); + if (getPrincipal() instanceof UserPrincipal principal) { + commenter = principal.getUsername(); + } else if (getPrincipal() instanceof ApiKey apiKey) { + List teams = apiKey.getTeams(); + List teamNames = new ArrayList(); + teams.forEach(team -> teamNames.add(team.getName())); + commenter = String.join(", ", teamNames); } boolean analysisStateChange = false; diff --git a/src/test/java/org/dependencytrack/resources/v1/AnalysisResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/AnalysisResourceTest.java index 237344d9ca..f29e4bf332 100644 --- a/src/test/java/org/dependencytrack/resources/v1/AnalysisResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/AnalysisResourceTest.java @@ -18,11 +18,13 @@ */ package org.dependencytrack.resources.v1; +import alpine.model.ManagedUser; import alpine.notification.Notification; import alpine.notification.NotificationLevel; import alpine.notification.NotificationService; import alpine.notification.Subscriber; import alpine.notification.Subscription; +import alpine.server.auth.JsonWebToken; import alpine.server.filters.ApiFilter; import alpine.server.filters.AuthenticationFilter; import alpine.server.filters.AuthorizationFilter; @@ -353,10 +355,70 @@ public void updateAnalysisCreateNewTest() throws Exception { assertThat(responseJson.getJsonArray("analysisComments")).hasSize(2); assertThat(responseJson.getJsonArray("analysisComments").getJsonObject(0)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Analysis: NOT_SET → NOT_AFFECTED")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(responseJson.getJsonArray("analysisComments").getJsonObject(1)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Analysis comment here")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); + assertThat(responseJson.getBoolean("isSuppressed")).isTrue(); + + assertConditionWithTimeout(() -> NOTIFICATIONS.size() == 2, Duration.ofSeconds(5)); + final Notification projectNotification = NOTIFICATIONS.poll(); + assertThat(projectNotification).isNotNull(); + final Notification notification = NOTIFICATIONS.poll(); + assertThat(notification).isNotNull(); + assertThat(notification.getScope()).isEqualTo(NotificationScope.PORTFOLIO.name()); + assertThat(notification.getGroup()).isEqualTo(NotificationGroup.PROJECT_AUDIT_CHANGE.name()); + assertThat(notification.getLevel()).isEqualTo(NotificationLevel.INFORMATIONAL); + assertThat(notification.getTitle()).isEqualTo(NotificationUtil.generateNotificationTitle(NotificationConstants.Title.ANALYSIS_DECISION_NOT_AFFECTED, project)); + assertThat(notification.getContent()).isEqualTo("An analysis decision was made to a finding affecting a project"); + } + + @Test + public void updateAnalysisCreateNewWithUserTest() throws Exception { + initializeWithPermissions(Permissions.VULNERABILITY_ANALYSIS); + ManagedUser testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); + String jwt = new JsonWebToken().createToken(testUser); + qm.addUserToTeam(testUser, team); + + final Project project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); + + var component = new Component(); + component.setProject(project); + component.setName("Acme Component"); + component.setVersion("1.0"); + component = qm.createComponent(component, false); + + var vulnerability = new Vulnerability(); + vulnerability.setVulnId("INT-001"); + vulnerability.setSource(Vulnerability.Source.INTERNAL); + vulnerability.setSeverity(Severity.HIGH); + vulnerability.setComponents(List.of(component)); + vulnerability = qm.createVulnerability(vulnerability, false); + + final var analysisRequest = new AnalysisRequest(project.getUuid().toString(), component.getUuid().toString(), + vulnerability.getUuid().toString(), AnalysisState.NOT_AFFECTED, AnalysisJustification.CODE_NOT_REACHABLE, + AnalysisResponse.WILL_NOT_FIX, "Analysis details here", "Analysis comment here", true); + + final Response response = jersey.target(V1_ANALYSIS) + .request() + .header("Authorization", "Bearer " + jwt) + .put(Entity.entity(analysisRequest, MediaType.APPLICATION_JSON)); + assertThat(response.getStatus()).isEqualTo(HttpStatus.SC_OK); + assertThat(response.getHeaderString(TOTAL_COUNT_HEADER)).isNull(); + + final JsonObject responseJson = parseJsonObject(response); + assertThat(responseJson).isNotNull(); + assertThat(responseJson.getString("analysisState")).isEqualTo(AnalysisState.NOT_AFFECTED.name()); + assertThat(responseJson.getString("analysisJustification")).isEqualTo(AnalysisJustification.CODE_NOT_REACHABLE.name()); + assertThat(responseJson.getString("analysisResponse")).isEqualTo(AnalysisResponse.WILL_NOT_FIX.name()); + assertThat(responseJson.getString("analysisDetails")).isEqualTo("Analysis details here"); + assertThat(responseJson.getJsonArray("analysisComments")).hasSize(2); + assertThat(responseJson.getJsonArray("analysisComments").getJsonObject(0)) + .hasFieldOrPropertyWithValue("comment", Json.createValue("Analysis: NOT_SET → NOT_AFFECTED")) + .hasFieldOrPropertyWithValue("commenter", Json.createValue("testuser")); + assertThat(responseJson.getJsonArray("analysisComments").getJsonObject(1)) + .hasFieldOrPropertyWithValue("comment", Json.createValue("Analysis comment here")) + .hasFieldOrPropertyWithValue("commenter", Json.createValue("testuser")); assertThat(responseJson.getBoolean("isSuppressed")).isTrue(); assertConditionWithTimeout(() -> NOTIFICATIONS.size() == 2, Duration.ofSeconds(5)); @@ -469,22 +531,22 @@ public void updateAnalysisUpdateExistingTest() throws Exception { .hasFieldOrPropertyWithValue("commenter", Json.createValue("Jane Doe")); assertThat(analysisComments.getJsonObject(1)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Analysis: NOT_AFFECTED → EXPLOITABLE")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(analysisComments.getJsonObject(2)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Justification: CODE_NOT_REACHABLE → NOT_SET")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(analysisComments.getJsonObject(3)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Vendor Response: WILL_NOT_FIX → UPDATE")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(analysisComments.getJsonObject(4)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Details: New analysis details here")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(analysisComments.getJsonObject(5)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Unsuppressed")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(analysisComments.getJsonObject(6)) .hasFieldOrPropertyWithValue("comment", Json.createValue("New analysis comment here")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(responseJson.getBoolean("isSuppressed")).isFalse(); assertConditionWithTimeout(() -> NOTIFICATIONS.size() == 2, Duration.ofSeconds(5)); @@ -596,13 +658,13 @@ public void updateAnalysisUpdateExistingWithEmptyRequestTest() throws Exception .hasFieldOrPropertyWithValue("commenter", Json.createValue("Jane Doe")); assertThat(analysisComments.getJsonObject(1)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Analysis: NOT_AFFECTED → NOT_SET")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(analysisComments.getJsonObject(2)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Justification: CODE_NOT_REACHABLE → NOT_SET")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(analysisComments.getJsonObject(3)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Vendor Response: WILL_NOT_FIX → NOT_SET")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertConditionWithTimeout(() -> NOTIFICATIONS.size() == 2, Duration.ofSeconds(5)); final Notification projectNotification = NOTIFICATIONS.poll(); @@ -759,19 +821,19 @@ public void updateAnalysisIssue1409Test() throws InterruptedException { assertThat(analysisComments).hasSize(5); assertThat(analysisComments.getJsonObject(0)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Analysis: IN_TRIAGE → NOT_AFFECTED")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(analysisComments.getJsonObject(1)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Justification: NOT_SET → PROTECTED_BY_MITIGATING_CONTROL")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(analysisComments.getJsonObject(2)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Vendor Response: NOT_SET → UPDATE")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(analysisComments.getJsonObject(3)) .hasFieldOrPropertyWithValue("comment", Json.createValue("Details: New analysis details here")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(analysisComments.getJsonObject(4)) .hasFieldOrPropertyWithValue("comment", Json.createValue("New analysis comment here")) - .doesNotContainKey("commenter"); // Not set when authenticating via API key + .hasFieldOrPropertyWithValue("commenter", Json.createValue("Test Users")); assertThat(responseJson.getBoolean("isSuppressed")).isFalse(); assertConditionWithTimeout(() -> NOTIFICATIONS.size() == 2, Duration.ofSeconds(5)); From 4bcd548ffd52425f2f8ca78dff60f30c8955a406 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 08:43:41 +0000 Subject: [PATCH 159/429] Bump lib.protobuf-java.version from 4.28.1 to 4.28.2 Bumps `lib.protobuf-java.version` from 4.28.1 to 4.28.2. Updates `com.google.protobuf:protobuf-java` from 4.28.1 to 4.28.2 - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/commits) Updates `com.google.protobuf:protobuf-java-util` from 4.28.1 to 4.28.2 --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-java dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.google.protobuf:protobuf-java-util dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d7fd29dbdd..9acf169275 100644 --- a/pom.xml +++ b/pom.xml @@ -118,7 +118,7 @@ 6.2.0 1.5.0 3.2.2 - 4.28.1 + 4.28.2 2.2.0 2.1.22 1.19.0 From 35d59e6e3d02b22f67e978779e3d6bdca7e544c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 08:22:29 +0000 Subject: [PATCH 160/429] Bump org.apache.httpcomponents.client5:httpclient5 from 5.3.1 to 5.4 Bumps [org.apache.httpcomponents.client5:httpclient5](https://github.com/apache/httpcomponents-client) from 5.3.1 to 5.4. - [Changelog](https://github.com/apache/httpcomponents-client/blob/master/RELEASE_NOTES.txt) - [Commits](https://github.com/apache/httpcomponents-client/compare/rel/v5.3.1...rel/v5.4) --- updated-dependencies: - dependency-name: org.apache.httpcomponents.client5:httpclient5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9acf169275..30a383ecd7 100644 --- a/pom.xml +++ b/pom.xml @@ -128,7 +128,7 @@ 1.1.1 2.1.1 4.5.14 - 5.3.1 + 5.4 2.0.16 1.323 From 0cb20df002ab4d119900dd2158d4a0045a4ef698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Fri, 20 Sep 2024 13:09:00 +0200 Subject: [PATCH 161/429] Implemented things from the review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../model/ConfigPropertyConstants.java | 2 +- .../org/dependencytrack/model/Project.java | 1 + .../resources/v1/ProjectResource.java | 61 ++++++++------ .../resources/v1/TeamResource.java | 25 +++--- .../resources/v1/vo/VisibleTeams.java | 27 ------ .../resources/v1/ProjectResourceTest.java | 83 +++++++++++-------- .../resources/v1/TeamResourceTest.java | 56 ++++++------- 7 files changed, 124 insertions(+), 131 deletions(-) delete mode 100644 src/main/java/org/dependencytrack/resources/v1/vo/VisibleTeams.java diff --git a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java index 7515206c91..fdc9ecb4f4 100644 --- a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java +++ b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java @@ -95,7 +95,7 @@ public enum ConfigPropertyConstants { KENNA_SYNC_CADENCE("integrations", "kenna.sync.cadence", "60", PropertyType.INTEGER, "The cadence (in minutes) to upload to Kenna Security"), KENNA_TOKEN("integrations", "kenna.token", null, PropertyType.ENCRYPTEDSTRING, "The token to use when authenticating to Kenna Security"), KENNA_CONNECTOR_ID("integrations", "kenna.connector.id", null, PropertyType.STRING, "The Kenna Security connector identifier to upload to"), - ACCESS_MANAGEMENT_ACL_ENABLED("access-management", "acl.enabled", "false", PropertyType.BOOLEAN, "Flag to enable/disable access control to projects in the portfolio"), + ACCESS_MANAGEMENT_ACL_ENABLED("access-management", "acl.enabled", "false", PropertyType.BOOLEAN, "Flag to enable/disable access control to projects in the portfolio", true), NOTIFICATION_TEMPLATE_BASE_DIR("notification", "template.baseDir", SystemUtils.getEnvironmentVariable("DEFAULT_TEMPLATES_OVERRIDE_BASE_DIRECTORY", System.getProperty("user.home")), PropertyType.STRING, "The base directory to use when searching for notification templates"), NOTIFICATION_TEMPLATE_DEFAULT_OVERRIDE_ENABLED("notification", "template.default.override.enabled", SystemUtils.getEnvironmentVariable("DEFAULT_TEMPLATES_OVERRIDE_ENABLED", "false"), PropertyType.BOOLEAN, "Flag to enable/disable override of default notification templates"), TASK_SCHEDULER_LDAP_SYNC_CADENCE("task-scheduler", "ldap.sync.cadence", "6", PropertyType.INTEGER, "Sync cadence (in hours) for LDAP"), diff --git a/src/main/java/org/dependencytrack/model/Project.java b/src/main/java/org/dependencytrack/model/Project.java index df8c5873b4..5d10072ee4 100644 --- a/src/main/java/org/dependencytrack/model/Project.java +++ b/src/main/java/org/dependencytrack/model/Project.java @@ -273,6 +273,7 @@ public enum FetchGroup { @Join(column = "PROJECT_ID") @Element(column = "TEAM_ID") @Order(extensions = @Extension(vendorName = "datanucleus", key = "list-ordering", value = "name ASC")) + @JsonInclude(value = JsonInclude.Include.NON_EMPTY) private List accessTeams; @Persistent(defaultFetchGroup = "true") diff --git a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java index b85446aee7..b209b331e4 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java @@ -284,11 +284,13 @@ public Response getProjectsByClassifier( content = @Content(schema = @Schema(implementation = Project.class)) ), @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "403", description = "You don't have the permission to assign this team to a project."), @ApiResponse(responseCode = "409", description = """
        • An inactive Parent cannot be selected as parent, or
        • A project with the specified name already exists
        """), + @ApiResponse(responseCode = "422", description = "You need to specify at least one team to which the project should belong"), }) @PermissionRequired(Permissions.Constants.PORTFOLIO_MANAGEMENT) public Response createProject(Project jsonProject) { @@ -315,33 +317,40 @@ public Response createProject(Project jsonProject) { Project parent = qm.getObjectByUuid(Project.class, jsonProject.getParent().getUuid()); jsonProject.setParent(parent); } - final List choosenTeams = jsonProject.getAccessTeams(); - Principal principal = getPrincipal(); - List userTeams = new ArrayList(); - if (principal instanceof final UserPrincipal userPrincipal) { - userTeams = userPrincipal.getTeams(); - } else if (principal instanceof final ApiKey apiKey) { - userTeams = apiKey.getTeams(); - } - boolean required = qm.isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED); - boolean isAdmin = qm.hasAccessManagementPermission(principal); - if (required && choosenTeams.isEmpty()) { - return Response.status(422) - .entity("You need to specify at least one team to which the project should belong").build(); - } - List visibleTeams = isAdmin ? qm.getTeams() : userTeams; - List visibleUuids = visibleTeams.isEmpty() ? new ArrayList(): visibleTeams.stream().map(Team::getUuid).toList(); - jsonProject.setAccessTeams(new ArrayList()); - for (Team choosenTeam : choosenTeams) { - if (!visibleUuids.contains(choosenTeam.getUuid())) { - return isAdmin ? Response.status(404).entity("This team does not exist!").build() - : Response.status(403) - .entity("You don't have the permission to assign this team to a project.").build(); + if (!qm.doesProjectExist(StringUtils.trimToNull(jsonProject.getName()), + StringUtils.trimToNull(jsonProject.getVersion()))) { + final List chosenTeams = jsonProject.getAccessTeams() == null ? new ArrayList() + : jsonProject.getAccessTeams(); + boolean required = qm.isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED); + if (required && chosenTeams.isEmpty()) { + return Response.status(422) + .entity("You need to specify at least one team to which the project should belong").build(); } - Team ormTeam = qm.getObjectByUuid(Team.class, choosenTeam.getUuid()); - jsonProject.addAccessTeam(ormTeam); - } - if (!qm.doesProjectExist(StringUtils.trimToNull(jsonProject.getName()), StringUtils.trimToNull(jsonProject.getVersion()))) { + Principal principal = getPrincipal(); + if (!chosenTeams.isEmpty()) { + List userTeams = new ArrayList(); + if (principal instanceof final UserPrincipal userPrincipal) { + userTeams = userPrincipal.getTeams(); + } else if (principal instanceof final ApiKey apiKey) { + userTeams = apiKey.getTeams(); + } + boolean isAdmin = qm.hasAccessManagementPermission(principal); + List visibleTeams = isAdmin ? qm.getTeams() : userTeams; + List visibleUuids = visibleTeams.isEmpty() ? new ArrayList() + : visibleTeams.stream().map(Team::getUuid).toList(); + jsonProject.setAccessTeams(new ArrayList()); + for (Team choosenTeam : chosenTeams) { + if (!visibleUuids.contains(choosenTeam.getUuid())) { + return isAdmin ? Response.status(404).entity("This team does not exist!").build() + : Response.status(403) + .entity("You don't have the permission to assign this team to a project.") + .build(); + } + Team ormTeam = qm.getObjectByUuid(Team.class, choosenTeam.getUuid()); + jsonProject.addAccessTeam(ormTeam); + } + } + final Project project; try { project = qm.createProject(jsonProject, jsonProject.getTags(), true); diff --git a/src/main/java/org/dependencytrack/resources/v1/TeamResource.java b/src/main/java/org/dependencytrack/resources/v1/TeamResource.java index fb447ac635..469a55a258 100644 --- a/src/main/java/org/dependencytrack/resources/v1/TeamResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/TeamResource.java @@ -37,11 +37,9 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirements; import io.swagger.v3.oas.annotations.tags.Tag; import org.dependencytrack.auth.Permissions; -import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.validation.ValidUuid; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.resources.v1.vo.TeamSelfResponse; -import org.dependencytrack.resources.v1.vo.VisibleTeams; import org.owasp.security.logging.SecurityMarkers; import jakarta.validation.Validator; @@ -230,24 +228,25 @@ public Response deleteTeam(Team jsonTeam) { @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Returns a list of Teams that are visible", description = "

        ") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "The Visible Teams", content = @Content(schema = @Schema(implementation = VisibleTeams.class))), + @ApiResponse(responseCode = "200", description = "The Visible Teams", content = @Content(array = @ArraySchema(schema = @Schema(implementation = Team.class)))), @ApiResponse(responseCode = "401", description = "Unauthorized") }) public Response availableTeams() { try (QueryManager qm = new QueryManager()) { Principal user = getPrincipal(); - List userTeams = new ArrayList(); - if (user instanceof final UserPrincipal userPrincipal) { - userTeams = userPrincipal.getTeams(); - } else if (user instanceof final ApiKey apiKey) { - userTeams = apiKey.getTeams(); - } - boolean required = qm.isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED); boolean isAllTeams = qm.hasAccessManagementPermission(user); - List teams = isAllTeams ? qm.getTeams() : userTeams; - VisibleTeams response = new VisibleTeams(required, teams); + List teams = new ArrayList(); + if (isAllTeams) { + teams = qm.getTeams(); + } else { + if (user instanceof final UserPrincipal userPrincipal) { + teams = userPrincipal.getTeams(); + } else if (user instanceof final ApiKey apiKey) { + teams = apiKey.getTeams(); + } + } - return Response.ok(response).build(); + return Response.ok(teams).build(); } } diff --git a/src/main/java/org/dependencytrack/resources/v1/vo/VisibleTeams.java b/src/main/java/org/dependencytrack/resources/v1/vo/VisibleTeams.java deleted file mode 100644 index ba9f186262..0000000000 --- a/src/main/java/org/dependencytrack/resources/v1/vo/VisibleTeams.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file is part of Dependency-Track. - * - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright (c) OWASP Foundation. All Rights Reserved. - */ -package org.dependencytrack.resources.v1.vo; - -import java.util.List; - -import alpine.model.Team; - -public record VisibleTeams(boolean required, - List teams) { -} diff --git a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java index 1418ab36e1..039f4ec4fc 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java @@ -20,9 +20,8 @@ import alpine.common.util.UuidUtil; import alpine.event.framework.EventService; -import alpine.model.IConfigProperty; -import alpine.model.ManagedUser; import alpine.model.IConfigProperty.PropertyType; +import alpine.model.ManagedUser; import alpine.model.Team; import alpine.model.Permission; import alpine.server.auth.JsonWebToken; @@ -97,7 +96,7 @@ public void after() throws Exception { super.after(); } - public void getUserToken(boolean isAdmin) { + public void setUpUser(boolean isAdmin, boolean isRequired) { testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); jwt = new JsonWebToken().createToken(testUser); qm.addUserToTeam(testUser, team); @@ -112,6 +111,14 @@ public void getUserToken(boolean isAdmin) { permissionsList.add(adminPermission); testUser.setPermissions(permissionsList); } + if (isRequired) { + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null); + } } @Test @@ -309,7 +316,6 @@ public void getProjectByUuidTest() { .withMatcher("childUuid", equalTo(childProject.getUuid().toString())) .isEqualTo(""" { - "accessTeams": [], "name": "acme-app", "version": "1.0.0", "uuid": "${json-unit.matches:projectUuid}", @@ -439,7 +445,6 @@ public void getProjectByUnknownTagTest() { @Test public void createProjectTest(){ Project project = new Project(); - project.setAccessTeams(new ArrayList()); project.setName("Acme Example"); project.setVersion("1.0"); project.setDescription("Test project"); @@ -460,7 +465,6 @@ public void createProjectTest(){ @Test public void createProjectDuplicateTest() { Project project = new Project(); - project.setAccessTeams(new ArrayList()); project.setName("Acme Example"); project.setVersion("1.0"); Response response = jersey.target(V1_PROJECT) @@ -480,7 +484,6 @@ public void createProjectDuplicateTest() { @Test public void createProjectWithoutVersionDuplicateTest() { Project project = new Project(); - project.setAccessTeams(new ArrayList()); project.setName("Acme Example"); Response response = jersey.target(V1_PROJECT) .request() @@ -509,30 +512,31 @@ public void createProjectEmptyTest() { @Test public void createProjectWithExistingTeamRequiredTest() { - getUserToken(false); + setUpUser(false, true); Team AllowedTeam = qm.createTeam("AllowedTeam", false); Project project = new Project(); project.setName("ProjectWithExistingTeamRequired"); qm.addUserToTeam(testUser, AllowedTeam); - qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); final JsonObject jsonTeam = Json.createObjectBuilder().add("uuid", AllowedTeam.getUuid().toString()).build(); final JsonObjectBuilder requestBodyBuilder = Json.createObjectBuilder() - .add("name", project.getName()).add("classifier", "CONTAINER").addNull("parent").add("active", true) + .add("name", project.getName()).add("classifier", "CONTAINER").addNull("parent").add("active", true).add("tags", Json.createArrayBuilder()) .add("accessTeams", Json.createArrayBuilder().add(jsonTeam).build()); Response response = jersey.target(V1_PROJECT) .request() .header("Authorization", "Bearer " + jwt) .put(Entity.json(requestBodyBuilder.build().toString())); - Assert.assertEquals(201, response.getStatus(), 0); + Assert.assertEquals(201, response.getStatus()); + JsonObject returnedProject = parseJsonObject(response); + JsonArray teams = returnedProject.getJsonArray("accessTeams"); + Assert.assertEquals(teams.size(), 1); + Assert.assertEquals(AllowedTeam.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); } @Test public void createProjectWithoutExistingTeamRequiredTest() { - getUserToken(false); + setUpUser(false, true); Project project = new Project(); project.setName("ProjectWithoutExistingTeamRequired"); - project.setAccessTeams(new ArrayList()); - qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); Response response = jersey.target(V1_PROJECT) .request() .header("Authorization", "Bearer " + jwt) @@ -542,54 +546,55 @@ public void createProjectWithoutExistingTeamRequiredTest() { @Test public void createProjectWithNotAllowedExistingTeamTest() { - getUserToken(false); + setUpUser(false, true); Team notAllowedTeam = qm.createTeam("NotAllowedTeam", false); Project project = new Project(); project.setName("ProjectWithNotAllowedExistingTeam"); project.addAccessTeam(notAllowedTeam); - qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); Response response = jersey.target(V1_PROJECT) .request() .header("Authorization", "Bearer " + jwt) .put(Entity.entity(project, MediaType.APPLICATION_JSON)); - Assert.assertEquals(403, response.getStatus(), 0); + Assert.assertEquals(403, response.getStatus()); } @Test public void createProjectWithNotAllowedExistingTeamAdminTest() { - getUserToken(true); + setUpUser(true, true); Team notAllowedTeam = qm.createTeam("NotAllowedTeam", false); Project project = new Project(); project.setName("ProjectWithNotAllowedExistingTeam"); project.addAccessTeam(notAllowedTeam); - qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); Response response = jersey.target(V1_PROJECT) .request() .header("Authorization", "Bearer " + jwt) .put(Entity.entity(project, MediaType.APPLICATION_JSON)); - Assert.assertEquals(201, response.getStatus(), 0); + Assert.assertEquals(201, response.getStatus()); + JsonObject returnedProject = parseJsonObject(response); + JsonArray teams = returnedProject.getJsonArray("accessTeams"); + Assert.assertEquals(teams.size(), 1); + Assert.assertEquals(notAllowedTeam.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); } @Test public void createProjectWithNotExistingTeamNoAdminTest() { - getUserToken(false); + setUpUser(false, true); Team notAllowedTeam = new Team(); notAllowedTeam.setUuid(new UUID(1, 1)); notAllowedTeam.setName("NotAllowedTeam"); Project project = new Project(); project.addAccessTeam(notAllowedTeam); project.setName("ProjectWithNotAllowedExistingTeam"); - qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); Response response = jersey.target(V1_PROJECT) .request() .header("Authorization", "Bearer " + jwt) .put(Entity.entity(project, MediaType.APPLICATION_JSON)); - Assert.assertEquals(403, response.getStatus(), 0); + Assert.assertEquals(403, response.getStatus()); } @Test public void createProjectWithNotExistingTeamTest() { - getUserToken(true); + setUpUser(true, false); Team notAllowedTeam = new Team(); notAllowedTeam.setUuid(new UUID(1, 1)); notAllowedTeam.setName("NotAllowedTeam"); @@ -600,7 +605,26 @@ public void createProjectWithNotExistingTeamTest() { .request() .header("Authorization", "Bearer " + jwt) .put(Entity.entity(project, MediaType.APPLICATION_JSON)); - Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertEquals(404, response.getStatus()); + } + + @Test + public void createProjectWithApiKeyTest() { + Project project = new Project(); + project.setName("ProjectWithNotExistingTeam"); + final JsonObject jsonTeam = Json.createObjectBuilder().add("uuid", team.getUuid().toString()).build(); + final JsonObjectBuilder requestBodyBuilder = Json.createObjectBuilder() + .add("name", project.getName()).add("classifier", "CONTAINER").addNull("parent").add("active", true).add("tags", Json.createArrayBuilder()) + .add("accessTeams", Json.createArrayBuilder().add(jsonTeam).build()); + Response response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .put(Entity.json(requestBodyBuilder.build().toString())); + Assert.assertEquals(201, response.getStatus()); + JsonObject returnedProject = parseJsonObject(response); + JsonArray teams = returnedProject.getJsonArray("accessTeams"); + Assert.assertEquals(teams.size(), 1); + Assert.assertEquals(team.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); } @Test @@ -833,7 +857,6 @@ public void patchProjectSuccessfullyPatchedTest() { .withMatcher("projectUuid", equalTo(p1.getUuid().toString())) .isEqualTo(""" { - "accessTeams": [], "publisher": "new publisher", "manufacturer": { "name": "manufacturerName", @@ -930,7 +953,6 @@ public void patchProjectParentTest() { .withMatcher("parentProjectUuid", CoreMatchers.equalTo(newParent.getUuid().toString())) .isEqualTo(""" { - "accessTeams": [], "name": "DEF", "version": "2.0", "uuid": "${json-unit.matches:projectUuid}", @@ -1289,7 +1311,6 @@ public void issue3883RegressionTest() { .header(X_API_KEY, apiKey) .put(Entity.json(""" { - "accessTeams": [], "name": "acme-app-parent", "version": "1.0.0" } @@ -1302,7 +1323,6 @@ public void issue3883RegressionTest() { .header(X_API_KEY, apiKey) .put(Entity.json(""" { - "accessTeams": [], "name": "acme-app", "version": "1.0.0", "parent": { @@ -1320,7 +1340,6 @@ public void issue3883RegressionTest() { assertThat(response.getStatus()).isEqualTo(200); assertThatJson(getPlainTextBody(response)).isEqualTo(""" { - "accessTeams": [], "name": "acme-app-parent", "version": "1.0.0", "classifier": "APPLICATION", @@ -1354,7 +1373,6 @@ public void issue3883RegressionTest() { assertThat(response.getStatus()).isEqualTo(200); assertThatJson(getPlainTextBody(response)).isEqualTo(""" { - "accessTeams": [], "name": "acme-app", "version": "1.0.0", "classifier": "APPLICATION", @@ -1395,8 +1413,7 @@ public void issue4048RegressionTest() { final JsonObjectBuilder requestBodyBuilder = Json.createObjectBuilder() .add("name", "project-%d-%d".formatted(i, j)) - .add("version", "%d.%d".formatted(i, j)) - .add("accessTeams",Json.createArrayBuilder().build()); + .add("version", "%d.%d".formatted(i, j)); if (parentUuid != null) { requestBodyBuilder.add("parent", Json.createObjectBuilder() .add("uuid", parentUuid.toString())); diff --git a/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java index 0e463ba7df..2d55542cb2 100644 --- a/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/TeamResourceTest.java @@ -21,7 +21,6 @@ import alpine.common.util.UuidUtil; import alpine.model.ApiKey; import alpine.model.ConfigProperty; -import alpine.model.IConfigProperty; import alpine.model.ManagedUser; import alpine.model.Permission; import alpine.model.Team; @@ -55,7 +54,6 @@ import static org.hamcrest.CoreMatchers.equalTo; public class TeamResourceTest extends ResourceTest { - private ManagedUser testUser; private String jwt; private Team userNotPartof; @@ -65,8 +63,8 @@ public class TeamResourceTest extends ResourceTest { .register(ApiFilter.class) .register(AuthenticationFilter.class)); - public void getUserToken(boolean isAdmin) { - testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); + public void setUpUser(boolean isAdmin) { + ManagedUser testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); jwt = new JsonWebToken().createToken(testUser); qm.addUserToTeam(testUser, team); userNotPartof = qm.createTeam("UserNotPartof", false); @@ -231,67 +229,63 @@ public void deleteTeamWithAclTest() { } @Test - public void getVisibleAdminRequiredTeams() { - getUserToken(true); - qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); + public void getVisibleAdminTeams() { + setUpUser(true); Response response = jersey.target(V1_TEAM + "/visible") .request() .header("Authorization", "Bearer " + jwt) .get(); Assert.assertEquals(200, response.getStatus(), 0); - JsonObject body = parseJsonObject(response); - Assert.assertTrue(body.getBoolean("required")); - JsonArray teams = body.getJsonArray("teams"); + JsonArray teams = parseJsonArray(response); Assert.assertEquals(2, teams.size()); Assert.assertEquals(this.team.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); Assert.assertEquals(userNotPartof.getUuid().toString(), teams.get(1).asJsonObject().getString("uuid")); } @Test - public void getVisibleAdminNotRequiredTeams() { - getUserToken(true); + public void getVisibleNotAdminTeams() { + setUpUser(false); Response response = jersey.target(V1_TEAM + "/visible") .request() .header("Authorization", "Bearer " + jwt) .get(); Assert.assertEquals(200, response.getStatus(), 0); - JsonObject body = parseJsonObject(response); - Assert.assertFalse(body.getBoolean("required")); - JsonArray teams = body.getJsonArray("teams"); - Assert.assertEquals(2, teams.size()); + JsonArray teams = parseJsonArray(response); + Assert.assertEquals(1, teams.size()); Assert.assertEquals(this.team.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); - Assert.assertEquals(userNotPartof.getUuid().toString(), teams.get(1).asJsonObject().getString("uuid")); } @Test - public void getVisibleNotAdminRequiredTeams() { - getUserToken(false); - qm.createConfigProperty("access-management", "acl.enabled", "true", IConfigProperty.PropertyType.BOOLEAN, ""); + public void getVisibleNotAdminApiKeyTeams() { Response response = jersey.target(V1_TEAM + "/visible") .request() - .header("Authorization", "Bearer " + jwt) + .header(X_API_KEY, apiKey) .get(); Assert.assertEquals(200, response.getStatus(), 0); - JsonObject body = parseJsonObject(response); - Assert.assertTrue(body.getBoolean("required")); - JsonArray teams = body.getJsonArray("teams"); + JsonArray teams = parseJsonArray(response); Assert.assertEquals(1, teams.size()); Assert.assertEquals(this.team.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); } @Test - public void getVisibleNotAdminNotRequiredTeams() { - getUserToken(false); + public void getVisibleAdminApiKeyTeams() { + userNotPartof = qm.createTeam("UserNotPartof", false); + final var generator = new DefaultObjectGenerator(); + generator.loadDefaultPermissions(); + List permissionsList = new ArrayList(); + final Permission adminPermission = qm.getPermission("ACCESS_MANAGEMENT"); + permissionsList.add(adminPermission); + this.team.setPermissions(permissionsList); + Response response = jersey.target(V1_TEAM + "/visible") .request() - .header("Authorization", "Bearer " + jwt) + .header(X_API_KEY, apiKey) .get(); Assert.assertEquals(200, response.getStatus(), 0); - JsonObject body = parseJsonObject(response); - Assert.assertFalse(body.getBoolean("required")); - JsonArray teams = body.getJsonArray("teams"); - Assert.assertEquals(1, teams.size()); + JsonArray teams = parseJsonArray(response); + Assert.assertEquals(2, teams.size()); Assert.assertEquals(this.team.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); + Assert.assertEquals(userNotPartof.getUuid().toString(), teams.get(1).asJsonObject().getString("uuid")); } @Test From 3c3b2aedbb46689a68dc5071175414ae8b36b472 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 22 Sep 2024 15:37:09 +0200 Subject: [PATCH 162/429] Fix infinite recursion during policy condition serialization As of Alpine v3, objects are no longer unloaded and refreshed (with fetch groups) during `QueryManager#persist`. In case of the `PolicyCondition` REST endpoints, the previous behavior caused `PolicyCondition#policy` to be unloaded, prior to serializing the `PolicyCondition` response. With persistent objects no longer being unloaded and refreshed in `QueryManager#persist`, that behavior changed, leading to infinite recursion when serializing `PolicyCondition#policy`. Resolved the issue by restoring the previous behavior of not returning policy data in any of the condition endpoints. Signed-off-by: nscuro --- .../resources/v1/PolicyConditionResource.java | 12 +- .../v1/PolicyConditionResourceTest.java | 226 ++++++++++++++++++ 2 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/dependencytrack/resources/v1/PolicyConditionResourceTest.java diff --git a/src/main/java/org/dependencytrack/resources/v1/PolicyConditionResource.java b/src/main/java/org/dependencytrack/resources/v1/PolicyConditionResource.java index fa068dd8b7..fd6e610b3f 100644 --- a/src/main/java/org/dependencytrack/resources/v1/PolicyConditionResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/PolicyConditionResource.java @@ -93,6 +93,11 @@ public Response createPolicyCondition( if (policy != null) { final PolicyCondition pc = qm.createPolicyCondition(policy, jsonPolicyCondition.getSubject(), jsonPolicyCondition.getOperator(), StringUtils.trimToNull(jsonPolicyCondition.getValue())); + + // Prevent infinite recursion during JSON serialization. + qm.makeTransient(pc); + pc.setPolicy(null); + return Response.status(Response.Status.CREATED).entity(pc).build(); } else { return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the policy could not be found.").build(); @@ -127,7 +132,12 @@ public Response updatePolicyCondition(PolicyCondition jsonPolicyCondition) { PolicyCondition pc = qm.getObjectByUuid(PolicyCondition.class, jsonPolicyCondition.getUuid()); if (pc != null) { pc = qm.updatePolicyCondition(jsonPolicyCondition); - return Response.status(Response.Status.CREATED).entity(pc).build(); + + // Prevent infinite recursion during JSON serialization. + qm.makeTransient(pc); + pc.setPolicy(null); + + return Response.status(Response.Status.OK).entity(pc).build(); } else { return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the policy condition could not be found.").build(); } diff --git a/src/test/java/org/dependencytrack/resources/v1/PolicyConditionResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/PolicyConditionResourceTest.java new file mode 100644 index 0000000000..1d39b595cd --- /dev/null +++ b/src/test/java/org/dependencytrack/resources/v1/PolicyConditionResourceTest.java @@ -0,0 +1,226 @@ +package org.dependencytrack.resources.v1; + +import alpine.server.filters.ApiFilter; +import alpine.server.filters.AuthenticationFilter; +import alpine.server.filters.AuthorizationFilter; +import org.dependencytrack.JerseyTestRule; +import org.dependencytrack.ResourceTest; +import org.dependencytrack.auth.Permissions; +import org.dependencytrack.model.Policy; +import org.dependencytrack.model.Policy.Operator; +import org.dependencytrack.model.Policy.ViolationState; +import org.dependencytrack.model.PolicyCondition; +import org.glassfish.jersey.server.ResourceConfig; +import org.junit.ClassRule; +import org.junit.Test; + +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.Response; +import javax.jdo.JDOObjectNotFoundException; +import java.util.UUID; + +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.hamcrest.CoreMatchers.equalTo; + +public class PolicyConditionResourceTest extends ResourceTest { + + @ClassRule + public static JerseyTestRule jersey = new JerseyTestRule( + new ResourceConfig(PolicyConditionResource.class) + .register(ApiFilter.class) + .register(AuthenticationFilter.class) + .register(AuthorizationFilter.class)); + + @Test + public void testCreateCondition() { + initializeWithPermissions(Permissions.POLICY_MANAGEMENT); + + final var policy = new Policy(); + policy.setName("foo"); + policy.setOperator(Operator.ANY); + policy.setViolationState(ViolationState.INFO); + qm.persist(policy); + + final Response response = jersey.target("%s/%s/condition".formatted(V1_POLICY, policy.getUuid())) + .request() + .header(X_API_KEY, apiKey) + .put(Entity.json(/* language=JSON */ """ + { + "subject": "PACKAGE_URL", + "operator": "MATCHES", + "value": "pkg:maven/foo/bar" + } + """)); + assertThat(response.getStatus()).isEqualTo(201); + assertThatJson(getPlainTextBody(response)).isEqualTo(/* language=JSON */ """ + { + "uuid": "${json-unit.any-string}", + "subject": "PACKAGE_URL", + "operator": "MATCHES", + "value": "pkg:maven/foo/bar" + } + """); + } + + @Test + public void testCreateConditionWhenPolicyDoesNotExist() { + initializeWithPermissions(Permissions.POLICY_MANAGEMENT); + + final Response response = jersey.target("%s/cec42e01-62a7-4c86-9b8f-cd6650be2888/condition".formatted(V1_POLICY)) + .request() + .header(X_API_KEY, apiKey) + .put(Entity.json(/* language=JSON */ """ + { + "subject": "PACKAGE_URL", + "operator": "MATCHES", + "value": "pkg:maven/foo/bar" + } + """)); + assertThat(response.getStatus()).isEqualTo(404); + assertThat(getPlainTextBody(response)).isEqualTo("The UUID of the policy could not be found."); + } + + @Test + public void testCreateConditionWhenUnauthorized() { + final Response response = jersey.target("%s/cec42e01-62a7-4c86-9b8f-cd6650be2888/condition".formatted(V1_POLICY)) + .request() + .header(X_API_KEY, apiKey) + .put(Entity.json(/* language=JSON */ """ + { + "subject": "PACKAGE_URL", + "operator": "MATCHES", + "value": "pkg:maven/foo/bar" + } + """)); + assertThat(response.getStatus()).isEqualTo(403); + } + + @Test + public void testUpdateCondition() { + initializeWithPermissions(Permissions.POLICY_MANAGEMENT); + + final var policy = new Policy(); + policy.setName("foo"); + policy.setOperator(Operator.ANY); + policy.setViolationState(ViolationState.INFO); + qm.persist(policy); + + final var condition = new PolicyCondition(); + condition.setPolicy(policy); + condition.setSubject(PolicyCondition.Subject.PACKAGE_URL); + condition.setOperator(PolicyCondition.Operator.MATCHES); + condition.setValue("pkg:maven/foo/bar"); + qm.persist(condition); + + final Response response = jersey.target("%s/condition".formatted(V1_POLICY)) + .request() + .header(X_API_KEY, apiKey) + .post(Entity.json(/* language=JSON */ """ + { + "uuid": "%s", + "subject": "SEVERITY", + "operator": "IS", + "value": "HIGH" + } + """.formatted(condition.getUuid()))); + assertThat(response.getStatus()).isEqualTo(200); + assertThatJson(getPlainTextBody(response)) + .withMatcher("conditionUuid", equalTo(condition.getUuid().toString())) + .isEqualTo(/* language=JSON */ """ + { + "uuid": "${json-unit.matches:conditionUuid}", + "subject": "SEVERITY", + "operator": "IS", + "value": "HIGH" + } + """); + } + + @Test + public void testUpdateConditionWhenConditionDoesNotExist() { + initializeWithPermissions(Permissions.POLICY_MANAGEMENT); + + final Response response = jersey.target("%s/condition".formatted(V1_POLICY)) + .request() + .header(X_API_KEY, apiKey) + .post(Entity.json(/* language=JSON */ """ + { + "uuid": "8683b1db-96a3-4014-baf8-03e8cab8c647", + "subject": "SEVERITY", + "operator": "IS", + "value": "HIGH" + } + """)); + assertThat(response.getStatus()).isEqualTo(404); + assertThat(getPlainTextBody(response)).isEqualTo("The UUID of the policy condition could not be found."); + } + + @Test + public void testUpdateConditionWhenUnauthorized() { + final Response response = jersey.target("%s/condition".formatted(V1_POLICY)) + .request() + .header(X_API_KEY, apiKey) + .post(Entity.json(/* language=JSON */ """ + { + "uuid": "8683b1db-96a3-4014-baf8-03e8cab8c647", + "subject": "SEVERITY", + "operator": "IS", + "value": "HIGH" + } + """)); + assertThat(response.getStatus()).isEqualTo(403); + } + + @Test + public void testDeleteCondition() { + initializeWithPermissions(Permissions.POLICY_MANAGEMENT); + + final var policy = new Policy(); + policy.setName("foo"); + policy.setOperator(Operator.ANY); + policy.setViolationState(ViolationState.INFO); + qm.persist(policy); + + final var condition = new PolicyCondition(); + condition.setPolicy(policy); + condition.setSubject(PolicyCondition.Subject.PACKAGE_URL); + condition.setOperator(PolicyCondition.Operator.MATCHES); + condition.setValue("pkg:maven/foo/bar"); + qm.persist(condition); + + final Response response = jersey.target("%s/condition/%s".formatted(V1_POLICY, condition.getUuid())) + .request() + .header(X_API_KEY, apiKey) + .delete(); + assertThat(response.getStatus()).isEqualTo(204); + assertThat(getPlainTextBody(response)).isEmpty(); + + qm.getPersistenceManager().evictAll(); + assertThatExceptionOfType(JDOObjectNotFoundException.class) + .isThrownBy(() -> qm.getObjectById(PolicyCondition.class, condition.getId())); + } + + @Test + public void testDeleteConditionWhenConditionDoesNotExist() { + initializeWithPermissions(Permissions.POLICY_MANAGEMENT); + + final Response response = jersey.target("%s/condition/%s".formatted(V1_POLICY, UUID.randomUUID())) + .request() + .header(X_API_KEY, apiKey) + .delete(); + assertThat(response.getStatus()).isEqualTo(404); + assertThat(getPlainTextBody(response)).isEqualTo("The UUID of the policy condition could not be found."); + } + + @Test + public void testDeleteConditionWhenUnauthorized() { + final Response response = jersey.target("%s/condition/%s".formatted(V1_POLICY, UUID.randomUUID())) + .request() + .header(X_API_KEY, apiKey) + .delete(); + assertThat(response.getStatus()).isEqualTo(403); + } + +} \ No newline at end of file From 0e3e57623f68db6799d446964ac056f2df378c35 Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Sun, 22 Sep 2024 22:33:23 +0200 Subject: [PATCH 163/429] Add default team for badges Add a default team for viewing badges for new DBs. Signed-off-by: Kirill.Sybin --- docs/_docs/integrations/badges.md | 3 +++ .../persistence/DefaultObjectGenerator.java | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/docs/_docs/integrations/badges.md b/docs/_docs/integrations/badges.md index c6422859dc..a7055368cd 100644 --- a/docs/_docs/integrations/badges.md +++ b/docs/_docs/integrations/badges.md @@ -11,6 +11,9 @@ basis via permission. To enable badges for a team, activate the permission `VIEW_BADGES`. To deactivate badges, remove the permission. To retrieve a badge, use a team's API key either in the badge API header `X-API-Key` or in the URI parameter `apiKey`. +Dependency-Track ships with a default team "_Badge Viewers_" dedicated to badges that already has the necessary +permission and an API key. + > As badges are typically embedded in places that more people have access to than to Dependency-Track, the API key used > for the badge request should have minimal scope to prevent unintended access beyond that badge. Ideally, the API > key belongs to a single-purpose team, having just the `VIEW_BADGES` permission, with only one API key and access to diff --git a/src/main/java/org/dependencytrack/persistence/DefaultObjectGenerator.java b/src/main/java/org/dependencytrack/persistence/DefaultObjectGenerator.java index b02d08d0ef..1cc6729b57 100644 --- a/src/main/java/org/dependencytrack/persistence/DefaultObjectGenerator.java +++ b/src/main/java/org/dependencytrack/persistence/DefaultObjectGenerator.java @@ -151,6 +151,8 @@ private void loadDefaultPersonas() { final Team managers = qm.createTeam("Portfolio Managers", false); LOGGER.debug("Creating team: Automation"); final Team automation = qm.createTeam("Automation", true); + LOGGER.debug("Creating team: Badge Viewers"); + final Team badges = qm.createTeam("Badge Viewers", true); final List fullList = qm.getPermissions(); @@ -158,10 +160,12 @@ private void loadDefaultPersonas() { sysadmins.setPermissions(fullList); managers.setPermissions(getPortfolioManagersPermissions(fullList)); automation.setPermissions(getAutomationPermissions(fullList)); + badges.setPermissions(getBadgesPermissions(fullList)); qm.persist(sysadmins); qm.persist(managers); qm.persist(automation); + qm.persist(badges); LOGGER.debug("Adding admin user to System Administrators"); qm.addUserToTeam(admin, sysadmins); @@ -194,6 +198,16 @@ private List getAutomationPermissions(final List fullLis return permissions; } + private List getBadgesPermissions(final List fullList) { + final List permissions = new ArrayList<>(); + for (final Permission permission : fullList) { + if (permission.getName().equals(Permissions.Constants.VIEW_BADGES)) { + permissions.add(permission); + } + } + return permissions; + } + /** * Loads the default repositories */ From 014ec6c1acfe6f86be56eb15313483cfaa2cc426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Mon, 23 Sep 2024 08:45:55 +0200 Subject: [PATCH 164/429] Test for escaped Data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../publisher/AbstractPublisherTest.java | 14 +++++++++ .../publisher/SendMailPublisherTest.java | 31 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/test/java/org/dependencytrack/notification/publisher/AbstractPublisherTest.java b/src/test/java/org/dependencytrack/notification/publisher/AbstractPublisherTest.java index 1f46b1b45f..cba787320c 100644 --- a/src/test/java/org/dependencytrack/notification/publisher/AbstractPublisherTest.java +++ b/src/test/java/org/dependencytrack/notification/publisher/AbstractPublisherTest.java @@ -215,6 +215,20 @@ public void testInformWithProjectAuditChangeNotification() { .isThrownBy(() -> publisherInstance.inform(PublishContext.from(notification), notification, createConfig())); } + @Test + public void testInformWithEscapedData() { + final var notification = new Notification() + .scope(NotificationScope.SYSTEM) + .group(NotificationGroup.ANALYZER) + .title(NotificationConstants.Title.NOTIFICATION_TEST) + .content("! \" § $ % & / ( ) = ? \\ ' * Ö Ü Ä ®️") + .level(NotificationLevel.ERROR) + .timestamp(LocalDateTime.ofEpochSecond(66666, 666, ZoneOffset.UTC)); + + assertThatNoException() + .isThrownBy(() -> publisherInstance.inform(PublishContext.from(notification), notification, createConfig())); + } + private static Component createComponent(final Project project) { final var component = new Component(); component.setProject(project); diff --git a/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java b/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java index 192578f1af..1388ac4540 100644 --- a/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java +++ b/src/test/java/org/dependencytrack/notification/publisher/SendMailPublisherTest.java @@ -395,6 +395,37 @@ public void testInformWithProjectAuditChangeNotification() { }); } + @Override + public void testInformWithEscapedData() { + super.testInformWithEscapedData(); + + assertThat(greenMail.getReceivedMessages()).satisfiesExactly(message -> { + assertThat(message.getSubject()).isEqualTo("[Dependency-Track] Notification Test"); + assertThat(message.getContent()).isInstanceOf(MimeMultipart.class); + final MimeMultipart content = (MimeMultipart) message.getContent(); + assertThat(content.getCount()).isEqualTo(1); + assertThat(content.getBodyPart(0)).isInstanceOf(MimeBodyPart.class); + assertThat((String) content.getBodyPart(0).getContent()).isEqualToIgnoringNewLines(""" + Notification Test + + -------------------------------------------------------------------------------- + + Level: ERROR + Scope: SYSTEM + Group: ANALYZER + + -------------------------------------------------------------------------------- + + ! " § $ % & / ( ) = ? \\ ' * Ö Ü Ä ®️ + + -------------------------------------------------------------------------------- + + 1970-01-01T18:31:06.000000666 + """); + }); + + } + @Override JsonObjectBuilder extraConfig() { return super.extraConfig() From 1caf0d498e5df769f5cd3197abf3fbc4b72edac3 Mon Sep 17 00:00:00 2001 From: Richard Bickert Date: Mon, 23 Sep 2024 08:57:19 +0200 Subject: [PATCH 165/429] Fix parameters in PolicyViolationResource Signed-off-by: Richard Bickert --- .../resources/v1/PolicyViolationResource.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java b/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java index efc4be5fb5..d7dc7e284c 100644 --- a/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/PolicyViolationResource.java @@ -86,23 +86,23 @@ public class PolicyViolationResource extends AlpineResource { @PermissionRequired(Permissions.Constants.VIEW_POLICY_VIOLATION) public Response getViolations(@Parameter(description = "Optionally includes suppressed violations") @QueryParam("suppressed") boolean suppressed, - @PathParam(value = "Optionally includes inactive projects") + @Parameter(description = "Optionally includes inactive projects") @QueryParam("showInactive") boolean showInactive, - @PathParam(value = "Filter by violation state") + @Parameter(description = "Filter by violation state") @QueryParam("violationState") String violationState, - @PathParam(value = "Filter by risk type") + @Parameter(description = "Filter by risk type") @QueryParam("riskType") String riskType, - @PathParam(value = "Filter by policy") + @Parameter(description = "Filter by policy") @QueryParam("policy") String policy, - @PathParam(value = "Filter by analysis state") + @Parameter(description = "Filter by analysis state") @QueryParam("analysisState") String analysisState, - @PathParam(value = "Filter occurred on from") + @Parameter(description = "Filter occurred on from") @QueryParam("occurredOnDateFrom") String occurredOnDateFrom, - @PathParam(value = "Filter occurred on to") + @Parameter(description = "Filter occurred on to") @QueryParam("occurredOnDateTo") String occurredOnDateTo, - @PathParam(value = "Filter the text input in these fields") + @Parameter(description = "Filter the text input in these fields") @QueryParam("textSearchField") String textSearchField, - @PathParam(value = "Filter by this text input") + @Parameter(description = "Filter by this text input") @QueryParam("textSearchInput") String textSearchInput) { try (QueryManager qm = new QueryManager(getAlpineRequest())) { Map filters = new HashMap<>(); From 656d38a257506d6cf52dbc466c30d69d3e462fd2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 08:26:15 +0000 Subject: [PATCH 166/429] Bump github/codeql-action from 3.26.7 to 3.26.8 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.7 to 3.26.8. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/8214744c546c1e5c8f03dde8fab3a7353211988d...294a9d92911152fe08befb9ec03e240add280cb3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index e15f90fb7b..e1f3dc92dd 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -145,6 +145,6 @@ jobs: - name: Upload Trivy Scan Results to GitHub Security Tab if: ${{ inputs.publish-container }} - uses: github/codeql-action/upload-sarif@8214744c546c1e5c8f03dde8fab3a7353211988d # tag=v3.26.7 + uses: github/codeql-action/upload-sarif@294a9d92911152fe08befb9ec03e240add280cb3 # tag=v3.26.8 with: sarif_file: 'trivy-results.sarif' From 11b005d7d98aa46159e5cb0b850d471bc4c0f1aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Tue, 24 Sep 2024 16:54:03 +0200 Subject: [PATCH 167/429] Excluded accessTeams for json encoding and updated tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../org/dependencytrack/model/Project.java | 4 +- .../resources/v1/ProjectResourceTest.java | 70 ++++++------------- 2 files changed, 25 insertions(+), 49 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/Project.java b/src/main/java/org/dependencytrack/model/Project.java index 5d10072ee4..01148e4e5d 100644 --- a/src/main/java/org/dependencytrack/model/Project.java +++ b/src/main/java/org/dependencytrack/model/Project.java @@ -24,6 +24,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonIncludeProperties; +import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; @@ -273,7 +274,6 @@ public enum FetchGroup { @Join(column = "PROJECT_ID") @Element(column = "TEAM_ID") @Order(extensions = @Extension(vendorName = "datanucleus", key = "list-ordering", value = "name ASC")) - @JsonInclude(value = JsonInclude.Include.NON_EMPTY) private List accessTeams; @Persistent(defaultFetchGroup = "true") @@ -537,10 +537,12 @@ public void setVersions(List versions) { this.versions = versions; } + @JsonIgnore public List getAccessTeams() { return accessTeams; } + @JsonSetter public void setAccessTeams(List accessTeams) { this.accessTeams = accessTeams; } diff --git a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java index 039f4ec4fc..76b3fe5ac7 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java @@ -96,7 +96,7 @@ public void after() throws Exception { super.after(); } - public void setUpUser(boolean isAdmin, boolean isRequired) { + public JsonObjectBuilder setUpEnvironment(boolean isAdmin, boolean isRequired, String name, Team team1) { testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); jwt = new JsonWebToken().createToken(testUser); qm.addUserToTeam(testUser, team); @@ -119,6 +119,13 @@ public void setUpUser(boolean isAdmin, boolean isRequired) { ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), null); } + final JsonObjectBuilder jsonProject = Json.createObjectBuilder() + .add("name", name).add("classifier", "CONTAINER").addNull("parent").add("active", true).add("tags", Json.createArrayBuilder()); + if (team1 != null) { + final JsonObject jsonTeam = Json.createObjectBuilder().add("uuid", team1.getUuid().toString()).build(); + jsonProject.add("accessTeams", Json.createArrayBuilder().add(jsonTeam).build()); + } + return jsonProject; } @Test @@ -512,119 +519,86 @@ public void createProjectEmptyTest() { @Test public void createProjectWithExistingTeamRequiredTest() { - setUpUser(false, true); Team AllowedTeam = qm.createTeam("AllowedTeam", false); - Project project = new Project(); - project.setName("ProjectWithExistingTeamRequired"); + final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(false, true, "ProjectWithExistingTeamRequired", AllowedTeam); qm.addUserToTeam(testUser, AllowedTeam); - final JsonObject jsonTeam = Json.createObjectBuilder().add("uuid", AllowedTeam.getUuid().toString()).build(); - final JsonObjectBuilder requestBodyBuilder = Json.createObjectBuilder() - .add("name", project.getName()).add("classifier", "CONTAINER").addNull("parent").add("active", true).add("tags", Json.createArrayBuilder()) - .add("accessTeams", Json.createArrayBuilder().add(jsonTeam).build()); Response response = jersey.target(V1_PROJECT) .request() .header("Authorization", "Bearer " + jwt) .put(Entity.json(requestBodyBuilder.build().toString())); Assert.assertEquals(201, response.getStatus()); JsonObject returnedProject = parseJsonObject(response); - JsonArray teams = returnedProject.getJsonArray("accessTeams"); - Assert.assertEquals(teams.size(), 1); - Assert.assertEquals(AllowedTeam.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); } @Test public void createProjectWithoutExistingTeamRequiredTest() { - setUpUser(false, true); - Project project = new Project(); - project.setName("ProjectWithoutExistingTeamRequired"); + final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(false, true, "ProjectWithoutExistingTeamRequired", null); Response response = jersey.target(V1_PROJECT) .request() .header("Authorization", "Bearer " + jwt) - .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + .put(Entity.json(requestBodyBuilder.build().toString())); Assert.assertEquals(422, response.getStatus(), 0); } @Test public void createProjectWithNotAllowedExistingTeamTest() { - setUpUser(false, true); Team notAllowedTeam = qm.createTeam("NotAllowedTeam", false); - Project project = new Project(); - project.setName("ProjectWithNotAllowedExistingTeam"); - project.addAccessTeam(notAllowedTeam); + final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(false, true, "ProjectWithNotAllowedExistingTeam", notAllowedTeam); Response response = jersey.target(V1_PROJECT) .request() .header("Authorization", "Bearer " + jwt) - .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + .put(Entity.json(requestBodyBuilder.build().toString())); Assert.assertEquals(403, response.getStatus()); } @Test public void createProjectWithNotAllowedExistingTeamAdminTest() { - setUpUser(true, true); - Team notAllowedTeam = qm.createTeam("NotAllowedTeam", false); - Project project = new Project(); - project.setName("ProjectWithNotAllowedExistingTeam"); - project.addAccessTeam(notAllowedTeam); + Team AllowedTeam = qm.createTeam("NotAllowedTeam", false); + final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(false, true, "ProjectWithNotAllowedExistingTeam", AllowedTeam); + qm.addUserToTeam(testUser, AllowedTeam); Response response = jersey.target(V1_PROJECT) .request() .header("Authorization", "Bearer " + jwt) - .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + .put(Entity.json(requestBodyBuilder.build().toString())); Assert.assertEquals(201, response.getStatus()); JsonObject returnedProject = parseJsonObject(response); - JsonArray teams = returnedProject.getJsonArray("accessTeams"); - Assert.assertEquals(teams.size(), 1); - Assert.assertEquals(notAllowedTeam.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); } @Test public void createProjectWithNotExistingTeamNoAdminTest() { - setUpUser(false, true); Team notAllowedTeam = new Team(); notAllowedTeam.setUuid(new UUID(1, 1)); notAllowedTeam.setName("NotAllowedTeam"); - Project project = new Project(); - project.addAccessTeam(notAllowedTeam); - project.setName("ProjectWithNotAllowedExistingTeam"); + final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(false, true, "ProjectWithNotAllowedExistingTeam", notAllowedTeam); Response response = jersey.target(V1_PROJECT) .request() .header("Authorization", "Bearer " + jwt) - .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + .put(Entity.json(requestBodyBuilder.build().toString())); Assert.assertEquals(403, response.getStatus()); } @Test public void createProjectWithNotExistingTeamTest() { - setUpUser(true, false); Team notAllowedTeam = new Team(); notAllowedTeam.setUuid(new UUID(1, 1)); notAllowedTeam.setName("NotAllowedTeam"); - Project project = new Project(); - project.addAccessTeam(notAllowedTeam); - project.setName("ProjectWithNotExistingTeam"); + final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(true, true, "ProjectWithNotAllowedExistingTeam", notAllowedTeam); Response response = jersey.target(V1_PROJECT) .request() .header("Authorization", "Bearer " + jwt) - .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + .put(Entity.json(requestBodyBuilder.build().toString())); Assert.assertEquals(404, response.getStatus()); } @Test public void createProjectWithApiKeyTest() { - Project project = new Project(); - project.setName("ProjectWithNotExistingTeam"); - final JsonObject jsonTeam = Json.createObjectBuilder().add("uuid", team.getUuid().toString()).build(); - final JsonObjectBuilder requestBodyBuilder = Json.createObjectBuilder() - .add("name", project.getName()).add("classifier", "CONTAINER").addNull("parent").add("active", true).add("tags", Json.createArrayBuilder()) - .add("accessTeams", Json.createArrayBuilder().add(jsonTeam).build()); + final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(false, true, "ProjectWithNotAllowedExistingTeam", team); Response response = jersey.target(V1_PROJECT) .request() .header(X_API_KEY, apiKey) .put(Entity.json(requestBodyBuilder.build().toString())); Assert.assertEquals(201, response.getStatus()); JsonObject returnedProject = parseJsonObject(response); - JsonArray teams = returnedProject.getJsonArray("accessTeams"); - Assert.assertEquals(teams.size(), 1); - Assert.assertEquals(team.getUuid().toString(), teams.getFirst().asJsonObject().getString("uuid")); } @Test From 3eac1d664f51b06331f6fd7c4ba39bd33ce706e7 Mon Sep 17 00:00:00 2001 From: nscuro Date: Tue, 24 Sep 2024 22:39:50 +0200 Subject: [PATCH 168/429] Fix `directDependencies` of cloned projects referring to original component UUIDs Fixes #4153 Signed-off-by: nscuro --- .../persistence/ComponentQueryManager.java | 1 + .../persistence/ProjectQueryManager.java | 85 ++++++++++++- .../resources/v1/ProjectResourceTest.java | 114 ++++++++++++------ 3 files changed, 159 insertions(+), 41 deletions(-) diff --git a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java index a163599617..5ed0c17f41 100644 --- a/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ComponentQueryManager.java @@ -379,6 +379,7 @@ public Component cloneComponent(Component sourceComponent, Project destinationPr component.setResolvedLicense(sourceComponent.getResolvedLicense()); component.setAuthors(sourceComponent.getAuthors()); component.setSupplier(sourceComponent.getSupplier()); + component.setDirectDependencies(sourceComponent.getDirectDependencies()); // TODO Add support for parent component and children components component.setProject(destinationProject); return createComponent(component, commitIndex); diff --git a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java index 91ac6fba82..89a2aa6285 100644 --- a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java @@ -28,6 +28,9 @@ import alpine.notification.NotificationLevel; import alpine.persistence.PaginatedResult; import alpine.resources.AlpineRequest; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.json.JsonMapper; import com.github.packageurl.PackageURL; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -58,12 +61,15 @@ import javax.jdo.Query; import javax.jdo.metadata.MemberMetadata; import javax.jdo.metadata.TypeMetadata; +import java.io.IOException; import java.security.Principal; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; final class ProjectQueryManager extends QueryManager implements IQueryManager { @@ -512,6 +518,8 @@ public Project clone( final boolean includeACL, final boolean includePolicyViolations ) { + final var jsonMapper = new JsonMapper(); + final Project clonedProject = callInTransaction(() -> { final Project source = getObjectByUuid(Project.class, from, Project.FetchGroup.ALL.name()); if (source == null) { @@ -539,7 +547,7 @@ public Project clone( project.setCpe(source.getCpe()); project.setPurl(source.getPurl()); project.setSwidTagId(source.getSwidTagId()); - if (includeComponents && includeServices) { + if (source.getDirectDependencies() != null && includeComponents && includeServices) { project.setDirectDependencies(source.getDirectDependencies()); } project.setParent(source.getParent()); @@ -573,7 +581,17 @@ public Project clone( } } - final Map clonedComponents = new HashMap<>(); + final var projectDirectDepsSourceComponentUuids = new HashSet(); + if (project.getDirectDependencies() != null) { + projectDirectDepsSourceComponentUuids.addAll( + parseDirectDependenciesUuids(jsonMapper, project.getDirectDependencies())); + } + + final var clonedComponentById = new HashMap(); + final var clonedComponentBySourceComponentId = new HashMap(); + final var directDepsSourceComponentUuidsByClonedComponentId = new HashMap>(); + final var clonedComponentUuidBySourceComponentUuid = new HashMap(); + if (includeComponents) { final List sourceComponents = getAllComponents(source); if (sourceComponents != null) { @@ -584,11 +602,44 @@ public Project clone( final FindingAttribution sourceAttribution = this.getFindingAttribution(vuln, sourceComponent); this.addVulnerability(vuln, clonedComponent, sourceAttribution.getAnalyzerIdentity(), sourceAttribution.getAlternateIdentifier(), sourceAttribution.getReferenceUrl(), sourceAttribution.getAttributedOn()); } - clonedComponents.put(sourceComponent.getId(), clonedComponent); + + clonedComponentById.put(clonedComponent.getId(), clonedComponent); + clonedComponentBySourceComponentId.put(sourceComponent.getId(), clonedComponent); + clonedComponentUuidBySourceComponentUuid.put(sourceComponent.getUuid(), clonedComponent.getUuid()); + + if (clonedComponent.getDirectDependencies() != null) { + final Set directDepsUuids = parseDirectDependenciesUuids(jsonMapper, clonedComponent.getDirectDependencies()); + if (!directDepsUuids.isEmpty()) { + directDepsSourceComponentUuidsByClonedComponentId.put(clonedComponent.getId(), directDepsUuids); + } + } } } } + if (!projectDirectDepsSourceComponentUuids.isEmpty()) { + String directDependencies = project.getDirectDependencies(); + for (final UUID sourceComponentUuid : projectDirectDepsSourceComponentUuids) { + final UUID clonedComponentUuid = clonedComponentUuidBySourceComponentUuid.get(sourceComponentUuid); + directDependencies = directDependencies.replace(sourceComponentUuid.toString(), clonedComponentUuid.toString()); + } + + project.setDirectDependencies(directDependencies); + } + + for (final long componentId : directDepsSourceComponentUuidsByClonedComponentId.keySet()) { + final Component component = clonedComponentById.get(componentId); + final Set sourceComponentUuids = directDepsSourceComponentUuidsByClonedComponentId.get(componentId); + + String directDependencies = component.getDirectDependencies(); + for (final UUID sourceComponentUuid : sourceComponentUuids) { + final UUID clonedComponentUuid = clonedComponentUuidBySourceComponentUuid.get(sourceComponentUuid); + directDependencies = directDependencies.replace(sourceComponentUuid.toString(), clonedComponentUuid.toString()); + } + + component.setDirectDependencies(directDependencies); + } + if (includeServices) { final List sourceServices = getAllServiceComponents(source); if (sourceServices != null) { @@ -604,7 +655,7 @@ public Project clone( for (final Analysis sourceAnalysis : analyses) { Analysis analysis = new Analysis(); analysis.setAnalysisState(sourceAnalysis.getAnalysisState()); - final Component clonedComponent = clonedComponents.get(sourceAnalysis.getComponent().getId()); + final Component clonedComponent = clonedComponentBySourceComponentId.get(sourceAnalysis.getComponent().getId()); if (clonedComponent == null) { break; } @@ -642,7 +693,7 @@ public Project clone( final List sourcePolicyViolations = getAllPolicyViolations(source); if (sourcePolicyViolations != null) { for (final PolicyViolation policyViolation : sourcePolicyViolations) { - final Component destinationComponent = clonedComponents.get(policyViolation.getComponent().getId()); + final Component destinationComponent = clonedComponentBySourceComponentId.get(policyViolation.getComponent().getId()); final PolicyViolation clonedPolicyViolation = clonePolicyViolation(policyViolation, destinationComponent); persist(clonedPolicyViolation); } @@ -657,6 +708,30 @@ public Project clone( return clonedProject; } + private static Set parseDirectDependenciesUuids( + final JsonMapper jsonMapper, + final String directDependencies) throws IOException { + final var uuids = new HashSet(); + try (final JsonParser jsonParser = jsonMapper.createParser(directDependencies)) { + JsonToken currentToken = jsonParser.nextToken(); + if (currentToken != JsonToken.START_ARRAY) { + throw new IllegalArgumentException(""" + Expected directDependencies to be a JSON array, \ + but encountered token: %s""".formatted(currentToken)); + } + + while (jsonParser.nextToken() != null) { + if (jsonParser.currentToken() == JsonToken.FIELD_NAME + && "uuid".equals(jsonParser.currentName()) + && jsonParser.nextToken() == JsonToken.VALUE_STRING) { + uuids.add(UUID.fromString(jsonParser.getValueAsString())); + } + } + } + + return uuids; + } + /** * Deletes a Project and all objects dependant on the project. * @param project the Project to delete diff --git a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java index 76b3fe5ac7..02566180e0 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java @@ -22,19 +22,11 @@ import alpine.event.framework.EventService; import alpine.model.IConfigProperty.PropertyType; import alpine.model.ManagedUser; -import alpine.model.Team; import alpine.model.Permission; +import alpine.model.Team; import alpine.server.auth.JsonWebToken; import alpine.server.filters.ApiFilter; import alpine.server.filters.AuthenticationFilter; -import jakarta.json.Json; -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; -import jakarta.json.JsonObjectBuilder; -import jakarta.ws.rs.HttpMethod; -import jakarta.ws.rs.client.Entity; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; import org.cyclonedx.model.ExternalReference.Type; import org.dependencytrack.JerseyTestRule; import org.dependencytrack.ResourceTest; @@ -44,6 +36,7 @@ import org.dependencytrack.model.AnalysisResponse; import org.dependencytrack.model.AnalysisState; import org.dependencytrack.model.Component; +import org.dependencytrack.model.ComponentIdentity; import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.ExternalReference; import org.dependencytrack.model.OrganizationalContact; @@ -60,11 +53,20 @@ import org.glassfish.jersey.client.HttpUrlConnectorProvider; import org.glassfish.jersey.server.ResourceConfig; import org.hamcrest.CoreMatchers; +import org.json.JSONArray; import org.junit.After; import org.junit.Assert; import org.junit.ClassRule; import org.junit.Test; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import jakarta.ws.rs.HttpMethod; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; @@ -78,6 +80,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; public class ProjectResourceTest extends ResourceTest { private ManagedUser testUser; @@ -1107,12 +1110,18 @@ public void cloneProjectTest() { final var componentSupplier = new OrganizationalEntity(); componentSupplier.setName("componentSupplier"); - final var component = new Component(); - component.setProject(project); - component.setName("acme-lib"); - component.setVersion("2.0.0"); - component.setSupplier(componentSupplier); - qm.persist(component); + final var componentA = new Component(); + componentA.setProject(project); + componentA.setName("acme-lib-a"); + componentA.setVersion("2.0.0"); + componentA.setSupplier(componentSupplier); + qm.persist(componentA); + + final var componentB = new Component(); + componentB.setProject(project); + componentB.setName("acme-lib-b"); + componentB.setVersion("2.1.0"); + qm.persist(componentB); final var service = new ServiceComponent(); service.setProject(project); @@ -1120,19 +1129,22 @@ public void cloneProjectTest() { service.setVersion("3.0.0"); qm.persist(service); + project.setDirectDependencies(new JSONArray().put(new ComponentIdentity(componentA).toJSON()).toString()); + componentA.setDirectDependencies(new JSONArray().put(new ComponentIdentity(componentB).toJSON()).toString()); + final var vuln = new Vulnerability(); vuln.setVulnId("INT-123"); vuln.setSource(Vulnerability.Source.INTERNAL); qm.persist(vuln); - qm.addVulnerability(vuln, component, AnalyzerIdentity.INTERNAL_ANALYZER); - final Analysis analysis = qm.makeAnalysis(component, vuln, AnalysisState.NOT_AFFECTED, + qm.addVulnerability(vuln, componentA, AnalyzerIdentity.INTERNAL_ANALYZER); + final Analysis analysis = qm.makeAnalysis(componentA, vuln, AnalysisState.NOT_AFFECTED, AnalysisJustification.REQUIRES_ENVIRONMENT, AnalysisResponse.WILL_NOT_FIX, "details", false); qm.makeAnalysisComment(analysis, "comment", "commenter"); final Response response = jersey.target("%s/clone".formatted(V1_PROJECT)).request() .header(X_API_KEY, apiKey) - .put(Entity.json(""" + .put(Entity.json(/* language=JSON */ """ { "project": "%s", "version": "1.1.0", @@ -1163,6 +1175,18 @@ public void cloneProjectTest() { assertThat(clonedProject.getManufacturer()).isNotNull(); assertThat(clonedProject.getManufacturer().getName()).isEqualTo("projectManufacturer"); assertThat(clonedProject.getAccessTeams()).containsOnly(team); + assertThatJson(clonedProject.getDirectDependencies()) + .withMatcher("notSourceComponentUuid", not(equalTo(componentA.getUuid().toString()))) + .isEqualTo(/* language=JSON */ """ + [ + { + "objectType": "COMPONENT", + "uuid": "${json-unit.matches:notSourceComponentUuid}", + "name": "acme-lib-a", + "version": "2.0.0" + } + ] + """); final List clonedProperties = qm.getProjectProperties(clonedProject); assertThat(clonedProperties).satisfiesExactly(clonedProperty -> { @@ -1184,24 +1208,42 @@ public void cloneProjectTest() { assertThat(clonedMetadata.getSupplier()) .satisfies(entity -> assertThat(entity.getName()).isEqualTo("metadataSupplier")); - assertThat(qm.getAllComponents(clonedProject)).satisfiesExactly(clonedComponent -> { - assertThat(clonedComponent.getUuid()).isNotEqualTo(component.getUuid()); - assertThat(clonedComponent.getName()).isEqualTo("acme-lib"); - assertThat(clonedComponent.getVersion()).isEqualTo("2.0.0"); - assertThat(clonedComponent.getSupplier()).isNotNull(); - assertThat(clonedComponent.getSupplier().getName()).isEqualTo("componentSupplier"); - - assertThat(qm.getAllVulnerabilities(clonedComponent)).containsOnly(vuln); - - assertThat(qm.getAnalysis(clonedComponent, vuln)).satisfies(clonedAnalysis -> { - assertThat(clonedAnalysis.getId()).isNotEqualTo(analysis.getId()); - assertThat(clonedAnalysis.getAnalysisState()).isEqualTo(AnalysisState.NOT_AFFECTED); - assertThat(clonedAnalysis.getAnalysisJustification()).isEqualTo(AnalysisJustification.REQUIRES_ENVIRONMENT); - assertThat(clonedAnalysis.getAnalysisResponse()).isEqualTo(AnalysisResponse.WILL_NOT_FIX); - assertThat(clonedAnalysis.getAnalysisDetails()).isEqualTo("details"); - assertThat(clonedAnalysis.isSuppressed()).isFalse(); - }); - }); + assertThat(qm.getAllComponents(clonedProject)).satisfiesExactlyInAnyOrder( + clonedComponent -> { + assertThat(clonedComponent.getUuid()).isNotEqualTo(componentA.getUuid()); + assertThat(clonedComponent.getName()).isEqualTo("acme-lib-a"); + assertThat(clonedComponent.getVersion()).isEqualTo("2.0.0"); + assertThat(clonedComponent.getSupplier()).isNotNull(); + assertThat(clonedComponent.getSupplier().getName()).isEqualTo("componentSupplier"); + assertThatJson(clonedComponent.getDirectDependencies()) + .withMatcher("notSourceComponentUuid", not(equalTo(componentB.getUuid().toString()))) + .isEqualTo(/* language=JSON */ """ + [ + { + "objectType": "COMPONENT", + "uuid": "${json-unit.matches:notSourceComponentUuid}", + "name": "acme-lib-b", + "version": "2.1.0" + } + ] + """); + + assertThat(qm.getAllVulnerabilities(clonedComponent)).containsOnly(vuln); + + assertThat(qm.getAnalysis(clonedComponent, vuln)).satisfies(clonedAnalysis -> { + assertThat(clonedAnalysis.getId()).isNotEqualTo(analysis.getId()); + assertThat(clonedAnalysis.getAnalysisState()).isEqualTo(AnalysisState.NOT_AFFECTED); + assertThat(clonedAnalysis.getAnalysisJustification()).isEqualTo(AnalysisJustification.REQUIRES_ENVIRONMENT); + assertThat(clonedAnalysis.getAnalysisResponse()).isEqualTo(AnalysisResponse.WILL_NOT_FIX); + assertThat(clonedAnalysis.getAnalysisDetails()).isEqualTo("details"); + assertThat(clonedAnalysis.isSuppressed()).isFalse(); + }); + }, + clonedComponent -> { + assertThat(clonedComponent.getUuid()).isNotEqualTo(componentA.getUuid()); + assertThat(clonedComponent.getName()).isEqualTo("acme-lib-b"); + assertThat(clonedComponent.getVersion()).isEqualTo("2.1.0"); + }); assertThat(qm.getAllServiceComponents(clonedProject)).satisfiesExactly(clonedService -> { assertThat(clonedService.getUuid()).isNotEqualTo(service.getUuid()); From 7764d9e1e8689e2c2b3c02e845a60d7ecb645ef5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 08:39:03 +0000 Subject: [PATCH 169/429] Bump lib.lucene.version from 8.11.3 to 8.11.4 Bumps `lib.lucene.version` from 8.11.3 to 8.11.4. Updates `org.apache.lucene:lucene-core` from 8.11.3 to 8.11.4 Updates `org.apache.lucene:lucene-analyzers-common` from 8.11.3 to 8.11.4 Updates `org.apache.lucene:lucene-queryparser` from 8.11.3 to 8.11.4 Updates `org.apache.lucene:lucene-queries` from 8.11.3 to 8.11.4 Updates `org.apache.lucene:lucene-sandbox` from 8.11.3 to 8.11.4 --- updated-dependencies: - dependency-name: org.apache.lucene:lucene-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.lucene:lucene-analyzers-common dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.lucene:lucene-queryparser dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.lucene:lucene-queries dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.apache.lucene:lucene-sandbox dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 30a383ecd7..c706924213 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ 20240303 3.4.1 4.13.2 - 8.11.3 + 8.11.4 3.9.9 5.15.0 6.2.0 From 397dd217ad696d2bddc3fe15b477edfb6f91b4cf Mon Sep 17 00:00:00 2001 From: nscuro Date: Wed, 25 Sep 2024 17:08:43 +0200 Subject: [PATCH 170/429] Fix CPE not being imported from CycloneDX `metadata.component` Fixes #4173 Signed-off-by: nscuro --- .../parser/cyclonedx/util/ModelConverter.java | 9 +++++---- .../dependencytrack/tasks/BomUploadProcessingTask.java | 1 + .../tasks/BomUploadProcessingTaskTest.java | 3 +++ src/test/resources/unit/bom-1.xml | 5 +++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java index fcf75410b8..347b4c5fbd 100644 --- a/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java +++ b/src/main/java/org/dependencytrack/parser/cyclonedx/util/ModelConverter.java @@ -23,6 +23,10 @@ import alpine.model.IConfigProperty.PropertyType; import com.github.packageurl.MalformedPackageURLException; import com.github.packageurl.PackageURL; +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonValue; import org.apache.commons.collections4.MultiValuedMap; import org.apache.commons.collections4.multimap.HashSetValuedHashMap; import org.apache.commons.lang3.StringUtils; @@ -57,10 +61,6 @@ import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.util.VulnerabilityUtil; -import jakarta.json.Json; -import jakarta.json.JsonArray; -import jakarta.json.JsonObject; -import jakarta.json.JsonValue; import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; @@ -124,6 +124,7 @@ public static Project convertToProject(final org.cyclonedx.model.Component cdxCo project.setName(trimToNull(cdxComponent.getName())); project.setVersion(trimToNull(cdxComponent.getVersion())); project.setDescription(trimToNull(cdxComponent.getDescription())); + project.setCpe(trimToNull(cdxComponent.getCpe())); project.setExternalReferences(convertExternalReferences(cdxComponent.getExternalReferences())); List contacts = new ArrayList<>(); diff --git a/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java b/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java index 210dc6d391..cc234968af 100644 --- a/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java +++ b/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java @@ -335,6 +335,7 @@ private Project processProject( // changed |= applyIfChanged(project, metadataComponent, Project::getVersion, project::setVersion); // changed |= applyIfChanged(project, metadataComponent, Project::getDescription, project::setDescription); hasChanged |= applyIfChanged(persistentProject, project, Project::getExternalReferences, persistentProject::setExternalReferences); + hasChanged |= applyIfChanged(persistentProject, project, Project::getCpe, persistentProject::setCpe); hasChanged |= applyIfChanged(persistentProject, project, Project::getPurl, persistentProject::setPurl); hasChanged |= applyIfChanged(persistentProject, project, Project::getSwidTagId, persistentProject::setSwidTagId); } diff --git a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java index 6384609b6a..84a5c9daf7 100644 --- a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java @@ -167,6 +167,9 @@ public void informTest() throws Exception { qm.getPersistenceManager().refresh(project); assertThat(project.getClassifier()).isEqualTo(Classifier.APPLICATION); + assertThat(project.getCpe()).isEqualTo("cpe:2.3:a:acme:example:1.0.0:*:*:*:*:*:*:*"); + assertThat(project.getPurl()).asString().isEqualTo("pkg:maven/com.acme/example@1.0.0"); + assertThat(project.getSwidTagId()).isEqualTo("swidgen-242eb18a-503e-ca37-393b-cf156ef09691_9.1.1"); assertThat(project.getLastBomImport()).isNotNull(); assertThat(project.getExternalReferences()).isNotNull(); assertThat(project.getExternalReferences()).hasSize(4); diff --git a/src/test/resources/unit/bom-1.xml b/src/test/resources/unit/bom-1.xml index 3349fc1df3..1a91de2354 100644 --- a/src/test/resources/unit/bom-1.xml +++ b/src/test/resources/unit/bom-1.xml @@ -20,6 +20,11 @@ DependencyTrack Acme example + cpe:2.3:a:acme:example:1.0.0:*:*:*:*:*:*:* + pkg:maven/com.acme/example@1.0.0 + + PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiID8+CjxTb2Z0d2FyZUlkZW50aXR5IHhtbDpsYW5nPSJFTiIgbmFtZT0iQWNtZSBBcHBsaWNhdGlvbiIgdmVyc2lvbj0iOS4xLjEiIAogdmVyc2lvblNjaGVtZT0ibXVsdGlwYXJ0bnVtZXJpYyIgCiB0YWdJZD0ic3dpZGdlbi1iNTk1MWFjOS00MmMwLWYzODItM2YxZS1iYzdhMmE0NDk3Y2JfOS4xLjEiIAogeG1sbnM9Imh0dHA6Ly9zdGFuZGFyZHMuaXNvLm9yZy9pc28vMTk3NzAvLTIvMjAxNS9zY2hlbWEueHNkIj4gCiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiAKIHhzaTpzY2hlbWFMb2NhdGlvbj0iaHR0cDovL3N0YW5kYXJkcy5pc28ub3JnL2lzby8xOTc3MC8tMi8yMDE1LWN1cnJlbnQvc2NoZW1hLnhzZCBzY2hlbWEueHNkIiA+CiAgPE1ldGEgZ2VuZXJhdG9yPSJTV0lEIFRhZyBPbmxpbmUgR2VuZXJhdG9yIHYwLjEiIC8+IAogIDxFbnRpdHkgbmFtZT0iQWNtZSwgSW5jLiIgcmVnaWQ9ImV4YW1wbGUuY29tIiByb2xlPSJ0YWdDcmVhdG9yIiAvPiAKPC9Tb2Z0d2FyZUlkZW50aXR5Pg== + https://acme.example From 125cf89dad8152b78bd7d1a7b1ab0cddbb641380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Thu, 26 Sep 2024 13:58:01 +0200 Subject: [PATCH 171/429] Visible Endpoint returns only Visible Teams(name, uuid) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../resources/v1/TeamResource.java | 9 +++++-- .../resources/v1/vo/VisibleTeams.java | 24 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/dependencytrack/resources/v1/vo/VisibleTeams.java diff --git a/src/main/java/org/dependencytrack/resources/v1/TeamResource.java b/src/main/java/org/dependencytrack/resources/v1/TeamResource.java index 469a55a258..4caf19d199 100644 --- a/src/main/java/org/dependencytrack/resources/v1/TeamResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/TeamResource.java @@ -40,6 +40,7 @@ import org.dependencytrack.model.validation.ValidUuid; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.resources.v1.vo.TeamSelfResponse; +import org.dependencytrack.resources.v1.vo.VisibleTeams; import org.owasp.security.logging.SecurityMarkers; import jakarta.validation.Validator; @@ -228,7 +229,7 @@ public Response deleteTeam(Team jsonTeam) { @Produces(MediaType.APPLICATION_JSON) @Operation(summary = "Returns a list of Teams that are visible", description = "

        ") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "The Visible Teams", content = @Content(array = @ArraySchema(schema = @Schema(implementation = Team.class)))), + @ApiResponse(responseCode = "200", description = "The Visible Teams", content = @Content(array = @ArraySchema(schema = @Schema(implementation = VisibleTeams.class)))), @ApiResponse(responseCode = "401", description = "Unauthorized") }) public Response availableTeams() { @@ -246,7 +247,11 @@ public Response availableTeams() { } } - return Response.ok(teams).build(); + List response = new ArrayList(); + for (Team team : teams) { + response.add(new VisibleTeams(team.getName(), team.getUuid())); + } + return Response.ok(response).build(); } } diff --git a/src/main/java/org/dependencytrack/resources/v1/vo/VisibleTeams.java b/src/main/java/org/dependencytrack/resources/v1/vo/VisibleTeams.java new file mode 100644 index 0000000000..465bde00cc --- /dev/null +++ b/src/main/java/org/dependencytrack/resources/v1/vo/VisibleTeams.java @@ -0,0 +1,24 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.resources.v1.vo; + +import java.util.UUID; + +public record VisibleTeams(String name, UUID uuid) { +} From a4c7698bf458f979ef82fbb2fec9b72aeee61657 Mon Sep 17 00:00:00 2001 From: Bilel MEDIMEGH Date: Thu, 26 Sep 2024 14:12:39 +0200 Subject: [PATCH 172/429] Fix missing parenthesis in documentation Signed-off-by: Bilel MEDIMEGH --- docs/_docs/best-practices.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_docs/best-practices.md b/docs/_docs/best-practices.md index 673facd6c5..2dba074704 100644 --- a/docs/_docs/best-practices.md +++ b/docs/_docs/best-practices.md @@ -12,7 +12,7 @@ how effective the system will be when performing component risk analysis. ### Generating and Obtaining BOMs * When developing software, generate BOMs during Continuous Integration (CI) * If using Jenkins, use the [Dependency-Track Jenkins Plugin](https://plugins.jenkins.io/dependency-track/) with synchronous publishing mode enabled -* Contractually require BOMs ([CycloneDX](https://cyclonedx.org) from vendors +* Contractually require BOMs ([CycloneDX](https://cyclonedx.org)) from vendors * Generate or acquire BOMs from commercial-off-the-shelf (COTS) software #### Summary From b32cffa91c4e99745d251225caf99e329cf6ddeb Mon Sep 17 00:00:00 2001 From: nscuro Date: Thu, 26 Sep 2024 19:18:38 +0200 Subject: [PATCH 173/429] Cache Trivy DB for integration tests Trivy integration tests are failing in CI more frequently now, mostly because the Trivy container keeps downloading its vulnerability from ghcr.io, and the registry enforces rate limiting. Cache the database between tests to prevent redundant downloads. Signed-off-by: nscuro --- .../TrivyAnalysisTaskIntegrationTest.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskIntegrationTest.java b/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskIntegrationTest.java index 1c86489cdb..50bfb51832 100644 --- a/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskIntegrationTest.java +++ b/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskIntegrationTest.java @@ -20,6 +20,9 @@ import alpine.model.IConfigProperty; import alpine.security.crypto.DataEncryption; +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.command.CreateVolumeResponse; +import com.github.dockerjava.api.model.Bind; import org.dependencytrack.PersistenceCapableTest; import org.dependencytrack.event.TrivyAnalysisEvent; import org.dependencytrack.model.Classifier; @@ -27,10 +30,13 @@ import org.dependencytrack.model.Project; import org.dependencytrack.model.Vulnerability; import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.images.PullPolicy; import org.testcontainers.utility.DockerImageName; @@ -57,6 +63,7 @@ public static Collection testParameters() { }); } + private static String trivyCacheVolumeName; private final String trivyVersion; private GenericContainer trivyContainer; @@ -64,6 +71,16 @@ public TrivyAnalysisTaskIntegrationTest(String trivyVersion) { this.trivyVersion = trivyVersion; } + @BeforeClass + @SuppressWarnings("resource") + public static void beforeClass() { + final DockerClient dockerClient = DockerClientFactory.lazyClient(); + final CreateVolumeResponse response = dockerClient.createVolumeCmd() + .withName("dtrack-test-trivy-cache") + .exec(); + trivyCacheVolumeName = response.getName(); + } + @Before @Override @SuppressWarnings("resource") @@ -72,8 +89,10 @@ public void before() throws Exception { trivyContainer = new GenericContainer<>(DockerImageName.parse("aquasec/trivy:" + trivyVersion)) .withImagePullPolicy(PullPolicy.alwaysPull()) - .withCommand("server --listen :8080 --token TrivyToken") + .withCommand("server --cache-dir /tmp/cache --listen :8080 --token TrivyToken") .withExposedPorts(8080) + .withCreateContainerCmdModifier(cmd -> cmd.getHostConfig() + .withBinds(Bind.parse("%s:/tmp/cache".formatted(trivyCacheVolumeName)))) .waitingFor(forLogMessage(".*Listening :8080.*", 1)); trivyContainer.start(); @@ -110,6 +129,15 @@ public void after() { super.after(); } + @AfterClass + @SuppressWarnings("resource") + public static void afterClass() { + if (trivyCacheVolumeName != null) { + final DockerClient dockerClient = DockerClientFactory.lazyClient(); + dockerClient.removeVolumeCmd(trivyCacheVolumeName).exec(); + } + } + @Test public void test() { final var project = new Project(); From 37f550a50ac9c68158d7dc530e6305c3fbd6dab7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 08:30:29 +0000 Subject: [PATCH 174/429] Bump debian from `64bc71f` to `a75706a` in /src/main/docker Bumps debian from `64bc71f` to `a75706a`. --- updated-dependencies: - dependency-name: debian dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile index 1be2d2af8f..21de995f5a 100644 --- a/src/main/docker/Dockerfile +++ b/src/main/docker/Dockerfile @@ -1,6 +1,6 @@ FROM eclipse-temurin:21.0.4_7-jre-jammy@sha256:870aae69d4521fdaf26e952f8026f75b37cb721e6302d4d4d7100f6b09823057 AS jre-build -FROM debian:stable-slim@sha256:64bc71feaa7ec2ac758a6a3a37c0f0d6ebccf0a45e3f5af1f1d3b5d4cb316b29 +FROM debian:stable-slim@sha256:a75706ac1838d761d95fe2690858392588310abcab67876a0c330252f1073373 # Arguments that can be passed at build time # Directory names must end with / to avoid errors when ADDing and COPYing From 4cd5671f5c0af10709c59fec500a82cb18f5437d Mon Sep 17 00:00:00 2001 From: nscuro Date: Sat, 28 Sep 2024 12:58:07 +0200 Subject: [PATCH 175/429] Fix breaking change in `PUT /api/v1/project` endpoint With #4093, the `accessTeams` field became a required request parameter if portfolio ACL is enabled. While it does make sense to enforce team assignment, it is a breaking change in the REST API and as such cannot be done in a minor version release. Make `accessTeams` optional. Also: * Enable `accessTeams` to be specified by `name`, not only by `uuid` * Document this feature in the OpenAPI spec * Reduce nesting in the `ProjectResource#createProject` method * Enhance tests to be more explicit and assert response content Signed-off-by: nscuro --- .../resources/v1/ProjectResource.java | 145 ++++--- .../dependencytrack/util/PersistenceUtil.java | 2 +- .../resources/v1/ProjectResourceTest.java | 383 +++++++++++++----- 3 files changed, 389 insertions(+), 141 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java index b209b331e4..d9dcf34432 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java @@ -41,7 +41,6 @@ import org.dependencytrack.auth.Permissions; import org.dependencytrack.event.CloneProjectEvent; import org.dependencytrack.model.Classifier; -import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.Project; import org.dependencytrack.model.Tag; import org.dependencytrack.model.validation.ValidUuid; @@ -65,14 +64,17 @@ import jakarta.ws.rs.core.Response; import javax.jdo.FetchGroup; import java.security.Principal; -import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.function.BiConsumer; import java.util.function.Function; +import static java.util.Objects.requireNonNullElseGet; +import static org.dependencytrack.util.PersistenceUtil.isPersistent; + /** * JAX-RS resources for processing projects. * @@ -274,6 +276,13 @@ public Response getProjectsByClassifier( summary = "Creates a new project", description = """

        If a parent project exists, parent.uuid is required

        +

        + When portfolio access control is enabled, one or more teams to grant access + to can be provided via accessTeams. Either uuid or + name of a team must be specified. Only teams which the authenticated + principal is a member of can be assigned. Principals with ACCESS_MANAGEMENT + permission can assign any team. +

        Requires permission PORTFOLIO_MANAGEMENT

        """ ) @@ -283,17 +292,16 @@ public Response getProjectsByClassifier( description = "The created project", content = @Content(schema = @Schema(implementation = Project.class)) ), + @ApiResponse(responseCode = "400", description = "Bad Request"), @ApiResponse(responseCode = "401", description = "Unauthorized"), - @ApiResponse(responseCode = "403", description = "You don't have the permission to assign this team to a project."), @ApiResponse(responseCode = "409", description = """
        • An inactive Parent cannot be selected as parent, or
        • A project with the specified name already exists
        • -
        """), - @ApiResponse(responseCode = "422", description = "You need to specify at least one team to which the project should belong"), +
      """) }) @PermissionRequired(Permissions.Constants.PORTFOLIO_MANAGEMENT) - public Response createProject(Project jsonProject) { + public Response createProject(final Project jsonProject) { final Validator validator = super.getValidator(); failOnValidationError( validator.validateProperty(jsonProject, "author"), @@ -312,58 +320,97 @@ public Response createProject(Project jsonProject) { if (jsonProject.getClassifier() == null) { jsonProject.setClassifier(Classifier.APPLICATION); } - try (QueryManager qm = new QueryManager()) { + try (final var qm = new QueryManager()) { + if (qm.doesProjectExist(StringUtils.trimToNull(jsonProject.getName()), + StringUtils.trimToNull(jsonProject.getVersion()))) { + return Response + .status(Response.Status.CONFLICT) + .entity("A project with the specified name already exists.") + .build(); + } + if (jsonProject.getParent() != null && jsonProject.getParent().getUuid() != null) { Project parent = qm.getObjectByUuid(Project.class, jsonProject.getParent().getUuid()); - jsonProject.setParent(parent); + jsonProject.setParent(parent); } - if (!qm.doesProjectExist(StringUtils.trimToNull(jsonProject.getName()), - StringUtils.trimToNull(jsonProject.getVersion()))) { - final List chosenTeams = jsonProject.getAccessTeams() == null ? new ArrayList() - : jsonProject.getAccessTeams(); - boolean required = qm.isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED); - if (required && chosenTeams.isEmpty()) { - return Response.status(422) - .entity("You need to specify at least one team to which the project should belong").build(); + + final Principal principal = getPrincipal(); + + final List chosenTeams = requireNonNullElseGet( + jsonProject.getAccessTeams(), Collections::emptyList); + jsonProject.setAccessTeams(null); + + for (final Team chosenTeam : chosenTeams) { + if (chosenTeam.getUuid() == null && chosenTeam.getName() == null) { + return Response + .status(Response.Status.BAD_REQUEST) + .entity(""" + accessTeams must either specify a UUID or a name,\ + but the team at index %d has neither.\ + """.formatted(chosenTeams.indexOf(chosenTeam))) + .build(); } - Principal principal = getPrincipal(); - if (!chosenTeams.isEmpty()) { - List userTeams = new ArrayList(); - if (principal instanceof final UserPrincipal userPrincipal) { - userTeams = userPrincipal.getTeams(); - } else if (principal instanceof final ApiKey apiKey) { - userTeams = apiKey.getTeams(); + } + + if (!chosenTeams.isEmpty()) { + final List userTeams; + if (principal instanceof final UserPrincipal userPrincipal) { + userTeams = userPrincipal.getTeams(); + } else if (principal instanceof final ApiKey apiKey) { + userTeams = apiKey.getTeams(); + } else { + userTeams = Collections.emptyList(); + } + + final boolean isAdmin = qm.hasAccessManagementPermission(principal); + final List visibleTeams = isAdmin ? qm.getTeams() : userTeams; + final var visibleTeamByUuid = new HashMap(visibleTeams.size()); + final var visibleTeamByName = new HashMap(visibleTeams.size()); + for (final Team visibleTeam : visibleTeams) { + visibleTeamByUuid.put(visibleTeam.getUuid(), visibleTeam); + visibleTeamByName.put(visibleTeam.getName(), visibleTeam); + } + + for (final Team chosenTeam : chosenTeams) { + Team visibleTeam = visibleTeamByUuid.getOrDefault( + chosenTeam.getUuid(), + visibleTeamByName.get(chosenTeam.getName())); + + if (visibleTeam == null) { + return Response + .status(Response.Status.BAD_REQUEST) + .entity(""" + The team with %s can not be assigned because it does not exist, \ + or is not accessible to the authenticated principal.\ + """.formatted(chosenTeam.getUuid() != null + ? "UUID " + chosenTeam.getUuid() + : "name " + chosenTeam.getName())) + .build(); } - boolean isAdmin = qm.hasAccessManagementPermission(principal); - List visibleTeams = isAdmin ? qm.getTeams() : userTeams; - List visibleUuids = visibleTeams.isEmpty() ? new ArrayList() - : visibleTeams.stream().map(Team::getUuid).toList(); - jsonProject.setAccessTeams(new ArrayList()); - for (Team choosenTeam : chosenTeams) { - if (!visibleUuids.contains(choosenTeam.getUuid())) { - return isAdmin ? Response.status(404).entity("This team does not exist!").build() - : Response.status(403) - .entity("You don't have the permission to assign this team to a project.") - .build(); - } - Team ormTeam = qm.getObjectByUuid(Team.class, choosenTeam.getUuid()); - jsonProject.addAccessTeam(ormTeam); + + if (!isPersistent(visibleTeam)) { + // Teams sourced from the principal will not be in persistent state + // and need to be attached to the persistence context. + visibleTeam = qm.getObjectById(Team.class, visibleTeam.getId()); } - } - final Project project; - try { - project = qm.createProject(jsonProject, jsonProject.getTags(), true); - } catch (IllegalArgumentException e){ - LOGGER.debug(e.getMessage()); - return Response.status(Response.Status.CONFLICT).entity("An inactive Parent cannot be selected as parent").build(); + jsonProject.addAccessTeam(visibleTeam); } - qm.updateNewProjectACL(project, principal); - LOGGER.info("Project " + project.toString() + " created by " + super.getPrincipal().getName()); - return Response.status(Response.Status.CREATED).entity(project).build(); - } else { - return Response.status(Response.Status.CONFLICT).entity("A project with the specified name already exists.").build(); } + + final Project project; + try { + project = qm.createProject(jsonProject, jsonProject.getTags(), true); + } catch (IllegalArgumentException e) { + LOGGER.debug("Failed to create project", e); + return Response + .status(Response.Status.CONFLICT) + .entity("An inactive Parent cannot be selected as parent") + .build(); + } + qm.updateNewProjectACL(project, principal); + LOGGER.info("Project " + project.toString() + " created by " + super.getPrincipal().getName()); + return Response.status(Response.Status.CREATED).entity(project).build(); } } diff --git a/src/main/java/org/dependencytrack/util/PersistenceUtil.java b/src/main/java/org/dependencytrack/util/PersistenceUtil.java index 1a9f4d34cd..dde347eb5a 100644 --- a/src/main/java/org/dependencytrack/util/PersistenceUtil.java +++ b/src/main/java/org/dependencytrack/util/PersistenceUtil.java @@ -233,7 +233,7 @@ public static void assertNonPersistentAll(final Collection objects, final Str objects.forEach(object -> assertNonPersistent(object, message)); } - private static boolean isPersistent(final Object object) { + public static boolean isPersistent(final Object object) { final ObjectState objectState = JDOHelper.getObjectState(object); return objectState == PERSISTENT_CLEAN || objectState == PERSISTENT_DIRTY diff --git a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java index 02566180e0..4efa8fcd09 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java @@ -22,7 +22,6 @@ import alpine.event.framework.EventService; import alpine.model.IConfigProperty.PropertyType; import alpine.model.ManagedUser; -import alpine.model.Permission; import alpine.model.Team; import alpine.server.auth.JsonWebToken; import alpine.server.filters.ApiFilter; @@ -30,6 +29,7 @@ import org.cyclonedx.model.ExternalReference.Type; import org.dependencytrack.JerseyTestRule; import org.dependencytrack.ResourceTest; +import org.dependencytrack.auth.Permissions; import org.dependencytrack.event.CloneProjectEvent; import org.dependencytrack.model.Analysis; import org.dependencytrack.model.AnalysisJustification; @@ -47,7 +47,6 @@ import org.dependencytrack.model.ServiceComponent; import org.dependencytrack.model.Tag; import org.dependencytrack.model.Vulnerability; -import org.dependencytrack.persistence.DefaultObjectGenerator; import org.dependencytrack.tasks.CloneProjectTask; import org.dependencytrack.tasks.scanners.AnalyzerIdentity; import org.glassfish.jersey.client.HttpUrlConnectorProvider; @@ -83,8 +82,6 @@ import static org.hamcrest.Matchers.not; public class ProjectResourceTest extends ResourceTest { - private ManagedUser testUser; - private String jwt; @ClassRule public static JerseyTestRule jersey = new JerseyTestRule( @@ -99,38 +96,6 @@ public void after() throws Exception { super.after(); } - public JsonObjectBuilder setUpEnvironment(boolean isAdmin, boolean isRequired, String name, Team team1) { - testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); - jwt = new JsonWebToken().createToken(testUser); - qm.addUserToTeam(testUser, team); - final var generator = new DefaultObjectGenerator(); - generator.loadDefaultPermissions(); - List permissionsList = new ArrayList(); - final Permission permission = qm.getPermission("PORTFOLIO_MANAGEMENT"); - permissionsList.add(permission); - testUser.setPermissions(permissionsList); - if (isAdmin) { - final Permission adminPermission = qm.getPermission("ACCESS_MANAGEMENT"); - permissionsList.add(adminPermission); - testUser.setPermissions(permissionsList); - } - if (isRequired) { - qm.createConfigProperty( - ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), - ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), - "true", - ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), - null); - } - final JsonObjectBuilder jsonProject = Json.createObjectBuilder() - .add("name", name).add("classifier", "CONTAINER").addNull("parent").add("active", true).add("tags", Json.createArrayBuilder()); - if (team1 != null) { - final JsonObject jsonTeam = Json.createObjectBuilder().add("uuid", team1.getUuid().toString()).build(); - jsonProject.add("accessTeams", Json.createArrayBuilder().add(jsonTeam).build()); - } - return jsonProject; - } - @Test public void getProjectsDefaultRequestTest() { for (int i=0; i<1000; i++) { @@ -521,87 +486,323 @@ public void createProjectEmptyTest() { } @Test - public void createProjectWithExistingTeamRequiredTest() { - Team AllowedTeam = qm.createTeam("AllowedTeam", false); - final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(false, true, "ProjectWithExistingTeamRequired", AllowedTeam); - qm.addUserToTeam(testUser, AllowedTeam); - Response response = jersey.target(V1_PROJECT) + public void createProjectAsUserWithAclEnabledAndExistingTeamByUuidTest() { + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + + final ManagedUser testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); + qm.addUserToTeam(testUser, team); + + final String userJwt = new JsonWebToken().createToken(testUser); + + final Response response = jersey.target(V1_PROJECT) .request() - .header("Authorization", "Bearer " + jwt) - .put(Entity.json(requestBodyBuilder.build().toString())); - Assert.assertEquals(201, response.getStatus()); - JsonObject returnedProject = parseJsonObject(response); + .header("Authorization", "Bearer " + userJwt) + .put(Entity.json(/* language=JSON */ """ + { + "name": "acme-app", + "accessTeams": [ + { + "uuid": "%s" + } + ] + } + """.formatted(team.getUuid()))); + assertThat(response.getStatus()).isEqualTo(201); + assertThatJson(getPlainTextBody(response)) + .isEqualTo(/* language=JSON */ """ + { + "uuid": "${json-unit.any-string}", + "name": "acme-app", + "classifier": "APPLICATION", + "children": [], + "properties": [], + "tags": [], + "active": true + } + """); + + assertThat(qm.getAllProjects()).satisfiesExactly(project -> + assertThat(project.getAccessTeams()).extracting(Team::getName).containsOnly(team.getName())); } @Test - public void createProjectWithoutExistingTeamRequiredTest() { - final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(false, true, "ProjectWithoutExistingTeamRequired", null); - Response response = jersey.target(V1_PROJECT) + public void createProjectAsUserWithAclEnabledAndExistingTeamByNameTest() { + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + + final ManagedUser testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); + qm.addUserToTeam(testUser, team); + + final String userJwt = new JsonWebToken().createToken(testUser); + + final Response response = jersey.target(V1_PROJECT) .request() - .header("Authorization", "Bearer " + jwt) - .put(Entity.json(requestBodyBuilder.build().toString())); - Assert.assertEquals(422, response.getStatus(), 0); + .header("Authorization", "Bearer " + userJwt) + .put(Entity.json(/* language=JSON */ """ + { + "name": "acme-app", + "accessTeams": [ + { + "name": "%s" + } + ] + } + """.formatted(team.getName()))); + assertThat(response.getStatus()).isEqualTo(201); + assertThatJson(getPlainTextBody(response)) + .isEqualTo(/* language=JSON */ """ + { + "uuid": "${json-unit.any-string}", + "name": "acme-app", + "classifier": "APPLICATION", + "children": [], + "properties": [], + "tags": [], + "active": true + } + """); + + assertThat(qm.getAllProjects()).satisfiesExactly(project -> + assertThat(project.getAccessTeams()).extracting(Team::getName).containsOnly(team.getName())); } @Test - public void createProjectWithNotAllowedExistingTeamTest() { - Team notAllowedTeam = qm.createTeam("NotAllowedTeam", false); - final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(false, true, "ProjectWithNotAllowedExistingTeam", notAllowedTeam); - Response response = jersey.target(V1_PROJECT) + public void createProjectAsUserWithAclEnabledAndWithoutTeamTest() { + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + + final ManagedUser testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); + qm.addUserToTeam(testUser, team); + + final String userJwt = new JsonWebToken().createToken(testUser); + + final Response response = jersey.target(V1_PROJECT) .request() - .header("Authorization", "Bearer " + jwt) - .put(Entity.json(requestBodyBuilder.build().toString())); - Assert.assertEquals(403, response.getStatus()); + .header("Authorization", "Bearer " + userJwt) + .put(Entity.json(/* language=JSON */ """ + { + "name": "acme-app" + } + """)); + assertThat(response.getStatus()).isEqualTo(201); + assertThatJson(getPlainTextBody(response)) + .isEqualTo(/* language=JSON */ """ + { + "uuid": "${json-unit.any-string}", + "name": "acme-app", + "classifier": "APPLICATION", + "children": [], + "properties": [], + "tags": [], + "active": true + } + """); + + assertThat(qm.getAllProjects()).satisfiesExactly(project -> + assertThat(project.getAccessTeams()).isEmpty()); } @Test - public void createProjectWithNotAllowedExistingTeamAdminTest() { - Team AllowedTeam = qm.createTeam("NotAllowedTeam", false); - final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(false, true, "ProjectWithNotAllowedExistingTeam", AllowedTeam); - qm.addUserToTeam(testUser, AllowedTeam); - Response response = jersey.target(V1_PROJECT) + public void createProjectAsUserWithNotAllowedExistingTeamTest() { + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + + final ManagedUser testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); + + final String userJwt = new JsonWebToken().createToken(testUser); + + final Response response = jersey.target(V1_PROJECT) + .request() + .header("Authorization", "Bearer " + userJwt) + .put(Entity.json(/* language=JSON */ """ + { + "name": "acme-app", + "accessTeams": [ + { + "uuid": "%s" + } + ] + } + """.formatted(team.getUuid()))); + assertThat(response.getStatus()).isEqualTo(400); + assertThat(getPlainTextBody(response)).isEqualTo(""" + The team with UUID %s can not be assigned because it does not exist, \ + or is not accessible to the authenticated principal.""", team.getUuid()); + } + + @Test + public void createProjectAsUserWithAclEnabledAndNotMemberOfTeamAdminTest() { + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + + initializeWithPermissions(Permissions.ACCESS_MANAGEMENT); + + final ManagedUser testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); + qm.addUserToTeam(testUser, team); + + final String userJwt = new JsonWebToken().createToken(testUser); + + final Team otherTeam = qm.createTeam("otherTeam", false); + + final Response response = jersey.target(V1_PROJECT) .request() - .header("Authorization", "Bearer " + jwt) - .put(Entity.json(requestBodyBuilder.build().toString())); - Assert.assertEquals(201, response.getStatus()); - JsonObject returnedProject = parseJsonObject(response); + .header("Authorization", "Bearer " + userJwt) + .put(Entity.json(/* language=JSON */ """ + { + "name": "acme-app", + "accessTeams": [ + { + "uuid": "%s" + } + ] + } + """.formatted(otherTeam.getUuid()))); + assertThat(response.getStatus()).isEqualTo(201); + assertThatJson(getPlainTextBody(response)) + .isEqualTo(/* language=JSON */ """ + { + "uuid": "${json-unit.any-string}", + "name": "acme-app", + "classifier": "APPLICATION", + "children": [], + "properties": [], + "tags": [], + "active": true + } + """); + + assertThat(qm.getAllProjects()).satisfiesExactly(project -> + assertThat(project.getAccessTeams()).extracting(Team::getName).containsOnly("otherTeam")); } @Test - public void createProjectWithNotExistingTeamNoAdminTest() { - Team notAllowedTeam = new Team(); - notAllowedTeam.setUuid(new UUID(1, 1)); - notAllowedTeam.setName("NotAllowedTeam"); - final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(false, true, "ProjectWithNotAllowedExistingTeam", notAllowedTeam); - Response response = jersey.target(V1_PROJECT) + public void createProjectAsUserWithAclEnabledAndTeamNotExistingNoAdminTest() { + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + + final ManagedUser testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); + + final String userJwt = new JsonWebToken().createToken(testUser); + + final Response response = jersey.target(V1_PROJECT) .request() - .header("Authorization", "Bearer " + jwt) - .put(Entity.json(requestBodyBuilder.build().toString())); - Assert.assertEquals(403, response.getStatus()); + .header("Authorization", "Bearer " + userJwt) + .put(Entity.json(/* language=JSON */ """ + { + "name": "acme-app", + "accessTeams": [ + { + "uuid": "419c32eb-5a30-47d5-8a9a-fc0cda651314" + } + ] + } + """)); + assertThat(response.getStatus()).isEqualTo(400); + assertThat(getPlainTextBody(response)).isEqualTo(""" + The team with UUID 419c32eb-5a30-47d5-8a9a-fc0cda651314 \ + can not be assigned because it does not exist, or is not \ + accessible to the authenticated principal."""); } @Test - public void createProjectWithNotExistingTeamTest() { - Team notAllowedTeam = new Team(); - notAllowedTeam.setUuid(new UUID(1, 1)); - notAllowedTeam.setName("NotAllowedTeam"); - final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(true, true, "ProjectWithNotAllowedExistingTeam", notAllowedTeam); - Response response = jersey.target(V1_PROJECT) + public void createProjectAsUserWithAclEnabledAndTeamNotExistingAdminTest() { + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + + initializeWithPermissions(Permissions.ACCESS_MANAGEMENT); + + final ManagedUser testUser = qm.createManagedUser("testuser", TEST_USER_PASSWORD_HASH); + qm.addUserToTeam(testUser, team); + + final String userJwt = new JsonWebToken().createToken(testUser); + + final Response response = jersey.target(V1_PROJECT) .request() - .header("Authorization", "Bearer " + jwt) - .put(Entity.json(requestBodyBuilder.build().toString())); - Assert.assertEquals(404, response.getStatus()); + .header("Authorization", "Bearer " + userJwt) + .put(Entity.json(/* language=JSON */ """ + { + "name": "acme-app", + "accessTeams": [ + { + "uuid": "419c32eb-5a30-47d5-8a9a-fc0cda651314" + } + ] + } + """)); + assertThat(response.getStatus()).isEqualTo(400); + assertThat(getPlainTextBody(response)).isEqualTo(""" + The team with UUID 419c32eb-5a30-47d5-8a9a-fc0cda651314 \ + can not be assigned because it does not exist, or is not \ + accessible to the authenticated principal."""); } @Test - public void createProjectWithApiKeyTest() { - final JsonObjectBuilder requestBodyBuilder = setUpEnvironment(false, true, "ProjectWithNotAllowedExistingTeam", team); - Response response = jersey.target(V1_PROJECT) + public void createProjectAsApiKeyWithAclEnabledAndWithExistentTeamTest() { + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + + final Response response = jersey.target(V1_PROJECT) .request() .header(X_API_KEY, apiKey) - .put(Entity.json(requestBodyBuilder.build().toString())); - Assert.assertEquals(201, response.getStatus()); - JsonObject returnedProject = parseJsonObject(response); + .put(Entity.json(/* language=JSON */ """ + { + "name": "acme-app", + "accessTeams": [ + { + "uuid": "%s" + } + ] + } + """.formatted(team.getUuid()))); + assertThat(response.getStatus()).isEqualTo(201); + assertThatJson(getPlainTextBody(response)) + .isEqualTo(/* language=JSON */ """ + { + "uuid": "${json-unit.any-string}", + "name": "acme-app", + "classifier": "APPLICATION", + "children": [], + "properties": [], + "tags": [], + "active": true + } + """); + + assertThat(qm.getAllProjects()).satisfiesExactly(project -> + assertThat(project.getAccessTeams()).extracting(Team::getName).containsOnly(team.getName())); } @Test From 3b4af924044b3ba47321b3841e609698ce5ed458 Mon Sep 17 00:00:00 2001 From: Ralf King Date: Thu, 26 Sep 2024 18:17:43 +0200 Subject: [PATCH 176/429] Introduce isLatest flag for projects. Support this for different endpoints which allow creation or modification of projects. Signed-off-by: Ralf King --- .../org/dependencytrack/model/Project.java | 19 +- .../ProjectQueryFilterBuilder.java | 5 + .../persistence/ProjectQueryManager.java | 198 ++++++++--- .../persistence/QueryManager.java | 15 + .../resources/v1/BomResource.java | 26 +- .../resources/v1/ProjectResource.java | 100 +++++- .../resources/v1/vo/BomSubmitRequest.java | 10 +- .../resources/v1/vo/CloneProjectRequest.java | 12 +- .../upgrade/v4120/v4120Updater.java | 14 +- .../org/dependencytrack/ResourceTest.java | 11 + .../event/CloneProjectEventTest.java | 4 +- .../resources/v1/BomResourceTest.java | 69 +++- .../resources/v1/ProjectResourceTest.java | 323 ++++++++++++++++-- 13 files changed, 716 insertions(+), 90 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/Project.java b/src/main/java/org/dependencytrack/model/Project.java index 01148e4e5d..d090e3a5de 100644 --- a/src/main/java/org/dependencytrack/model/Project.java +++ b/src/main/java/org/dependencytrack/model/Project.java @@ -24,6 +24,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonIncludeProperties; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; @@ -95,7 +96,8 @@ @Persistent(name = "properties"), @Persistent(name = "tags"), @Persistent(name = "accessTeams"), - @Persistent(name = "metadata") + @Persistent(name = "metadata"), + @Persistent(name = "isLatest") }), @FetchGroup(name = "METADATA", members = { @Persistent(name = "metadata") @@ -109,6 +111,7 @@ @Persistent(name = "parent") }) }) +@Index(name = "PROJECT_NAME_IS_LATEST_IDX", members = {"name", "isLatest"}) @JsonInclude(JsonInclude.Include.NON_NULL) public class Project implements Serializable { @@ -270,6 +273,11 @@ public enum FetchGroup { @JsonSerialize(nullsUsing = BooleanDefaultTrueSerializer.class) private Boolean active; // Added in v3.6. Existing records need to be nullable on upgrade. + @Persistent + @Index(name = "PROJECT_IS_LATEST_IDX") + @Column(name = "IS_LATEST", defaultValue = "false") + private Boolean isLatest; // Added in v4.12. Needs to be nullable therefore + @Persistent(table = "PROJECT_ACCESS_TEAMS", defaultFetchGroup = "true") @Join(column = "PROJECT_ID") @Element(column = "TEAM_ID") @@ -513,6 +521,15 @@ public void setActive(Boolean active) { this.active = active; } + @JsonProperty("isLatest") + public Boolean isLatest() { + return isLatest != null ? isLatest : false; + } + + public void setIsLatest(Boolean latest) { + isLatest = latest != null ? latest : false; + } + public String getBomRef() { return bomRef; } diff --git a/src/main/java/org/dependencytrack/persistence/ProjectQueryFilterBuilder.java b/src/main/java/org/dependencytrack/persistence/ProjectQueryFilterBuilder.java index 4d8ce37f88..21076fef9c 100644 --- a/src/main/java/org/dependencytrack/persistence/ProjectQueryFilterBuilder.java +++ b/src/main/java/org/dependencytrack/persistence/ProjectQueryFilterBuilder.java @@ -115,6 +115,11 @@ ProjectQueryFilterBuilder withParent(UUID uuid){ return this; } + public ProjectQueryFilterBuilder onlyLatestVersion() { + filterCriteria.add("(isLatest == true)"); + return this; + } + String buildFilter() { return String.join(" && ", this.filterCriteria); } diff --git a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java index 89a2aa6285..d1ca5a213f 100644 --- a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java @@ -267,6 +267,38 @@ public Project getProject(final String name, final String version) { return project; } + + /** + * Returns the latest version of a project by its name. + * + * @param name the name of the Project (required) + * @return a Project object representing the latest version, or null if not found + */ + @Override + public Project getLatestProjectVersion(final String name) { + final Query query = pm.newQuery(Project.class); + + final var filterBuilder = new ProjectQueryFilterBuilder() + .withName(name) + .onlyLatestVersion(); + + final String queryFilter = filterBuilder.buildFilter(); + final Map params = filterBuilder.getParams(); + + preprocessACLs(query, queryFilter, params, false); + query.setFilter(queryFilter); + query.setRange(0, 1); + + final Project project = singleResult(query.executeWithMap(params)); + if (project != null) { + // set Metrics to prevent extra round trip + project.setMetrics(getMostRecentProjectMetrics(project)); + // set ProjectVersions to prevent extra round trip + project.setVersions(getProjectVersions(project)); + } + return project; + } + /** * Returns a list of projects that are accessible by the specified team. * @param team the team the has access to Projects @@ -397,35 +429,35 @@ public PaginatedResult getProjects(final Tag tag) { * @return the created Project */ @Override - public Project createProject(String name, String description, String version, List tags, Project parent, PackageURL purl, boolean active, boolean commitIndex) { + public Project createProject(String name, String description, String version, List tags, Project parent, + PackageURL purl, boolean active, boolean commitIndex) { + return createProject(name, description, version, tags, parent, purl, active, false, commitIndex); + } + + /** + * Creates a new Project. + * @param name the name of the project to create + * @param description a description of the project + * @param version the project version + * @param tags a List of Tags - these will be resolved if necessary + * @param parent an optional parent Project + * @param purl an optional Package URL + * @param active specified if the project is active + * @param commitIndex specifies if the search index should be committed (an expensive operation) + * @return the created Project + */ + @Override + public Project createProject(String name, String description, String version, List tags, Project parent, + PackageURL purl, boolean active, boolean isLatest, boolean commitIndex) { final Project project = new Project(); project.setName(name); project.setDescription(description); project.setVersion(version); - if (parent != null ) { - if (!Boolean.TRUE.equals(parent.isActive())){ - throw new IllegalArgumentException("An inactive Parent cannot be selected as parent"); - } - project.setParent(parent); - } + project.setParent(parent); project.setPurl(purl); project.setActive(active); - final Project result = persist(project); - - final List resolvedTags = resolveTags(tags); - bind(project, resolvedTags); - - Event.dispatch(new IndexEvent(IndexEvent.Action.CREATE, result)); - Notification.dispatch(new Notification() - .scope(NotificationScope.PORTFOLIO) - .group(NotificationGroup.PROJECT_CREATED) - .title(NotificationConstants.Title.PROJECT_CREATED) - .level(NotificationLevel.INFORMATIONAL) - .content(result.getName() + " was created") - .subject(NotificationUtil.toJson(pm.detachCopy(result))) - ); - commitSearchIndex(commitIndex, Project.class); - return result; + project.setIsLatest(isLatest); + return createProject(project, tags, commitIndex); } /** @@ -443,11 +475,36 @@ public Project createProject(final Project project, List tags, boolean comm if (project.isActive() == null) { project.setActive(Boolean.TRUE); } - final Project result = persist(project); - final List resolvedTags = resolveTags(tags); - bind(project, resolvedTags); + if (project.isLatest() == null) { + project.setIsLatest(Boolean.FALSE); + } + final Project oldLatestProject = project.isLatest() ? getLatestProjectVersion(project.getName()) : null; + final Project result = callInTransaction(() -> { + // Remove isLatest flag from current latest project version, if the new project will be the latest + if(oldLatestProject != null) { + oldLatestProject.setIsLatest(false); + persist(oldLatestProject); + } + + final Project newProject = persist(project); + final List resolvedTags = resolveTags(tags); + bind(project, resolvedTags); + return newProject; + }); + if(oldLatestProject != null) { + // if we removed isLatest flag from old version, dispatch update event for the old version + Event.dispatch(new IndexEvent(IndexEvent.Action.UPDATE, oldLatestProject)); + } Event.dispatch(new IndexEvent(IndexEvent.Action.CREATE, result)); + Notification.dispatch(new Notification() + .scope(NotificationScope.PORTFOLIO) + .group(NotificationGroup.PROJECT_CREATED) + .title(NotificationConstants.Title.PROJECT_CREATED) + .level(NotificationLevel.INFORMATIONAL) + .content(result.getName() + " was created") + .subject(NotificationUtil.toJson(pm.detachCopy(result))) + ); commitSearchIndex(commitIndex, Project.class); return result; } @@ -480,6 +537,14 @@ public Project updateProject(Project transientProject, boolean commitIndex) { } project.setActive(transientProject.isActive()); + final Project oldLatestProject; + if(Boolean.TRUE.equals(transientProject.isLatest()) && Boolean.FALSE.equals(project.isLatest())) { + oldLatestProject = getLatestProjectVersion(project.getName()); + } else { + oldLatestProject = null; + } + project.setIsLatest(transientProject.isLatest()); + if (transientProject.getParent() != null && transientProject.getParent().getUuid() != null) { if (project.getUuid().equals(transientProject.getParent().getUuid())){ throw new IllegalArgumentException("A project cannot select itself as a parent"); @@ -497,10 +562,23 @@ public Project updateProject(Project transientProject, boolean commitIndex) { project.setParent(null); } - final List resolvedTags = resolveTags(transientProject.getTags()); - bind(project, resolvedTags); + final Project result = callInTransaction(() -> { + // Remove isLatest flag from current latest project version, if this project will be the latest now + if(oldLatestProject != null) { + oldLatestProject.setIsLatest(false); + persist(oldLatestProject); + } + + final List resolvedTags = resolveTags(transientProject.getTags()); + bind(project, resolvedTags); - final Project result = persist(project); + return persist(project); + }); + + if(oldLatestProject != null) { + // if we removed isLatest flag from old version, dispatch update event for the old version + Event.dispatch(new IndexEvent(IndexEvent.Action.UPDATE, oldLatestProject)); + } Event.dispatch(new IndexEvent(IndexEvent.Action.UPDATE, result)); commitSearchIndex(commitIndex, Project.class); return result; @@ -518,21 +596,45 @@ public Project clone( final boolean includeACL, final boolean includePolicyViolations ) { - final var jsonMapper = new JsonMapper(); + return clone(from, newVersion, includeTags, includeProperties, includeComponents, includeServices, includeAuditHistory, + includeACL, includePolicyViolations, false); + } + + @Override + public Project clone( + final UUID from, + final String newVersion, + final boolean includeTags, + final boolean includeProperties, + final boolean includeComponents, + final boolean includeServices, + final boolean includeAuditHistory, + final boolean includeACL, + final boolean includePolicyViolations, + final boolean makeCloneLatest + ) { + final Project source = getObjectByUuid(Project.class, from, Project.FetchGroup.ALL.name()); + if (source == null) { + LOGGER.warn("Project was supposed to be cloned, but it does not exist anymore"); + return null; + } + if (doesProjectExist(source.getName(), newVersion)) { + // Project cloning is an asynchronous process. When receiving the clone request, we already perform + // this check. It is possible though that a project with the new version is created synchronously + // between the clone event being dispatched, and it being processed. + LOGGER.warn("Project was supposed to be cloned to version %s, but that version already exists".formatted(newVersion)); + return null; + } + final Project oldLatestProject; + if(makeCloneLatest) { + oldLatestProject = source.isLatest() ? source : getLatestProjectVersion(source.getName()); + } else { + oldLatestProject = null; + } + + final var jsonMapper = new JsonMapper(); final Project clonedProject = callInTransaction(() -> { - final Project source = getObjectByUuid(Project.class, from, Project.FetchGroup.ALL.name()); - if (source == null) { - LOGGER.warn("Project was supposed to be cloned, but it does not exist anymore"); - return null; - } - if (doesProjectExist(source.getName(), newVersion)) { - // Project cloning is an asynchronous process. When receiving the clone request, we already perform - // this check. It is possible though that a project with the new version is created synchronously - // between the clone event being dispatched, and it being processed. - LOGGER.warn("Project was supposed to be cloned to version %s, but that version already exists".formatted(newVersion)); - return null; - } Project project = new Project(); project.setAuthors(source.getAuthors()); project.setManufacturer(source.getManufacturer()); @@ -544,6 +646,7 @@ public Project clone( project.setVersion(newVersion); project.setClassifier(source.getClassifier()); project.setActive(source.isActive()); + project.setIsLatest(makeCloneLatest); project.setCpe(source.getCpe()); project.setPurl(source.getPurl()); project.setSwidTagId(source.getSwidTagId()); @@ -551,6 +654,13 @@ public Project clone( project.setDirectDependencies(source.getDirectDependencies()); } project.setParent(source.getParent()); + + // Remove isLatest flag from current latest project version, if this project will be the latest now + if(oldLatestProject != null) { + oldLatestProject.setIsLatest(false); + persist(oldLatestProject); + } + project = persist(project); if (source.getMetadata() != null) { @@ -703,6 +813,10 @@ public Project clone( return project; }); + if(oldLatestProject != null) { + // if we removed isLatest flag from old version, dispatch update event for the old version + Event.dispatch(new IndexEvent(IndexEvent.Action.UPDATE, oldLatestProject)); + } Event.dispatch(new IndexEvent(IndexEvent.Action.CREATE, clonedProject)); commitSearchIndex(true, Project.class); return clonedProject; diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index a610387b8c..190758ceeb 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -417,6 +417,10 @@ public Project getProject(final String name, final String version) { return getProjectQueryManager().getProject(name, version); } + public Project getLatestProjectVersion(final String name) { + return getProjectQueryManager().getLatestProjectVersion(name); + } + public PaginatedResult getProjects(final Team team, final boolean excludeInactive, final boolean bypass, final boolean onlyRoot) { return getProjectQueryManager().getProjects(team, excludeInactive, bypass, onlyRoot); } @@ -484,6 +488,10 @@ public List resolveTags(final List tags) { public Project createProject(String name, String description, String version, List tags, Project parent, PackageURL purl, boolean active, boolean commitIndex) { return getProjectQueryManager().createProject(name, description, version, tags, parent, purl, active, commitIndex); } + public Project createProject(String name, String description, String version, List tags, Project parent, + PackageURL purl, boolean active, boolean isLatest, boolean commitIndex) { + return getProjectQueryManager().createProject(name, description, version, tags, parent, purl, active, isLatest, commitIndex); + } public Project createProject(final Project project, List tags, boolean commitIndex) { return getProjectQueryManager().createProject(project, tags, commitIndex); @@ -504,6 +512,13 @@ public Project clone(UUID from, String newVersion, boolean includeTags, boolean includeComponents, includeServices, includeAuditHistory, includeACL, includePolicyViolations); } + public Project clone(UUID from, String newVersion, boolean includeTags, boolean includeProperties, + boolean includeComponents, boolean includeServices, boolean includeAuditHistory, + boolean includeACL, boolean includePolicyViolations, boolean makeCloneLatest) { + return getProjectQueryManager().clone(from, newVersion, includeTags, includeProperties, + includeComponents, includeServices, includeAuditHistory, includeACL, includePolicyViolations, makeCloneLatest); + } + public Project updateLastBomImport(Project p, Date date, String bomFormat) { return getProjectQueryManager().updateLastBomImport(p, date, bomFormat); } diff --git a/src/main/java/org/dependencytrack/resources/v1/BomResource.java b/src/main/java/org/dependencytrack/resources/v1/BomResource.java index 9c83022282..ddfd9c7f8b 100644 --- a/src/main/java/org/dependencytrack/resources/v1/BomResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/BomResource.java @@ -327,8 +327,20 @@ public Response uploadBom(@Parameter(required = true) BomSubmitRequest request) return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified parent project is forbidden").build(); } } + final String trimmedProjectName = StringUtils.trimToNull(request.getProjectName()); + if(request.isLatestProjectVersion()) { + final Project oldLatest = qm.getLatestProjectVersion(trimmedProjectName); + if(oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { + return Response.status(Response.Status.FORBIDDEN) + .entity("Cannot create latest version for project with this name. Access to current latest " + + "version is forbidden!") + .build(); + } + } - project = qm.createProject(StringUtils.trimToNull(request.getProjectName()), null, StringUtils.trimToNull(request.getProjectVersion()), request.getProjectTags(), parent, null, true, true); + project = qm.createProject(trimmedProjectName, null, + StringUtils.trimToNull(request.getProjectVersion()), request.getProjectTags(), parent, + null, true, request.isLatestProjectVersion(), true); Principal principal = getPrincipal(); qm.updateNewProjectACL(project, principal); } else { @@ -390,6 +402,7 @@ public Response uploadBom( @FormDataParam("parentName") String parentName, @FormDataParam("parentVersion") String parentVersion, @FormDataParam("parentUUID") String parentUUID, + @DefaultValue("false") @FormDataParam("isLatest") boolean isLatest, @Parameter(schema = @Schema(type = "string")) @FormDataParam("bom") final List artifactParts ) { if (projectUuid != null) { // behavior in v3.0.0 @@ -421,10 +434,19 @@ public Response uploadBom( return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified parent project is forbidden").build(); } } + if(isLatest) { + final Project oldLatest = qm.getLatestProjectVersion(trimmedProjectName); + if(oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { + return Response.status(Response.Status.FORBIDDEN) + .entity("Cannot create latest version for project with this name. Access to current latest " + + "version is forbidden!") + .build(); + } + } final List tags = (projectTags != null && !projectTags.isBlank()) ? Arrays.stream(projectTags.split(",")).map(String::trim).filter(not(String::isEmpty)).map(Tag::new).toList() : null; - project = qm.createProject(trimmedProjectName, null, trimmedProjectVersion, tags, parent, null, true, true); + project = qm.createProject(trimmedProjectName, null, trimmedProjectVersion, tags, parent, null, true, isLatest, true); Principal principal = getPrincipal(); qm.updateNewProjectACL(project, principal); } else { diff --git a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java index d9dcf34432..fd66c5a466 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java @@ -203,6 +203,42 @@ public Response getProject( } } + + @GET + @Path("/latest/{name}") + @Produces(MediaType.APPLICATION_JSON) + @Operation( + summary = "Returns the latest version of a project by its name", + description = "

      Requires permission VIEW_PORTFOLIO

      " + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "The latest version of the specified project", + content = @Content(schema = @Schema(implementation = Project.class)) + ), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "403", description = "Access to the specified project is forbidden"), + @ApiResponse(responseCode = "404", description = "The project could not be found") + }) + @PermissionRequired(Permissions.Constants.VIEW_PORTFOLIO) + public Response getLatestProjectByName( + @Parameter(description = "The name of the project to retrieve the latest version of", required = true) + @PathParam("name") String name) { + try (QueryManager qm = new QueryManager()) { + final Project project = qm.getLatestProjectVersion(name); + if (project != null) { + if (qm.hasAccess(super.getPrincipal(), project)) { + return Response.ok(project).build(); + } else { + return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + } + } else { + return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); + } + } + } + @GET @Path("/tag/{tag}") @Produces(MediaType.APPLICATION_JSON) @@ -294,6 +330,7 @@ public Response getProjectsByClassifier( ), @ApiResponse(responseCode = "400", description = "Bad Request"), @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "403", description = "The project version cannot be created as latest version because access to current latest version is forbidden."), @ApiResponse(responseCode = "409", description = """
      • An inactive Parent cannot be selected as parent, or
      • @@ -329,6 +366,16 @@ public Response createProject(final Project jsonProject) { .build(); } + if(jsonProject.isLatest()) { + final Project oldLatest = qm.getLatestProjectVersion(jsonProject.getName()); + if(oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { + return Response.status(Response.Status.FORBIDDEN) + .entity("Cannot create latest version for project with this name. Access to current latest " + + "version is forbidden!") + .build(); + } + } + if (jsonProject.getParent() != null && jsonProject.getParent().getUuid() != null) { Project parent = qm.getObjectByUuid(Project.class, jsonProject.getParent().getUuid()); jsonProject.setParent(parent); @@ -428,6 +475,8 @@ public Response createProject(final Project jsonProject) { content = @Content(schema = @Schema(implementation = Project.class)) ), @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "403", description = "The project version cannot be set as latest version " + + "because access to current latest version is forbidden."), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found"), @ApiResponse(responseCode = "409", description = """
          @@ -461,14 +510,27 @@ public Response updateProject(Project jsonProject) { if (!qm.hasAccess(super.getPrincipal(), project)) { return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } - final String name = StringUtils.trimToNull(jsonProject.getName()); + String name = StringUtils.trimToNull(jsonProject.getName()); + // Name cannot be empty or null - prevent it + if (name == null) { + name = project.getName(); + jsonProject.setName(name); + } + + // if project is newly set to latest, ensure user has access to current latest version to modify it + if(jsonProject.isLatest() && !project.isLatest()) { + final Project oldLatest = qm.getLatestProjectVersion(name); + if(oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { + return Response.status(Response.Status.FORBIDDEN) + .entity("Cannot set this project version to latest. Access to current latest " + + "version is forbidden!") + .build(); + } + } + final String version = StringUtils.trimToNull(jsonProject.getVersion()); final Project tmpProject = qm.getProject(name, version); if (tmpProject == null || (tmpProject.getUuid().equals(project.getUuid()))) { - // Name cannot be empty or null - prevent it - if (name == null) { - jsonProject.setName(project.getName()); - } try { project = qm.updateProject(jsonProject, true); } catch (IllegalArgumentException e){ @@ -536,6 +598,19 @@ public Response patchProject( if (!qm.hasAccess(super.getPrincipal(), project)) { return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } + + // if project is newly set to latest, ensure user has access to current latest version to modify it + if(jsonProject.isLatest() && !project.isLatest()) { + final var oldName = jsonProject.getName() != null ? jsonProject.getName() : project.getName(); + final Project oldLatest = qm.getLatestProjectVersion(oldName); + if(oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { + return Response.status(Response.Status.FORBIDDEN) + .entity("Cannot set this project version to latest. Access to current latest " + + "version is forbidden!") + .build(); + } + } + var modified = false; project = qm.detachWithGroups(project, List.of(FetchGroup.DEFAULT, Project.FetchGroup.PARENT.name())); modified |= setIfDifferent(jsonProject, project, Project::getName, Project::setName); @@ -555,6 +630,7 @@ public Response patchProject( modified |= setIfDifferent(jsonProject, project, Project::isActive, Project::setActive); modified |= setIfDifferent(jsonProject, project, Project::getManufacturer, Project::setManufacturer); modified |= setIfDifferent(jsonProject, project, Project::getSupplier, Project::setSupplier); + modified |= setIfDifferent(jsonProject, project, Project::isLatest, Project::setIsLatest); if (jsonProject.getParent() != null && jsonProject.getParent().getUuid() != null) { final Project parent = qm.getObjectByUuid(Project.class, jsonProject.getParent().getUuid()); if (parent == null) { @@ -673,6 +749,9 @@ public Response deleteProject( content = @Content(schema = @Schema(implementation = BomUploadResponse.class)) ), @ApiResponse(responseCode = "401", description = "Unauthorized"), + + @ApiResponse(responseCode = "403", description = "The project clone cannot be set to latest version " + + "because access to current latest version is forbidden."), @ApiResponse(responseCode = "404", description = "The UUID of the project could not be found") }) @PermissionRequired(Permissions.Constants.PORTFOLIO_MANAGEMENT) @@ -692,6 +771,17 @@ public Response cloneProject(CloneProjectRequest jsonRequest) { return Response.status(Response.Status.CONFLICT).entity("A project with the specified name and version already exists.").build(); } + // if project is newly set to latest, ensure user has access to current latest version to modify it + if(jsonRequest.makeCloneLatest() && !sourceProject.isLatest()) { + final Project oldLatest = qm.getLatestProjectVersion(sourceProject.getName()); + if(oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { + return Response.status(Response.Status.FORBIDDEN) + .entity("Cannot set cloned project version to latest. Access to current latest " + + "version is forbidden!") + .build(); + } + } + LOGGER.info("Project " + sourceProject + " is being cloned by " + super.getPrincipal().getName()); CloneProjectEvent event = new CloneProjectEvent(jsonRequest); Event.dispatch(event); diff --git a/src/main/java/org/dependencytrack/resources/v1/vo/BomSubmitRequest.java b/src/main/java/org/dependencytrack/resources/v1/vo/BomSubmitRequest.java index 99129c7a81..b6171d1314 100644 --- a/src/main/java/org/dependencytrack/resources/v1/vo/BomSubmitRequest.java +++ b/src/main/java/org/dependencytrack/resources/v1/vo/BomSubmitRequest.java @@ -72,13 +72,16 @@ public final class BomSubmitRequest { private final boolean autoCreate; + private final boolean isLatestProjectVersion; + public BomSubmitRequest(String project, String projectName, String projectVersion, List projectTags, boolean autoCreate, + boolean isLatestProjectVersion, String bom) { - this(project, projectName, projectVersion, projectTags, autoCreate, null, null, null, bom); + this(project, projectName, projectVersion, projectTags, autoCreate, null, null, null, isLatestProjectVersion, bom); } @JsonCreator @@ -90,6 +93,7 @@ public BomSubmitRequest(@JsonProperty(value = "project") String project, @JsonProperty(value = "parentUUID") String parentUUID, @JsonProperty(value = "parentName") String parentName, @JsonProperty(value = "parentVersion") String parentVersion, + @JsonProperty(value = "isLatestProjectVersion", defaultValue = "false") boolean isLatestProjectVersion, @JsonProperty(value = "bom", required = true) String bom) { this.project = project; this.projectName = projectName; @@ -99,6 +103,7 @@ public BomSubmitRequest(@JsonProperty(value = "project") String project, this.parentUUID = parentUUID; this.parentName = parentName; this.parentVersion = parentVersion; + this.isLatestProjectVersion = isLatestProjectVersion; this.bom = bom; } @@ -141,6 +146,9 @@ public boolean isAutoCreate() { return autoCreate; } + @JsonProperty("isLatestProjectVersion") + public boolean isLatestProjectVersion() { return isLatestProjectVersion; } + @Schema( description = "Base64 encoded BOM", required = true, diff --git a/src/main/java/org/dependencytrack/resources/v1/vo/CloneProjectRequest.java b/src/main/java/org/dependencytrack/resources/v1/vo/CloneProjectRequest.java index 2850d2369f..c86f951c66 100644 --- a/src/main/java/org/dependencytrack/resources/v1/vo/CloneProjectRequest.java +++ b/src/main/java/org/dependencytrack/resources/v1/vo/CloneProjectRequest.java @@ -61,6 +61,8 @@ public class CloneProjectRequest { private final boolean includePolicyViolations; + private final boolean makeCloneLatest; + @JsonCreator public CloneProjectRequest(@JsonProperty(value = "project", required = true) String project, @JsonProperty(value = "version", required = true) String version, @@ -71,8 +73,10 @@ public CloneProjectRequest(@JsonProperty(value = "project", required = true) Str @JsonProperty(value = "includeServices") boolean includeServices, @JsonProperty(value = "includeAuditHistory") boolean includeAuditHistory, @JsonProperty(value = "includeACL") boolean includeACL, - @JsonProperty(value = "includePolicyViolations") boolean includePolicyViolations) { - if (includeDependencies) { // For backward compatibility + @JsonProperty(value = "includePolicyViolations") boolean includePolicyViolations, + @JsonProperty(value = "makeCloneLatest", defaultValue = "false") boolean makeCloneLatest) { + + if (includeDependencies) { // For backward compatibility includeComponents = true; } this.project = project; @@ -85,6 +89,7 @@ public CloneProjectRequest(@JsonProperty(value = "project", required = true) Str this.includeAuditHistory = includeAuditHistory; this.includeACL = includeACL; this.includePolicyViolations = includePolicyViolations; + this.makeCloneLatest = makeCloneLatest; } public String getProject() { @@ -127,4 +132,7 @@ public boolean includePolicyViolations() { return includePolicyViolations; } + public boolean makeCloneLatest() { + return makeCloneLatest; + } } diff --git a/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java b/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java index a8dc36f363..69c10975be 100644 --- a/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java +++ b/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java @@ -48,7 +48,8 @@ public void executeUpgrade(final AlpineQueryManager qm, final Connection connect migrateBomValidationConfigProperty(connection); extendTeamNameColumnMaxLength(connection); migrateAuthorToAuthors(connection); - dropAuthorColumns(connection); + dropAuthorColumns(connection); + setInitialIsLatestFlags(connection); } private static void removeExperimentalBomUploadProcessingV2ConfigProperty(final Connection connection) throws SQLException { @@ -286,4 +287,15 @@ private void dropAuthorColumns(final Connection connection) throws SQLException } } + + + private void setInitialIsLatestFlags(Connection connection) throws SQLException { + LOGGER.info("Setting IS_LATEST flag to false for all Project entries"); + try (final Statement stmt = connection.createStatement()) { + stmt.executeUpdate(""" + UPDATE "PROJECT" SET "IS_LATEST" = false + """); + } + } + } diff --git a/src/test/java/org/dependencytrack/ResourceTest.java b/src/test/java/org/dependencytrack/ResourceTest.java index ef37c77967..2057fc2c94 100644 --- a/src/test/java/org/dependencytrack/ResourceTest.java +++ b/src/test/java/org/dependencytrack/ResourceTest.java @@ -24,6 +24,7 @@ import alpine.server.auth.PasswordService; import alpine.server.persistence.PersistenceManagerFactory; import org.dependencytrack.auth.Permissions; +import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.persistence.QueryManager; import org.junit.After; import org.junit.Before; @@ -123,6 +124,16 @@ public void initializeWithPermissions(Permissions... permissions) { qm.persist(team); } + protected void enablePortfolioAccessControl() { + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + null + ); + } + protected String getPlainTextBody(Response response) { return response.readEntity(String.class); } diff --git a/src/test/java/org/dependencytrack/event/CloneProjectEventTest.java b/src/test/java/org/dependencytrack/event/CloneProjectEventTest.java index 1f46dabfe8..ebf1bfe00a 100644 --- a/src/test/java/org/dependencytrack/event/CloneProjectEventTest.java +++ b/src/test/java/org/dependencytrack/event/CloneProjectEventTest.java @@ -29,7 +29,9 @@ public class CloneProjectEventTest { @Test public void testEvent() { UUID uuid = UUID.randomUUID(); - CloneProjectRequest request = new CloneProjectRequest(uuid.toString(), "1.0", true, true, true, true, true, true, true, true); + CloneProjectRequest request = new CloneProjectRequest(uuid.toString(), "1.0", true, + true, true, true, true, true, + true, true, false); CloneProjectEvent event = new CloneProjectEvent(request); Assert.assertEquals(request, event.getRequest()); } diff --git a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java index e99a365dae..30fa40a102 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BomResourceTest.java @@ -821,7 +821,7 @@ public void uploadBomTest() throws Exception { initializeWithPermissions(Permissions.BOM_UPLOAD); Project project = qm.createProject("Acme Example", null, "1.0", null, null, null, true, false); String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); - BomSubmitRequest request = new BomSubmitRequest(project.getUuid().toString(), null, null, null, false, bomString); + BomSubmitRequest request = new BomSubmitRequest(project.getUuid().toString(), null, null, null, false, false, bomString); Response response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -836,7 +836,7 @@ public void uploadBomTest() throws Exception { public void uploadBomInvalidProjectTest() throws Exception { initializeWithPermissions(Permissions.BOM_UPLOAD); String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); - BomSubmitRequest request = new BomSubmitRequest(UUID.randomUUID().toString(), null, null, null, false, bomString); + BomSubmitRequest request = new BomSubmitRequest(UUID.randomUUID().toString(), null, null, null, false, false, bomString); Response response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -850,7 +850,7 @@ public void uploadBomInvalidProjectTest() throws Exception { public void uploadBomAutoCreateTest() throws Exception { initializeWithPermissions(Permissions.BOM_UPLOAD, Permissions.PROJECT_CREATION_UPLOAD); String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); - BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", null, true, bomString); + BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", null, true, false, bomString); Response response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -872,7 +872,8 @@ public void uploadBomAutoCreateWithTagsTest() throws Exception { tag.setName(name); return tag; }).collect(Collectors.toList()); - BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", tags, true, bomString); + BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", + tags, true, false, bomString); Response response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -924,7 +925,8 @@ public void uploadBomAutoCreateWithTagsMultipartTest() throws Exception { @Test public void uploadBomUnauthorizedTest() throws Exception { String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); - BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", null, true, bomString); + BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", + null, true, false, bomString); Response response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -933,12 +935,57 @@ public void uploadBomUnauthorizedTest() throws Exception { Assert.assertEquals("The principal does not have permission to create project.", body); } + @Test + public void uploadBomAutoCreateLatestWithAclTest() throws Exception { + initializeWithPermissions(Permissions.BOM_UPLOAD, Permissions.PROJECT_CREATION_UPLOAD); + enablePortfolioAccessControl(); + + final var accessLatestProject = new Project(); + accessLatestProject.setName("acme-app-a"); + accessLatestProject.setVersion("1.0.0"); + accessLatestProject.setIsLatest(true); + accessLatestProject.setAccessTeams(List.of(team)); + qm.persist(accessLatestProject); + + String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); + BomSubmitRequest request = new BomSubmitRequest(null, accessLatestProject.getName(), + "1.0.1", null, true, true, bomString); + Response response = jersey.target(V1_BOM).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(request, MediaType.APPLICATION_JSON)); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertNotNull(json); + Assert.assertNotNull(json.getString("token")); + } + + @Test + public void uploadBomAutoCreateLatestWithAclNoAccessTest() throws Exception { + initializeWithPermissions(Permissions.BOM_UPLOAD, Permissions.PROJECT_CREATION_UPLOAD); + enablePortfolioAccessControl(); + + final var noAccessLatestProject = new Project(); + noAccessLatestProject.setName("acme-app-a"); + noAccessLatestProject.setVersion("1.0.0"); + noAccessLatestProject.setIsLatest(true); + qm.persist(noAccessLatestProject); + + String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); + BomSubmitRequest request = new BomSubmitRequest(null, noAccessLatestProject.getName(), + "1.0.1", null, true, true, bomString); + Response response = jersey.target(V1_BOM).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(request, MediaType.APPLICATION_JSON)); + Assert.assertEquals(403, response.getStatus(), 0); + } + @Test public void uploadBomAutoCreateTestWithParentTest() throws Exception { initializeWithPermissions(Permissions.BOM_UPLOAD, Permissions.PROJECT_CREATION_UPLOAD); String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); // Upload parent project - BomSubmitRequest request = new BomSubmitRequest(null, "Acme Parent", "1.0", null, true, bomString); + BomSubmitRequest request = new BomSubmitRequest(null, "Acme Parent", "1.0", + null, true, false, bomString); Response response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -950,7 +997,7 @@ public void uploadBomAutoCreateTestWithParentTest() throws Exception { String parentUUID = parent.getUuid().toString(); // Upload first child, search parent by UUID - request = new BomSubmitRequest(null, "Acme Example", "1.0", null, true, parentUUID, null, null, bomString); + request = new BomSubmitRequest(null, "Acme Example", "1.0", null, true, parentUUID, null, null, false, bomString); response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -966,7 +1013,7 @@ public void uploadBomAutoCreateTestWithParentTest() throws Exception { // Upload second child, search parent by name+ver - request = new BomSubmitRequest(null, "Acme Example", "2.0", null, true, null, "Acme Parent", "1.0", bomString); + request = new BomSubmitRequest(null, "Acme Example", "2.0", null, true, null, "Acme Parent", "1.0", false, bomString); response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -981,7 +1028,7 @@ public void uploadBomAutoCreateTestWithParentTest() throws Exception { Assert.assertEquals(parentUUID, child.getParent().getUuid().toString()); // Upload third child, specify parent's UUID, name, ver. Name and ver are ignored when UUID is specified. - request = new BomSubmitRequest(null, "Acme Example", "3.0", null, true, parentUUID, "Non-existent parent", "1.0", bomString); + request = new BomSubmitRequest(null, "Acme Example", "3.0", null, true, parentUUID, "Non-existent parent", "1.0", false, bomString); response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -1000,7 +1047,7 @@ public void uploadBomAutoCreateTestWithParentTest() throws Exception { public void uploadBomInvalidParentTest() throws Exception { initializeWithPermissions(Permissions.BOM_UPLOAD, Permissions.PROJECT_CREATION_UPLOAD); String bomString = Base64.getEncoder().encodeToString(resourceToByteArray("/unit/bom-1.xml")); - BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", null, true, UUID.randomUUID().toString(), null, null, bomString); + BomSubmitRequest request = new BomSubmitRequest(null, "Acme Example", "1.0", null, true, UUID.randomUUID().toString(), null, null, false, bomString); Response response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); @@ -1008,7 +1055,7 @@ public void uploadBomInvalidParentTest() throws Exception { String body = getPlainTextBody(response); Assert.assertEquals("The parent component could not be found.", body); - request = new BomSubmitRequest(null, "Acme Example", "2.0", null, true, null, "Non-existent parent", null, bomString); + request = new BomSubmitRequest(null, "Acme Example", "2.0", null, true, null, "Non-existent parent", null, false, bomString); response = jersey.target(V1_BOM).request() .header(X_API_KEY, apiKey) .put(Entity.entity(request, MediaType.APPLICATION_JSON)); diff --git a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java index 4efa8fcd09..4298f5a02a 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java @@ -116,14 +116,7 @@ public void getProjectsDefaultRequestTest() { @Test // https://github.com/DependencyTrack/dependency-track/issues/2583 public void getProjectsWithAclEnabledTest() { - // Enable portfolio access control. - qm.createConfigProperty( - ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), - ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), - "true", - ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), - null - ); + enablePortfolioAccessControl(); // Create project and give access to current principal's team. final Project accessProject = qm.createProject("acme-app-a", null, "1.0.0", null, null, null, true, false); @@ -304,12 +297,14 @@ public void getProjectByUuidTest() { "name": "acme-app-child", "version": "1.0.0", "uuid": "${json-unit.matches:childUuid}", - "active": true + "active": true, + "isLatest":false } ], "properties": [], "tags": [], "active": true, + "isLatest":false, "versions": [ { "uuid": "${json-unit.matches:projectUuid}", @@ -522,7 +517,8 @@ public void createProjectAsUserWithAclEnabledAndExistingTeamByUuidTest() { "children": [], "properties": [], "tags": [], - "active": true + "active": true, + "isLatest":false } """); @@ -567,7 +563,8 @@ public void createProjectAsUserWithAclEnabledAndExistingTeamByNameTest() { "children": [], "properties": [], "tags": [], - "active": true + "active": true, + "isLatest":false } """); @@ -607,7 +604,8 @@ public void createProjectAsUserWithAclEnabledAndWithoutTeamTest() { "children": [], "properties": [], "tags": [], - "active": true + "active": true, + "isLatest":false } """); @@ -688,7 +686,8 @@ public void createProjectAsUserWithAclEnabledAndNotMemberOfTeamAdminTest() { "children": [], "properties": [], "tags": [], - "active": true + "active": true, + "isLatest":false } """); @@ -797,13 +796,90 @@ public void createProjectAsApiKeyWithAclEnabledAndWithExistentTeamTest() { "children": [], "properties": [], "tags": [], - "active": true + "active": true, + "isLatest":false } """); assertThat(qm.getAllProjects()).satisfiesExactly(project -> assertThat(project.getAccessTeams()).extracting(Team::getName).containsOnly(team.getName())); } + @Test + public void createProjectAsLatestTest() { + Project project = new Project(); + project.setName("Acme Example"); + project.setVersion("1.0"); + Response response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + Assert.assertEquals(201, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + // ensure initial value is false when not specified + Assert.assertFalse(json.getBoolean("isLatest")); + + project.setVersion("2.0"); + project.setIsLatest(true); + response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + Assert.assertEquals(201, response.getStatus(), 0); + json = parseJsonObject(response); + // ensure value of latest version is true when specified + Assert.assertTrue(json.getBoolean("isLatest")); + String v20uuid = json.getString("uuid"); + + project.setVersion("2.1"); + response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + Assert.assertEquals(201, response.getStatus(), 0); + json = parseJsonObject(response); + // ensure value of latest version is true when specified + Assert.assertTrue(json.getBoolean("isLatest")); + // ensure v2.0 is no longer latest + Assert.assertFalse(qm.getProject(v20uuid).isLatest()); + } + + @Test + public void createProjectAsLatestWithACLTest() { + enablePortfolioAccessControl(); + + final var accessProject = new Project(); + accessProject.setName("acme-app-a"); + accessProject.setVersion("1.0.0"); + accessProject.setIsLatest(true); + accessProject.setAccessTeams(List.of(team)); + qm.persist(accessProject); + + final var noAccessProject = new Project(); + noAccessProject.setName("acme-app-b"); + noAccessProject.setVersion("2.0.0"); + noAccessProject.setIsLatest(true); + qm.persist(noAccessProject); + + Project project = new Project(); + project.setName(accessProject.getName()); + project.setVersion("1.0.1"); + project.setIsLatest(true); + Response response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + Assert.assertEquals(201, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertTrue(json.getBoolean("isLatest")); + + project.setName(noAccessProject.getName()); + project.setVersion("3.0.0"); + response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(project, MediaType.APPLICATION_JSON)); + Assert.assertEquals(403, response.getStatus(), 0); + } @Test public void updateProjectTest() { @@ -921,6 +997,104 @@ public void updateProjectDuplicateTest() { Assert.assertEquals("A project with the specified name and version already exists.", body); } + @Test + public void updateProjectAsLatestTest() { + // create project not as latest + Project project = qm.createProject("ABC", null, "1.0", null, null, null, + true, false, false); + + // make it latest by update + var jsonProject = qm.detach(project); + jsonProject.setIsLatest(true); + Response response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(jsonProject, MediaType.APPLICATION_JSON)); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertTrue(json.getBoolean("isLatest")); + + // add another project version, "forget" to make it latest + final Project newProject = qm.createProject("ABC", null, "1.0.1", null, null, null, + true, false, false); + // make the new version latest afterwards via update + jsonProject = qm.detach(newProject); + jsonProject.setIsLatest(true); + response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(jsonProject, MediaType.APPLICATION_JSON)); + Assert.assertEquals(200, response.getStatus(), 0); + json = parseJsonObject(response); + // ensure is now latest + Assert.assertTrue(json.getBoolean("isLatest")); + // ensure old is no longer latest + Assert.assertFalse(qm.getProject(project.getName(), project.getVersion()).isLatest()); + } + + @Test + public void updateProjectAsLatestWithACLAndAccessTest() { + enablePortfolioAccessControl(); + + final var accessLatestProject = new Project(); + accessLatestProject.setName("acme-app-a"); + accessLatestProject.setVersion("1.0.0"); + accessLatestProject.setIsLatest(true); + accessLatestProject.setAccessTeams(List.of(team)); + qm.persist(accessLatestProject); + + final var accessNotLatestProject = new Project(); + accessNotLatestProject.setName("acme-app-a"); + accessNotLatestProject.setVersion("1.0.1"); + accessNotLatestProject.setIsLatest(false); + accessNotLatestProject.setAccessTeams(List.of(team)); + qm.persist(accessNotLatestProject); + + // make the new version latest afterwards via update + final var jsonProject = qm.detach(accessNotLatestProject); + jsonProject.setIsLatest(true); + Response response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(jsonProject, MediaType.APPLICATION_JSON)); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + // ensure is now latest + Assert.assertTrue(json.getBoolean("isLatest")); + // ensure old is no longer latest (bypass db cache) + qm.getPersistenceManager().refreshAll(); + Assert.assertFalse(qm.getProject(accessLatestProject.getName(), accessLatestProject.getVersion()).isLatest()); + } + + @Test + public void updateProjectAsLatestWithACLAndNoAccessTest() { + enablePortfolioAccessControl(); + + final var noAccessLatestProject = new Project(); + noAccessLatestProject.setName("acme-app-a"); + noAccessLatestProject.setVersion("1.0.0"); + noAccessLatestProject.setIsLatest(true); + qm.persist(noAccessLatestProject); + + final var accessNotLatestProject = new Project(); + accessNotLatestProject.setName("acme-app-a"); + accessNotLatestProject.setVersion("1.0.1"); + accessNotLatestProject.setIsLatest(false); + accessNotLatestProject.setAccessTeams(List.of(team)); + qm.persist(accessNotLatestProject); + + // make the new version latest afterwards via update (but have no access to old latest) + final var jsonProject = qm.detach(accessNotLatestProject); + jsonProject.setIsLatest(true); + Response response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(jsonProject, MediaType.APPLICATION_JSON)); + Assert.assertEquals(403, response.getStatus(), 0); + // ensure old is still latest + Assert.assertTrue(qm.getProject(noAccessLatestProject.getName(), noAccessLatestProject.getVersion()).isLatest()); + } + @Test public void deleteProjectTest() { Project project = qm.createProject("ABC", null, "1.0", null, null, null, true, false); @@ -1069,6 +1243,7 @@ public void patchProjectSuccessfullyPatchedTest() { } ], "active": false, + "isLatest":false, "children": [] } """); @@ -1141,7 +1316,8 @@ public void patchProjectParentTest() { }, "properties": [], "tags": [], - "active": true + "active": true, + "isLatest":false } """); @@ -1176,6 +1352,109 @@ public void patchProjectParentNotFoundTest() { assertThat(project.getParent().getUuid()).isEqualTo(parent.getUuid()); } + @Test + public void patchProjectAsLatestTest() { + // create project not as latest + Project project = qm.createProject("ABC", null, "1.0", null, null, null, + true, false, false); + + // make it latest by patch + var jsonProject = new Project(); + jsonProject.setIsLatest(true); + Response response = jersey.target(V1_PROJECT + "/" + project.getUuid()) + .request() + .header(X_API_KEY, apiKey) + .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true) + .method(HttpMethod.PATCH, Entity.json(jsonProject)); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertTrue(json.getBoolean("isLatest")); + + // add another project version, "forget" to make it latest + final Project newProject = qm.createProject("ABC", null, "1.0.1", null, null, null, + true, false, false); + // make the new version latest afterwards via update + jsonProject = new Project(); + jsonProject.setIsLatest(true); + response = jersey.target(V1_PROJECT + "/" + newProject.getUuid()) + .request() + .header(X_API_KEY, apiKey) + .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true) + .method(HttpMethod.PATCH, Entity.json(jsonProject)); + Assert.assertEquals(200, response.getStatus(), 0); + json = parseJsonObject(response); + // ensure is now latest + Assert.assertTrue(json.getBoolean("isLatest")); + // ensure old is no longer latest + Assert.assertFalse(qm.getProject(project.getName(), project.getVersion()).isLatest()); + } + + @Test + public void patchProjectAsLatestWithACLAndAccessTest() { + enablePortfolioAccessControl(); + + final var accessLatestProject = new Project(); + accessLatestProject.setName("acme-app-a"); + accessLatestProject.setVersion("1.0.0"); + accessLatestProject.setIsLatest(true); + accessLatestProject.setAccessTeams(List.of(team)); + qm.persist(accessLatestProject); + + final var accessNotLatestProject = new Project(); + accessNotLatestProject.setName("acme-app-a"); + accessNotLatestProject.setVersion("1.0.1"); + accessNotLatestProject.setIsLatest(false); + accessNotLatestProject.setAccessTeams(List.of(team)); + qm.persist(accessNotLatestProject); + + // make the new version latest afterwards via update + final var jsonProject = new Project(); + jsonProject.setIsLatest(true); + Response response = jersey.target(V1_PROJECT + "/" + accessNotLatestProject.getUuid()) + .request() + .header(X_API_KEY, apiKey) + .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true) + .method(HttpMethod.PATCH, Entity.json(jsonProject)); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + // ensure is now latest + Assert.assertTrue(json.getBoolean("isLatest")); + // ensure old is no longer latest (bypass db cache) + qm.getPersistenceManager().refreshAll(); + Assert.assertFalse(qm.getProject(accessLatestProject.getName(), accessLatestProject.getVersion()).isLatest()); + } + + @Test + public void patchProjectAsLatestWithACLAndNoAccessTest() { + enablePortfolioAccessControl(); + + final var noAccessLatestProject = new Project(); + noAccessLatestProject.setName("acme-app-a"); + noAccessLatestProject.setVersion("1.0.0"); + noAccessLatestProject.setIsLatest(true); + qm.persist(noAccessLatestProject); + + final var accessNotLatestProject = new Project(); + accessNotLatestProject.setName("acme-app-a"); + accessNotLatestProject.setVersion("1.0.1"); + accessNotLatestProject.setIsLatest(false); + accessNotLatestProject.setAccessTeams(List.of(team)); + qm.persist(accessNotLatestProject); + + // make the new version latest afterwards via update (but have no access to old latest) + final var jsonProject = new Project(); + jsonProject.setIsLatest(true); + Response response = jersey.target(V1_PROJECT + "/" + accessNotLatestProject.getUuid()) + .request() + .header(X_API_KEY, apiKey) + .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true) + .method(HttpMethod.PATCH, Entity.json(jsonProject)); + Assert.assertEquals(403, response.getStatus(), 0); + // ensure old is still latest + qm.getPersistenceManager().refreshAll(); + Assert.assertTrue(qm.getProject(noAccessLatestProject.getName(), noAccessLatestProject.getVersion()).isLatest()); + } + @Test public void getRootProjectsTest() { Project parent = qm.createProject("ABC", null, "1.0", null, null, null, true, false); @@ -1476,13 +1755,7 @@ public void cloneProjectConflictTest() { @Test public void cloneProjectWithAclTest() { - qm.createConfigProperty( - ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), - ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), - "true", - ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), - null - ); + enablePortfolioAccessControl(); final var accessProject = new Project(); accessProject.setName("acme-app-a"); @@ -1567,12 +1840,14 @@ public void issue3883RegressionTest() { "version": "1.0.0", "classifier": "APPLICATION", "uuid": "${json-unit.any-string}", - "active": true + "active": true, + "isLatest":false } ], "properties": [], "tags": [], "active": true, + "isLatest":false, "versions": [ { "uuid": "${json-unit.any-string}", @@ -1603,6 +1878,7 @@ public void issue3883RegressionTest() { "properties": [], "tags": [], "active": true, + "isLatest":false, "versions": [ { "uuid": "${json-unit.any-string}", @@ -1685,5 +1961,4 @@ public void issue4048RegressionTest() { .isNotEmpty(); } } - } From 7f31e781278746d47eebf406c40857d05dc243b2 Mon Sep 17 00:00:00 2001 From: Ralf King Date: Fri, 27 Sep 2024 18:07:27 +0200 Subject: [PATCH 177/429] Allow policies to be limited to projects marked with isLatest. Signed-off-by: Ralf King --- .../org/dependencytrack/model/Policy.java | 12 +++++ .../persistence/PolicyQueryManager.java | 4 +- .../persistence/QueryManager.java | 5 +- .../dependencytrack/policy/PolicyEngine.java | 3 ++ .../resources/v1/PolicyResource.java | 3 +- .../policy/PolicyEngineTest.java | 46 +++++++++++++++++++ 6 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/Policy.java b/src/main/java/org/dependencytrack/model/Policy.java index aeeacaf499..ca43c4d8c7 100644 --- a/src/main/java/org/dependencytrack/model/Policy.java +++ b/src/main/java/org/dependencytrack/model/Policy.java @@ -142,6 +142,10 @@ public enum ViolationState { @Column(name = "INCLUDE_CHILDREN", allowsNull = "true") // New column, must allow nulls on existing data bases) private boolean includeChildren; + @Persistent + @Column(name = "ONLY_FOR_LATEST_PROJECT_VERSION", allowsNull = "true") // New column, must allow nulls for existing data bases + private boolean onlyForLatestProjectVersion; + public long getId() { return id; } @@ -224,4 +228,12 @@ public boolean isIncludeChildren() { public void setIncludeChildren(boolean includeChildren) { this.includeChildren = includeChildren; } + + public boolean isOnlyForLatestProjectVersion() { + return onlyForLatestProjectVersion; + } + + public void setOnlyForLatestProjectVersion(boolean onlyForLatestProjectVersion) { + this.onlyForLatestProjectVersion = onlyForLatestProjectVersion; + } } diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index 653b5aa8be..9aff5f67e0 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -117,11 +117,13 @@ public Policy getPolicy(final String name) { * @param violationState the violation state * @return the created Policy */ - public Policy createPolicy(String name, Policy.Operator operator, Policy.ViolationState violationState) { + public Policy createPolicy(String name, Policy.Operator operator, Policy.ViolationState violationState, + boolean onlyForLatestProjectVersion) { final Policy policy = new Policy(); policy.setName(name); policy.setOperator(operator); policy.setViolationState(violationState); + policy.setOnlyForLatestProjectVersion(onlyForLatestProjectVersion); return persist(policy); } diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index 190758ceeb..c6f4fddd1a 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -690,7 +690,10 @@ public Policy getPolicy(final String name) { } public Policy createPolicy(String name, Policy.Operator operator, Policy.ViolationState violationState) { - return getPolicyQueryManager().createPolicy(name, operator, violationState); + return this.createPolicy(name, operator, violationState, false); + } + public Policy createPolicy(String name, Policy.Operator operator, Policy.ViolationState violationState, boolean onlyForLatestProjectVersion) { + return getPolicyQueryManager().createPolicy(name, operator, violationState, onlyForLatestProjectVersion); } public void removeProjectFromPolicies(final Project project) { diff --git a/src/main/java/org/dependencytrack/policy/PolicyEngine.java b/src/main/java/org/dependencytrack/policy/PolicyEngine.java index 7b6053718a..89b38f7253 100644 --- a/src/main/java/org/dependencytrack/policy/PolicyEngine.java +++ b/src/main/java/org/dependencytrack/policy/PolicyEngine.java @@ -80,6 +80,9 @@ private List evaluate(final QueryManager qm, final List final List policyViolations = new ArrayList<>(); final List existingPolicyViolations = qm.detach(qm.getAllPolicyViolations(component)); for (final Policy policy : policies) { + if(policy.isOnlyForLatestProjectVersion() && Boolean.FALSE.equals(component.getProject().isLatest())) { + continue; + } if (policy.isGlobal() || isPolicyAssignedToProject(policy, component.getProject()) || isPolicyAssignedToProjectTag(policy, component.getProject())) { LOGGER.debug("Evaluating component (" + component.getUuid() + ") against policy (" + policy.getUuid() + ")"); diff --git a/src/main/java/org/dependencytrack/resources/v1/PolicyResource.java b/src/main/java/org/dependencytrack/resources/v1/PolicyResource.java index 8bf4ab299a..f775fe047a 100644 --- a/src/main/java/org/dependencytrack/resources/v1/PolicyResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/PolicyResource.java @@ -157,7 +157,7 @@ public Response createPolicy(Policy jsonPolicy) { } policy = qm.createPolicy( StringUtils.trimToNull(jsonPolicy.getName()), - operator, violationState); + operator, violationState, jsonPolicy.isOnlyForLatestProjectVersion()); return Response.status(Response.Status.CREATED).entity(policy).build(); } else { return Response.status(Response.Status.CONFLICT).entity("A policy with the specified name already exists.").build(); @@ -194,6 +194,7 @@ public Response updatePolicy(Policy jsonPolicy) { policy.setOperator(jsonPolicy.getOperator()); policy.setViolationState(jsonPolicy.getViolationState()); policy.setIncludeChildren(jsonPolicy.isIncludeChildren()); + policy.setOnlyForLatestProjectVersion(jsonPolicy.isOnlyForLatestProjectVersion()); policy = qm.persist(policy); return Response.ok(policy).build(); } else { diff --git a/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java b/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java index 62463bc9dc..d3c7f7363d 100644 --- a/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java +++ b/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java @@ -195,6 +195,52 @@ public void noPolicyAssignedToParentProject() { Assert.assertEquals(0, violations.size()); } + @Test + public void policyForLatestTriggersOnLatestVersion() { + Policy policy = qm.createPolicy("Test Policy", Operator.ANY, ViolationState.INFO, true); + qm.createPolicyCondition(policy, Subject.SEVERITY, PolicyCondition.Operator.IS, Severity.CRITICAL.name()); + Project project = qm.createProject("My Project", null, "1", null, null, + null, true, true, false); + Component component = new Component(); + component.setName("Test Component"); + component.setVersion("1.0"); + component.setProject(project); + Vulnerability vulnerability = new Vulnerability(); + vulnerability.setVulnId("12345"); + vulnerability.setSource(Vulnerability.Source.INTERNAL); + vulnerability.setSeverity(Severity.CRITICAL); + qm.persist(project); + qm.persist(component); + qm.persist(vulnerability); + qm.addVulnerability(vulnerability, component, AnalyzerIdentity.INTERNAL_ANALYZER); + PolicyEngine policyEngine = new PolicyEngine(); + List violations = policyEngine.evaluate(List.of(component)); + Assert.assertEquals(1, violations.size()); + } + + @Test + public void policyForLatestTriggersNotOnNotLatestVersion() { + Policy policy = qm.createPolicy("Test Policy", Operator.ANY, ViolationState.INFO, true); + qm.createPolicyCondition(policy, Subject.SEVERITY, PolicyCondition.Operator.IS, Severity.CRITICAL.name()); + Project project = qm.createProject("My Project", null, "1", null, null, + null, true, false, false); + Component component = new Component(); + component.setName("Test Component"); + component.setVersion("1.0"); + component.setProject(project); + Vulnerability vulnerability = new Vulnerability(); + vulnerability.setVulnId("12345"); + vulnerability.setSource(Vulnerability.Source.INTERNAL); + vulnerability.setSeverity(Severity.CRITICAL); + qm.persist(project); + qm.persist(component); + qm.persist(vulnerability); + qm.addVulnerability(vulnerability, component, AnalyzerIdentity.INTERNAL_ANALYZER); + PolicyEngine policyEngine = new PolicyEngine(); + List violations = policyEngine.evaluate(List.of(component)); + Assert.assertEquals(0, violations.size()); + } + @Test public void determineViolationTypeTest() { PolicyCondition policyCondition = new PolicyCondition(); From f55714539277a4aca1551234130efa59607ee214 Mon Sep 17 00:00:00 2001 From: Ralf King Date: Fri, 27 Sep 2024 18:22:07 +0200 Subject: [PATCH 178/429] Add db migration for policy to avoid NULLs Signed-off-by: Ralf King --- .../java/org/dependencytrack/upgrade/v4120/v4120Updater.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java b/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java index 69c10975be..38425b2ca0 100644 --- a/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java +++ b/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java @@ -295,6 +295,9 @@ private void setInitialIsLatestFlags(Connection connection) throws SQLException stmt.executeUpdate(""" UPDATE "PROJECT" SET "IS_LATEST" = false """); + stmt.executeUpdate(""" + UPDATE "POLICY" SET "ONLY_FOR_LATEST_PROJECT_VERSION" = false + """); } } From cee37014e9a07e951c13f4d70ddbd6c4d1aa6c9c Mon Sep 17 00:00:00 2001 From: Ralf King Date: Sat, 28 Sep 2024 23:01:11 +0200 Subject: [PATCH 179/429] Fix failing tests (they relied on a bug fixed in these changes) Signed-off-by: Ralf King --- .../policy/PolicyEngineTest.java | 8 +++++-- .../tasks/scanners/TrivyAnalysisTaskTest.java | 21 ++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java b/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java index d3c7f7363d..75cb8a068f 100644 --- a/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java +++ b/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java @@ -388,13 +388,17 @@ public void notificationTest() { // Evaluate policies and ensure that a notification has been sent. final var policyEngine = new PolicyEngine(); assertThat(policyEngine.evaluate(List.of(component))).hasSize(1); - assertThat(NOTIFICATIONS).hasSize(1); + assertThat(NOTIFICATIONS).hasSize(2); // Create an additional policy condition that matches on the exact version of the component, // and re-evaluate policies. Ensure that only one notification per newly violated condition was sent. final var policyConditionB = qm.createPolicyCondition(policy, Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.2.3"); assertThat(policyEngine.evaluate(List.of(component))).hasSize(2); assertThat(NOTIFICATIONS).satisfiesExactly( + notification -> { + assertThat(notification.getScope()).isEqualTo(NotificationScope.PORTFOLIO.name()); + assertThat(notification.getGroup()).isEqualTo(NotificationGroup.PROJECT_CREATED.name()); + }, notification -> { assertThat(notification.getScope()).isEqualTo(NotificationScope.PORTFOLIO.name()); assertThat(notification.getGroup()).isEqualTo(NotificationGroup.POLICY_VIOLATION.name()); @@ -420,7 +424,7 @@ public void notificationTest() { // Delete a policy condition and re-evaluate policies again. No new notifications should be sent. qm.deletePolicyCondition(policyConditionA); assertThat(policyEngine.evaluate(List.of(component))).hasSize(1); - assertThat(NOTIFICATIONS).hasSize(2); + assertThat(NOTIFICATIONS).hasSize(3); } @Test diff --git a/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java b/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java index 9138cd1eac..fe29c545f5 100644 --- a/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java @@ -38,6 +38,7 @@ import org.dependencytrack.model.Severity; import org.dependencytrack.model.Vulnerability; import org.dependencytrack.notification.NotificationGroup; +import org.dependencytrack.notification.NotificationScope; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -413,13 +414,19 @@ public void testAnalyzeWithConnectionError() { assertThat(qm.getCount(ComponentAnalysisCache.class)).isZero(); - assertThat(NOTIFICATIONS).satisfiesExactly(notification -> { - assertThat(notification.getGroup()).isEqualTo(NotificationGroup.ANALYZER.name()); - assertThat(notification.getLevel()).isEqualTo(NotificationLevel.ERROR); - assertThat(notification.getContent()).isEqualTo(""" - An error occurred while communicating with a vulnerability intelligence source. \ - Check log for details. Connection reset"""); - }); + assertThat(NOTIFICATIONS).satisfiesExactly( + notification -> { + assertThat(notification.getScope()).isEqualTo(NotificationScope.PORTFOLIO.name()); + assertThat(notification.getGroup()).isEqualTo(NotificationGroup.PROJECT_CREATED.name()); + }, + notification -> { + assertThat(notification.getGroup()).isEqualTo(NotificationGroup.ANALYZER.name()); + assertThat(notification.getLevel()).isEqualTo(NotificationLevel.ERROR); + assertThat(notification.getContent()).isEqualTo(""" + An error occurred while communicating with a vulnerability intelligence source. \ + Check log for details. Connection reset"""); + } + ); wireMock.verify(exactly(1), postRequestedFor(urlPathEqualTo("/twirp/trivy.cache.v1.Cache/PutBlob"))); wireMock.verify(exactly(0), postRequestedFor(urlPathEqualTo("/twirp/trivy.scanner.v1.Scanner/Scan"))); From fe36568e36cb346d1b6653be4bb4c0eea0c38447 Mon Sep 17 00:00:00 2001 From: Ralf King Date: Sat, 28 Sep 2024 23:59:04 +0200 Subject: [PATCH 180/429] Handle Review comments: *Simplify Boolean to boolean in Project and Policy and remove unncessary migration script part *Move reads into transaction *remove unnecessary index *Rename ONLY_FOR_LATEST_PROJECT_VERSION to ONLY_LATEST_PROJECT_VERSION Signed-off-by: Ralf King --- .../org/dependencytrack/model/Policy.java | 12 ++--- .../org/dependencytrack/model/Project.java | 7 ++- .../persistence/PolicyQueryManager.java | 4 +- .../persistence/ProjectQueryManager.java | 52 +++++++++---------- .../persistence/QueryManager.java | 4 +- .../dependencytrack/policy/PolicyEngine.java | 2 +- .../resources/v1/PolicyResource.java | 4 +- .../upgrade/v4120/v4120Updater.java | 17 +----- 8 files changed, 41 insertions(+), 61 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/Policy.java b/src/main/java/org/dependencytrack/model/Policy.java index ca43c4d8c7..715ecc9cd5 100644 --- a/src/main/java/org/dependencytrack/model/Policy.java +++ b/src/main/java/org/dependencytrack/model/Policy.java @@ -143,8 +143,8 @@ public enum ViolationState { private boolean includeChildren; @Persistent - @Column(name = "ONLY_FOR_LATEST_PROJECT_VERSION", allowsNull = "true") // New column, must allow nulls for existing data bases - private boolean onlyForLatestProjectVersion; + @Column(name = "ONLY_LATEST_PROJECT_VERSION", defaultValue = "false") + private boolean onlyLatestProjectVersion = false; public long getId() { return id; @@ -229,11 +229,11 @@ public void setIncludeChildren(boolean includeChildren) { this.includeChildren = includeChildren; } - public boolean isOnlyForLatestProjectVersion() { - return onlyForLatestProjectVersion; + public boolean isOnlyLatestProjectVersion() { + return onlyLatestProjectVersion; } - public void setOnlyForLatestProjectVersion(boolean onlyForLatestProjectVersion) { - this.onlyForLatestProjectVersion = onlyForLatestProjectVersion; + public void setOnlyLatestProjectVersion(boolean onlyLatestProjectVersion) { + this.onlyLatestProjectVersion = onlyLatestProjectVersion; } } diff --git a/src/main/java/org/dependencytrack/model/Project.java b/src/main/java/org/dependencytrack/model/Project.java index d090e3a5de..c53555db3b 100644 --- a/src/main/java/org/dependencytrack/model/Project.java +++ b/src/main/java/org/dependencytrack/model/Project.java @@ -111,7 +111,6 @@ @Persistent(name = "parent") }) }) -@Index(name = "PROJECT_NAME_IS_LATEST_IDX", members = {"name", "isLatest"}) @JsonInclude(JsonInclude.Include.NON_NULL) public class Project implements Serializable { @@ -276,7 +275,7 @@ public enum FetchGroup { @Persistent @Index(name = "PROJECT_IS_LATEST_IDX") @Column(name = "IS_LATEST", defaultValue = "false") - private Boolean isLatest; // Added in v4.12. Needs to be nullable therefore + private boolean isLatest = false; // Added in v4.12. @Persistent(table = "PROJECT_ACCESS_TEAMS", defaultFetchGroup = "true") @Join(column = "PROJECT_ID") @@ -522,8 +521,8 @@ public void setActive(Boolean active) { } @JsonProperty("isLatest") - public Boolean isLatest() { - return isLatest != null ? isLatest : false; + public boolean isLatest() { + return isLatest; } public void setIsLatest(Boolean latest) { diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index 9aff5f67e0..0f0ded45c9 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -118,12 +118,12 @@ public Policy getPolicy(final String name) { * @return the created Policy */ public Policy createPolicy(String name, Policy.Operator operator, Policy.ViolationState violationState, - boolean onlyForLatestProjectVersion) { + boolean onlyLatestProjectVersion) { final Policy policy = new Policy(); policy.setName(name); policy.setOperator(operator); policy.setViolationState(violationState); - policy.setOnlyForLatestProjectVersion(onlyForLatestProjectVersion); + policy.setOnlyLatestProjectVersion(onlyLatestProjectVersion); return persist(policy); } diff --git a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java index d1ca5a213f..e9cd261e1f 100644 --- a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java @@ -71,6 +71,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; final class ProjectQueryManager extends QueryManager implements IQueryManager { @@ -475,9 +476,6 @@ public Project createProject(final Project project, List tags, boolean comm if (project.isActive() == null) { project.setActive(Boolean.TRUE); } - if (project.isLatest() == null) { - project.setIsLatest(Boolean.FALSE); - } final Project oldLatestProject = project.isLatest() ? getLatestProjectVersion(project.getName()) : null; final Project result = callInTransaction(() -> { // Remove isLatest flag from current latest project version, if the new project will be the latest @@ -613,28 +611,26 @@ public Project clone( final boolean includePolicyViolations, final boolean makeCloneLatest ) { - final Project source = getObjectByUuid(Project.class, from, Project.FetchGroup.ALL.name()); - if (source == null) { - LOGGER.warn("Project was supposed to be cloned, but it does not exist anymore"); - return null; - } - if (doesProjectExist(source.getName(), newVersion)) { - // Project cloning is an asynchronous process. When receiving the clone request, we already perform - // this check. It is possible though that a project with the new version is created synchronously - // between the clone event being dispatched, and it being processed. - LOGGER.warn("Project was supposed to be cloned to version %s, but that version already exists".formatted(newVersion)); - return null; - } - - final Project oldLatestProject; - if(makeCloneLatest) { - oldLatestProject = source.isLatest() ? source : getLatestProjectVersion(source.getName()); - } else { - oldLatestProject = null; - } - + final AtomicReference oldLatestProject = new AtomicReference<>(); final var jsonMapper = new JsonMapper(); final Project clonedProject = callInTransaction(() -> { + final Project source = getObjectByUuid(Project.class, from, Project.FetchGroup.ALL.name()); + if (source == null) { + LOGGER.warn("Project was supposed to be cloned, but it does not exist anymore"); + return null; + } + if (doesProjectExist(source.getName(), newVersion)) { + // Project cloning is an asynchronous process. When receiving the clone request, we already perform + // this check. It is possible though that a project with the new version is created synchronously + // between the clone event being dispatched, and it being processed. + LOGGER.warn("Project was supposed to be cloned to version %s, but that version already exists".formatted(newVersion)); + return null; + } + if(makeCloneLatest) { + oldLatestProject.set(source.isLatest() ? source : getLatestProjectVersion(source.getName())); + } else { + oldLatestProject.set(null); + } Project project = new Project(); project.setAuthors(source.getAuthors()); project.setManufacturer(source.getManufacturer()); @@ -656,9 +652,9 @@ public Project clone( project.setParent(source.getParent()); // Remove isLatest flag from current latest project version, if this project will be the latest now - if(oldLatestProject != null) { - oldLatestProject.setIsLatest(false); - persist(oldLatestProject); + if(oldLatestProject.get() != null) { + oldLatestProject.get().setIsLatest(false); + persist(oldLatestProject.get()); } project = persist(project); @@ -813,9 +809,9 @@ public Project clone( return project; }); - if(oldLatestProject != null) { + if(oldLatestProject.get() != null) { // if we removed isLatest flag from old version, dispatch update event for the old version - Event.dispatch(new IndexEvent(IndexEvent.Action.UPDATE, oldLatestProject)); + Event.dispatch(new IndexEvent(IndexEvent.Action.UPDATE, oldLatestProject.get())); } Event.dispatch(new IndexEvent(IndexEvent.Action.CREATE, clonedProject)); commitSearchIndex(true, Project.class); diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index c6f4fddd1a..349c24b3d1 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -692,8 +692,8 @@ public Policy getPolicy(final String name) { public Policy createPolicy(String name, Policy.Operator operator, Policy.ViolationState violationState) { return this.createPolicy(name, operator, violationState, false); } - public Policy createPolicy(String name, Policy.Operator operator, Policy.ViolationState violationState, boolean onlyForLatestProjectVersion) { - return getPolicyQueryManager().createPolicy(name, operator, violationState, onlyForLatestProjectVersion); + public Policy createPolicy(String name, Policy.Operator operator, Policy.ViolationState violationState, boolean onlyLatestProjectVersion) { + return getPolicyQueryManager().createPolicy(name, operator, violationState, onlyLatestProjectVersion); } public void removeProjectFromPolicies(final Project project) { diff --git a/src/main/java/org/dependencytrack/policy/PolicyEngine.java b/src/main/java/org/dependencytrack/policy/PolicyEngine.java index 89b38f7253..67db3db1a9 100644 --- a/src/main/java/org/dependencytrack/policy/PolicyEngine.java +++ b/src/main/java/org/dependencytrack/policy/PolicyEngine.java @@ -80,7 +80,7 @@ private List evaluate(final QueryManager qm, final List final List policyViolations = new ArrayList<>(); final List existingPolicyViolations = qm.detach(qm.getAllPolicyViolations(component)); for (final Policy policy : policies) { - if(policy.isOnlyForLatestProjectVersion() && Boolean.FALSE.equals(component.getProject().isLatest())) { + if(policy.isOnlyLatestProjectVersion() && Boolean.FALSE.equals(component.getProject().isLatest())) { continue; } if (policy.isGlobal() || isPolicyAssignedToProject(policy, component.getProject()) diff --git a/src/main/java/org/dependencytrack/resources/v1/PolicyResource.java b/src/main/java/org/dependencytrack/resources/v1/PolicyResource.java index f775fe047a..c4a9e88ee7 100644 --- a/src/main/java/org/dependencytrack/resources/v1/PolicyResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/PolicyResource.java @@ -157,7 +157,7 @@ public Response createPolicy(Policy jsonPolicy) { } policy = qm.createPolicy( StringUtils.trimToNull(jsonPolicy.getName()), - operator, violationState, jsonPolicy.isOnlyForLatestProjectVersion()); + operator, violationState, jsonPolicy.isOnlyLatestProjectVersion()); return Response.status(Response.Status.CREATED).entity(policy).build(); } else { return Response.status(Response.Status.CONFLICT).entity("A policy with the specified name already exists.").build(); @@ -194,7 +194,7 @@ public Response updatePolicy(Policy jsonPolicy) { policy.setOperator(jsonPolicy.getOperator()); policy.setViolationState(jsonPolicy.getViolationState()); policy.setIncludeChildren(jsonPolicy.isIncludeChildren()); - policy.setOnlyForLatestProjectVersion(jsonPolicy.isOnlyForLatestProjectVersion()); + policy.setOnlyLatestProjectVersion(jsonPolicy.isOnlyLatestProjectVersion()); policy = qm.persist(policy); return Response.ok(policy).build(); } else { diff --git a/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java b/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java index 38425b2ca0..a8dc36f363 100644 --- a/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java +++ b/src/main/java/org/dependencytrack/upgrade/v4120/v4120Updater.java @@ -48,8 +48,7 @@ public void executeUpgrade(final AlpineQueryManager qm, final Connection connect migrateBomValidationConfigProperty(connection); extendTeamNameColumnMaxLength(connection); migrateAuthorToAuthors(connection); - dropAuthorColumns(connection); - setInitialIsLatestFlags(connection); + dropAuthorColumns(connection); } private static void removeExperimentalBomUploadProcessingV2ConfigProperty(final Connection connection) throws SQLException { @@ -287,18 +286,4 @@ private void dropAuthorColumns(final Connection connection) throws SQLException } } - - - private void setInitialIsLatestFlags(Connection connection) throws SQLException { - LOGGER.info("Setting IS_LATEST flag to false for all Project entries"); - try (final Statement stmt = connection.createStatement()) { - stmt.executeUpdate(""" - UPDATE "PROJECT" SET "IS_LATEST" = false - """); - stmt.executeUpdate(""" - UPDATE "POLICY" SET "ONLY_FOR_LATEST_PROJECT_VERSION" = false - """); - } - } - } From 0e786f437e54c2cb647259891a47608c75cb085d Mon Sep 17 00:00:00 2001 From: Ralf King Date: Sun, 29 Sep 2024 00:17:21 +0200 Subject: [PATCH 181/429] Add missing tests for new /latest endpoint Signed-off-by: Ralf King --- .../org/dependencytrack/ResourceTest.java | 1 + .../resources/v1/ProjectResourceTest.java | 56 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/test/java/org/dependencytrack/ResourceTest.java b/src/test/java/org/dependencytrack/ResourceTest.java index 2057fc2c94..1282a63204 100644 --- a/src/test/java/org/dependencytrack/ResourceTest.java +++ b/src/test/java/org/dependencytrack/ResourceTest.java @@ -62,6 +62,7 @@ public abstract class ResourceTest { protected final String V1_POLICY = "/v1/policy"; protected final String V1_POLICY_VIOLATION = "/v1/violation"; protected final String V1_PROJECT = "/v1/project"; + protected final String V1_PROJECT_LATEST = "/v1/project/latest/"; protected final String V1_REPOSITORY = "/v1/repository"; protected final String V1_SCAN = "/v1/scan"; protected final String V1_SEARCH = "/v1/search"; diff --git a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java index 4298f5a02a..0027aa6188 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java @@ -1961,4 +1961,60 @@ public void issue4048RegressionTest() { .isNotEmpty(); } } + + @Test + public void getLatestProjectTest() { + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + qm.createProject("Acme Example", null, "1.0.2", null, null, null, true, true, false); + qm.createProject("Different project", null, "1.0.3", null, null, null, true, true, false); + + Response response = jersey.target(V1_PROJECT_LATEST + "Acme Example") + .request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertNotNull(json); + Assert.assertEquals("Acme Example", json.getString("name")); + Assert.assertEquals("1.0.2", json.getString("version")); + } + + @Test + public void getLatestProjectWithAclEnabledTest() { + enablePortfolioAccessControl(); + + // Create project and give access to current principal's team. + Project accessProject = qm.createProject("acme-app-a", null, "1.0.0", null, null, null, true, false, false); + accessProject.setAccessTeams(List.of(team)); + qm.persist(accessProject); + + accessProject = qm.createProject("acme-app-a", null, "1.0.2", null, null, null, true, true, false); + accessProject.setAccessTeams(List.of(team)); + qm.persist(accessProject); + + final Response response = jersey.target(V1_PROJECT_LATEST + "acme-app-a") + .request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertNotNull(json); + Assert.assertEquals("acme-app-a", json.getString("name")); + Assert.assertEquals("1.0.2", json.getString("version")); + } + + @Test + public void getLatestProjectWithAclEnabledNoAccessTest() { + enablePortfolioAccessControl(); + + // Create projects and give NO access + Project accessProject = qm.createProject("acme-app-a", null, "1.0.0", null, null, null, true, false, false); + accessProject = qm.createProject("acme-app-a", null, "1.0.2", null, null, null, true, true, false); + + final Response response = jersey.target(V1_PROJECT_LATEST + "acme-app-a") + .request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(403, response.getStatus(), 0); + } } From 3c643c97963c5e1369881389ec7d2d1ba7a8c070 Mon Sep 17 00:00:00 2001 From: Ralf King Date: Sun, 29 Sep 2024 00:43:48 +0200 Subject: [PATCH 182/429] Fix unit tests which relied on fixed bug Signed-off-by: Ralf King --- .../tasks/scanners/TrivyAnalysisTaskTest.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java b/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java index fe29c545f5..3bdb010338 100644 --- a/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java @@ -316,8 +316,12 @@ Those using Woodstox to parse XML data may be vulnerable to Denial of Service at assertThat(qm.getCount(ComponentAnalysisCache.class)).isOne(); - assertThat(NOTIFICATIONS).satisfiesExactly(notification -> - assertThat(notification.getGroup()).isEqualTo(NotificationGroup.NEW_VULNERABILITY.name())); + assertThat(NOTIFICATIONS).satisfiesExactly( + notification -> + assertThat(notification.getGroup()).isEqualTo(NotificationGroup.PROJECT_CREATED.name()), + notification -> + assertThat(notification.getGroup()).isEqualTo(NotificationGroup.NEW_VULNERABILITY.name()) + ); wireMock.verify(postRequestedFor(urlPathEqualTo("/twirp/trivy.cache.v1.Cache/PutBlob")) .withHeader("Trivy-Token", equalTo("token")) @@ -382,7 +386,10 @@ public void testAnalyzeWithNoVulnerabilities() { assertThat(qm.getCount(ComponentAnalysisCache.class)).isZero(); - assertThat(NOTIFICATIONS).isEmpty(); + assertThat(NOTIFICATIONS).satisfiesExactly( + notification -> + assertThat(notification.getGroup()).isEqualTo(NotificationGroup.PROJECT_CREATED.name()) + ); wireMock.verify(postRequestedFor(urlPathEqualTo("/twirp/trivy.cache.v1.Cache/PutBlob"))); wireMock.verify(postRequestedFor(urlPathEqualTo("/twirp/trivy.scanner.v1.Scanner/Scan"))); From 229a3265ebc54884482ca7b7716d907101e69473 Mon Sep 17 00:00:00 2001 From: Ralf King Date: Sun, 29 Sep 2024 01:30:27 +0200 Subject: [PATCH 183/429] Fixed cloning not properly respecting latest flag. Add regression test for it. Signed-off-by: Ralf King --- .../persistence/ProjectQueryManager.java | 16 ------- .../persistence/QueryManager.java | 7 --- .../tasks/CloneProjectTask.java | 3 +- .../persistence/ProjectQueryManagerTest.java | 6 +-- .../resources/v1/ProjectResourceTest.java | 43 ++++++++++++++++++- .../tasks/BomUploadProcessingTaskTest.java | 2 +- 6 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java index e9cd261e1f..b462df02a4 100644 --- a/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/ProjectQueryManager.java @@ -582,22 +582,6 @@ public Project updateProject(Project transientProject, boolean commitIndex) { return result; } - @Override - public Project clone( - final UUID from, - final String newVersion, - final boolean includeTags, - final boolean includeProperties, - final boolean includeComponents, - final boolean includeServices, - final boolean includeAuditHistory, - final boolean includeACL, - final boolean includePolicyViolations - ) { - return clone(from, newVersion, includeTags, includeProperties, includeComponents, includeServices, includeAuditHistory, - includeACL, includePolicyViolations, false); - } - @Override public Project clone( final UUID from, diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index 349c24b3d1..75ee9f26a5 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -505,13 +505,6 @@ public boolean updateNewProjectACL(Project transientProject, Principal principal return getProjectQueryManager().updateNewProjectACL(transientProject, principal); } - public Project clone(UUID from, String newVersion, boolean includeTags, boolean includeProperties, - boolean includeComponents, boolean includeServices, boolean includeAuditHistory, - boolean includeACL, boolean includePolicyViolations) { - return getProjectQueryManager().clone(from, newVersion, includeTags, includeProperties, - includeComponents, includeServices, includeAuditHistory, includeACL, includePolicyViolations); - } - public Project clone(UUID from, String newVersion, boolean includeTags, boolean includeProperties, boolean includeComponents, boolean includeServices, boolean includeAuditHistory, boolean includeACL, boolean includePolicyViolations, boolean makeCloneLatest) { diff --git a/src/main/java/org/dependencytrack/tasks/CloneProjectTask.java b/src/main/java/org/dependencytrack/tasks/CloneProjectTask.java index 863c46ed58..da4b9768c8 100644 --- a/src/main/java/org/dependencytrack/tasks/CloneProjectTask.java +++ b/src/main/java/org/dependencytrack/tasks/CloneProjectTask.java @@ -55,7 +55,8 @@ public void inform(final Event e) { request.includeServices(), request.includeAuditHistory(), request.includeACL(), - request.includePolicyViolations() + request.includePolicyViolations(), + request.makeCloneLatest() ); LOGGER.info("Cloned project for version %s into project %s".formatted(project.getVersion(), project.getUuid())); } catch (RuntimeException ex) { diff --git a/src/test/java/org/dependencytrack/persistence/ProjectQueryManagerTest.java b/src/test/java/org/dependencytrack/persistence/ProjectQueryManagerTest.java index 2a84d9a621..9d2d674da5 100644 --- a/src/test/java/org/dependencytrack/persistence/ProjectQueryManagerTest.java +++ b/src/test/java/org/dependencytrack/persistence/ProjectQueryManagerTest.java @@ -18,7 +18,6 @@ */ package org.dependencytrack.persistence; -import alpine.persistence.PaginatedResult; import org.dependencytrack.PersistenceCapableTest; import org.dependencytrack.model.*; import org.dependencytrack.tasks.scanners.AnalyzerIdentity; @@ -28,8 +27,6 @@ import java.util.Date; import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; - public class ProjectQueryManagerTest extends PersistenceCapableTest { @Test @@ -48,7 +45,8 @@ public void testCloneProjectPreservesVulnerabilityAttributionDate() throws Excep vuln.setSeverity(Severity.HIGH); qm.persist(vuln); qm.addVulnerability(vuln, comp, AnalyzerIdentity.INTERNAL_ANALYZER, "Vuln1", "http://vuln.com/vuln1", new Date(1708559165229L)); - Project clonedProject = qm.clone(project.getUuid(), "1.1.0", false, false, true, false, false, false, false); + Project clonedProject = qm.clone(project.getUuid(), "1.1.0", false, false, + true, false, false, false, false, false); List findings = qm.getFindings(clonedProject); Assert.assertEquals(1, findings.size()); Finding finding = findings.get(0); diff --git a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java index 0027aa6188..b3c13396c4 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java @@ -1794,6 +1794,45 @@ public void cloneProjectWithAclTest() { Assert.assertTrue(UuidUtil.isValidUUID(json.getString("token"))); } + @Test + public void cloneProjectAsLatestTest() { + EventService.getInstance().subscribe(CloneProjectEvent.class, CloneProjectTask.class); + + final var project = new Project(); + project.setName("acme-app-a"); + project.setVersion("1.0.0"); + project.setIsLatest(true); + qm.persist(project); + + final Response response = jersey.target("%s/clone".formatted(V1_PROJECT)).request() + .header(X_API_KEY, apiKey) + .put(Entity.json(""" + { + "project": "%s", + "version": "1.1.0", + "makeCloneLatest": true + } + """.formatted(project.getUuid()))); + assertThat(response.getStatus()).isEqualTo(200); + JsonObject json = parseJsonObject(response); + Assert.assertNotNull(json); + Assert.assertNotNull(json.getString("token")); + Assert.assertTrue(UuidUtil.isValidUUID(json.getString("token"))); + + await("Cloning completion") + .atMost(Duration.ofSeconds(15)) + .pollInterval(Duration.ofMillis(50)) + .untilAsserted(() -> { + final Project clonedProject = qm.getProject("acme-app-a", "1.1.0"); + assertThat(clonedProject).isNotNull(); + assertThat(clonedProject.isLatest()).isTrue(); + + // ensure source is no longer latest + qm.getPersistenceManager().refresh(project); + assertThat(project.isLatest()).isFalse(); + }); + } + @Test // https://github.com/DependencyTrack/dependency-track/issues/3883 public void issue3883RegressionTest() { Response response = jersey.target(V1_PROJECT) @@ -2008,8 +2047,8 @@ public void getLatestProjectWithAclEnabledNoAccessTest() { enablePortfolioAccessControl(); // Create projects and give NO access - Project accessProject = qm.createProject("acme-app-a", null, "1.0.0", null, null, null, true, false, false); - accessProject = qm.createProject("acme-app-a", null, "1.0.2", null, null, null, true, true, false); + qm.createProject("acme-app-a", null, "1.0.0", null, null, null, true, false, false); + qm.createProject("acme-app-a", null, "1.0.2", null, null, null, true, true, false); final Response response = jersey.target(V1_PROJECT_LATEST + "acme-app-a") .request() diff --git a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java index 84a5c9daf7..100241138d 100644 --- a/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/BomUploadProcessingTaskTest.java @@ -1397,7 +1397,7 @@ public void informIssue3981Test() { awaitBomProcessedNotification(bomUploadEvent); NOTIFICATIONS.clear(); - final Project clonedProject = qm.clone(project.getUuid(), "3.2.1", true, true, true, true, true, true, true); + final Project clonedProject = qm.clone(project.getUuid(), "3.2.1", true, true, true, true, true, true, true, false); bomBytes = """ { From 4665b5316a8ee38d5f2c5930f80c16904a1f8460 Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Sun, 29 Sep 2024 15:34:20 +0200 Subject: [PATCH 184/429] Resurrect enable badges setting for deprecation To make the removal of unauthenticated access to badges not be a breaking change after all, the enable badges config property is kept in after all, but repurposed into a setting to enable unauthenticated access to the badges resource. If it is disabled, then the badges api remains accessible to authenticated and authorized requests. Signed-off-by: Kirill.Sybin --- .../model/ConfigPropertyConstants.java | 1 + .../resources/v1/BadgeResource.java | 161 ++++++++++++++++-- 2 files changed, 148 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java index e6c623decf..6c1032e3fa 100644 --- a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java +++ b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java @@ -27,6 +27,7 @@ public enum ConfigPropertyConstants { GENERAL_BASE_URL("general", "base.url", null, PropertyType.URL, "URL used to construct links back to Dependency-Track from external systems"), + GENERAL_BADGE_ENABLED("general", "badge.enabled", "false", PropertyType.BOOLEAN, "Flag to enable/disable unauthenticated access to SVG badge from metrics"), EMAIL_SMTP_ENABLED("email", "smtp.enabled", "false", PropertyType.BOOLEAN, "Flag to enable/disable SMTP"), EMAIL_SMTP_FROM_ADDR("email", "smtp.from.address", null, PropertyType.STRING, "The from email address to use to send output SMTP mail"), EMAIL_PREFIX("email", "subject.prefix", "[Dependency-Track]", PropertyType.STRING, "The Prefix Subject email to use"), diff --git a/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java b/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java index 0abad88e19..6ecc04d254 100644 --- a/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java @@ -18,8 +18,18 @@ */ package org.dependencytrack.resources.v1; -import alpine.server.auth.AllowApiKeyInQueryParameter; -import alpine.server.auth.PermissionRequired; +import alpine.common.logging.Logger; +import alpine.common.util.BooleanUtil; +import alpine.model.ApiKey; +import alpine.model.ConfigProperty; +import alpine.model.UserPrincipal; +import alpine.model.LdapUser; +import alpine.model.ManagedUser; +import alpine.model.OidcUser; +import alpine.server.auth.ApiKeyAuthenticationService; +import alpine.server.auth.JwtAuthenticationService; +import alpine.server.auth.AuthenticationNotRequired; +import alpine.server.filters.AuthenticationFilter; import alpine.server.resources.AlpineResource; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -40,8 +50,16 @@ import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.HttpMethod; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.Response; +import org.glassfish.jersey.server.ContainerRequest; +import org.owasp.security.logging.SecurityMarkers; + +import javax.naming.AuthenticationException; +import java.security.Principal; + +import static org.dependencytrack.model.ConfigPropertyConstants.GENERAL_BADGE_ENABLED; /** * JAX-RS resources for processing metrics. @@ -60,6 +78,97 @@ public class BadgeResource extends AlpineResource { private static final String SVG_MEDIA_TYPE = "image/svg+xml"; + private final Logger LOGGER = Logger.getLogger(AuthenticationFilter.class); + + private boolean isUnauthenticatedBadgeAccessEnabled(final QueryManager qm) { + ConfigProperty property = qm.getConfigProperty( + GENERAL_BADGE_ENABLED.getGroupName(), GENERAL_BADGE_ENABLED.getPropertyName()); + return BooleanUtil.valueOf(property.getPropertyValue()); + } + + // Stand-in methods for alpine.server.filters.AuthenticationFilter and + // alpine.server.filters.AuthorizationFilter to allow enabling and disabling of + // unauthenticated access to the badges API during runtime, used solely to offer + // a deprecation period for unauthenticated access to badges. + private boolean passesAuthentication() { + ContainerRequest request = (ContainerRequest) super.getRequestContext().getRequest(); + + if (HttpMethod.OPTIONS.equals(request.getMethod())) { + return true; + } + + Principal principal = null; + + final ApiKeyAuthenticationService apiKeyAuthService = new ApiKeyAuthenticationService(request, true); + if (apiKeyAuthService.isSpecified()) { + try { + principal = apiKeyAuthService.authenticate(); + } catch (AuthenticationException e) { + LOGGER.info(SecurityMarkers.SECURITY_FAILURE, "Invalid API key asserted"); + return false; + } + } + + final JwtAuthenticationService jwtAuthService = new JwtAuthenticationService(request); + if (jwtAuthService.isSpecified()) { + try { + principal = jwtAuthService.authenticate(); + } catch (AuthenticationException e) { + LOGGER.info(SecurityMarkers.SECURITY_FAILURE, "Invalid JWT asserted"); + return false; + } + } + + if (principal == null) { + return false; + } else { + super.getRequestContext().setProperty("Principal", principal); + return true; + } + } + + private boolean passesAuthorization(final QueryManager qm) { + final Principal principal = (Principal) super.getRequestContext().getProperty("Principal"); + if (principal == null) { + LOGGER.info(SecurityMarkers.SECURITY_FAILURE, "A request was made without the assertion of a valid user principal"); + return false; + } + + final String[] permissions = { Permissions.Constants.VIEW_BADGES }; + + if (principal instanceof ApiKey) { + final ApiKey apiKey = (ApiKey)principal; + for (final String permission: permissions) { + if (qm.hasPermission(apiKey, permission)) { + return true; + } + } + LOGGER.info(SecurityMarkers.SECURITY_FAILURE, "Unauthorized access attempt made by API Key " + + apiKey.getMaskedKey() + " to " + ((ContainerRequest) super.getRequestContext()).getRequestUri().toString()); + } else { + UserPrincipal user = null; + if (principal instanceof ManagedUser) { + user = qm.getManagedUser(((ManagedUser) principal).getUsername()); + } else if (principal instanceof LdapUser) { + user = qm.getLdapUser(((LdapUser) principal).getUsername()); + } else if (principal instanceof OidcUser) { + user = qm.getOidcUser(((OidcUser) principal).getUsername()); + } + if (user == null) { + LOGGER.info(SecurityMarkers.SECURITY_FAILURE, "A request was made but the system in unable to find the user principal"); + return false; + } + for (final String permission : permissions) { + if (qm.hasPermission(user, permission, true)) { + return true; + } + } + LOGGER.info(SecurityMarkers.SECURITY_FAILURE, "Unauthorized access attempt made by " + + user.getUsername() + " to " + ((ContainerRequest) super.getRequestContext()).getRequestUri().toString()); + } + return false; + } + @GET @Path("/vulns/project/{uuid}") @Produces(SVG_MEDIA_TYPE) @@ -74,17 +183,23 @@ public class BadgeResource extends AlpineResource { content = @Content(schema = @Schema(type = "string")) ), @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "403", description = "Forbidden"), @ApiResponse(responseCode = "404", description = "The project could not be found") }) - @PermissionRequired(Permissions.Constants.VIEW_BADGES) - @AllowApiKeyInQueryParameter + @AuthenticationNotRequired public Response getProjectVulnerabilitiesBadge( @Parameter(description = "The UUID of the project to retrieve metrics for", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { + if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthentication()) { + return Response.status(Response.Status.UNAUTHORIZED).build(); + } + if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthorization(qm)) { + return Response.status(Response.Status.FORBIDDEN).build(); + } final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { - if (!qm.hasAccess(super.getPrincipal(), project)) { + if (!isUnauthenticatedBadgeAccessEnabled(qm) && !qm.hasAccess(super.getPrincipal(), project)) { return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); @@ -110,19 +225,25 @@ public Response getProjectVulnerabilitiesBadge( content = @Content(schema = @Schema(type = "string")) ), @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "403", description = "Forbidden"), @ApiResponse(responseCode = "404", description = "The project could not be found") }) - @PermissionRequired(Permissions.Constants.VIEW_BADGES) - @AllowApiKeyInQueryParameter + @AuthenticationNotRequired public Response getProjectVulnerabilitiesBadge( @Parameter(description = "The name of the project to query on", required = true) @PathParam("name") String name, @Parameter(description = "The version of the project to query on", required = true) @PathParam("version") String version) { try (QueryManager qm = new QueryManager()) { + if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthentication()) { + return Response.status(Response.Status.UNAUTHORIZED).build(); + } + if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthorization(qm)) { + return Response.status(Response.Status.FORBIDDEN).build(); + } final Project project = qm.getProject(name, version); if (project != null) { - if (!qm.hasAccess(super.getPrincipal(), project)) { + if (!isUnauthenticatedBadgeAccessEnabled(qm) && !qm.hasAccess(super.getPrincipal(), project)) { return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); @@ -148,17 +269,23 @@ public Response getProjectVulnerabilitiesBadge( content = @Content(schema = @Schema(type = "string")) ), @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "403", description = "Forbidden"), @ApiResponse(responseCode = "404", description = "The project could not be found") }) - @PermissionRequired(Permissions.Constants.VIEW_BADGES) - @AllowApiKeyInQueryParameter + @AuthenticationNotRequired public Response getProjectPolicyViolationsBadge( @Parameter(description = "The UUID of the project to retrieve a badge for", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { + if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthentication()) { + return Response.status(Response.Status.UNAUTHORIZED).build(); + } + if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthorization(qm)) { + return Response.status(Response.Status.FORBIDDEN).build(); + } final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { - if (!qm.hasAccess(super.getPrincipal(), project)) { + if (!isUnauthenticatedBadgeAccessEnabled(qm) && !qm.hasAccess(super.getPrincipal(), project)) { return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); @@ -184,19 +311,25 @@ public Response getProjectPolicyViolationsBadge( content = @Content(schema = @Schema(type = "string")) ), @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "403", description = "Forbidden"), @ApiResponse(responseCode = "404", description = "The project could not be found") }) - @PermissionRequired(Permissions.Constants.VIEW_BADGES) - @AllowApiKeyInQueryParameter + @AuthenticationNotRequired public Response getProjectPolicyViolationsBadge( @Parameter(description = "The name of the project to query on", required = true) @PathParam("name") String name, @Parameter(description = "The version of the project to query on", required = true) @PathParam("version") String version) { try (QueryManager qm = new QueryManager()) { + if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthentication()) { + return Response.status(Response.Status.UNAUTHORIZED).build(); + } + if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthorization(qm)) { + return Response.status(Response.Status.FORBIDDEN).build(); + } final Project project = qm.getProject(name, version); if (project != null) { - if (!qm.hasAccess(super.getPrincipal(), project)) { + if (!isUnauthenticatedBadgeAccessEnabled(qm) && !qm.hasAccess(super.getPrincipal(), project)) { return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); From f265b35d4194c0d8067d55317d65f0bc13dad8bd Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Sun, 29 Sep 2024 17:04:15 +0200 Subject: [PATCH 185/429] Add tests for enabled unauthenticated badge access Signed-off-by: Kirill.Sybin --- .../resources/v1/BadgeResourceTest.java | 77 +++++++++++++++++-- 1 file changed, 71 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/BadgeResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/BadgeResourceTest.java index 3599d3111d..fade62e6ca 100644 --- a/src/test/java/org/dependencytrack/resources/v1/BadgeResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/BadgeResourceTest.java @@ -18,9 +18,8 @@ */ package org.dependencytrack.resources.v1; +import alpine.model.IConfigProperty; import alpine.server.filters.ApiFilter; -import alpine.server.filters.AuthenticationFilter; -import alpine.server.filters.AuthorizationFilter; import org.dependencytrack.JerseyTestRule; import org.dependencytrack.ResourceTest; import org.dependencytrack.auth.Permissions; @@ -40,14 +39,20 @@ import java.util.List; import java.util.UUID; +import static org.dependencytrack.model.ConfigPropertyConstants.GENERAL_BADGE_ENABLED; + public class BadgeResourceTest extends ResourceTest { @ClassRule public static JerseyTestRule jersey = new JerseyTestRule( new ResourceConfig(BadgeResource.class) - .register(ApiFilter.class) - .register(AuthenticationFilter.class) - .register(AuthorizationFilter.class)); + .register(ApiFilter.class)); + + @Override + public void before() throws Exception { + super.before(); + qm.createConfigProperty(GENERAL_BADGE_ENABLED.getGroupName(), GENERAL_BADGE_ENABLED.getPropertyName(), "false", IConfigProperty.PropertyType.BOOLEAN, "Unauthenticated access to badge enabled"); + } @Test public void projectVulnerabilitiesByUuidTest() { @@ -76,6 +81,19 @@ public void projectVulnerabilitiesByUuidWithHeaderAuthenticationTest() { Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); } + @Test + public void projectVulnerabilitiesByUuidMissingAuthenticationWithUnauthenticatedAccessEnabledTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + enableUnauthenticatedBadgeAccess(); + + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/vulns/project/" + project.getUuid()).request() + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + @Test public void projectVulnerabilitiesByUuidProjectNotFoundTest() { initializeWithPermissions(Permissions.VIEW_BADGES); @@ -201,10 +219,24 @@ public void projectVulnerabilitiesByNameAndVersionTest() { @Test public void projectVulnerabilitiesByNameAndVersionWithHeaderAuthenticationTest() { initializeWithPermissions(Permissions.VIEW_BADGES); + enableUnauthenticatedBadgeAccess(); qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0").request() - .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + + @Test + public void projectVulnerabilitiesByNameAndVersionMissingAuthenticationWithUnauthenticatedAccessEnabledTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/vulns/project/Acme%20Example/1.0.0") + .queryParam(API_KEY, apiKey) + .request() .get(Response.class); Assert.assertEquals(200, response.getStatus(), 0); Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); @@ -358,6 +390,20 @@ public void projectPolicyViolationsByUuidWithHeaderAuthenticationTest() { Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); } + @Test + public void projectPolicyViolationsByUuidMissingAuthenticationWithUnauthenticatedAccessEnabledTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + enableUnauthenticatedBadgeAccess(); + + Project project = qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/violations/project/" + project.getUuid()) + .request() + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + @Test public void projectPolicyViolationsByUuidProjectNotFoundTest() { initializeWithPermissions(Permissions.VIEW_BADGES); @@ -493,6 +539,20 @@ public void projectPolicyViolationsByNameAndVersionWithHeaderAuthenticationTest( Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); } + @Test + public void projectPolicyViolationsByNameAndVersionMissingAuthenticationWithUnauthenticatedAccessEnabledTest() { + initializeWithPermissions(Permissions.VIEW_BADGES); + enableUnauthenticatedBadgeAccess(); + + qm.createProject("Acme Example", null, "1.0.0", null, null, null, true, false); + Response response = jersey.target(V1_BADGE + "/violations/project/Acme%20Example/1.0.0") + .request() + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals("image/svg+xml", response.getHeaderString("Content-Type")); + Assert.assertTrue(isLikelySvg(getPlainTextBody(response))); + } + @Test public void projectPolicyViolationsByNameAndVersionProjectNotFoundTest() { initializeWithPermissions(Permissions.VIEW_BADGES); @@ -620,4 +680,9 @@ private boolean isLikelySvg(String body) { return false; } } + + private void enableUnauthenticatedBadgeAccess() { + qm.getConfigProperty(GENERAL_BADGE_ENABLED.getGroupName(), GENERAL_BADGE_ENABLED.getPropertyName()) + .setPropertyValue("true"); + } } From ffda6d5e01b3b788636e28e39142a88f86c7a3f5 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 29 Sep 2024 17:26:07 +0200 Subject: [PATCH 186/429] Bump awaitility to 4.2.2 Signed-off-by: nscuro --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c706924213..05b969eaff 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ 4.11.6 ${project.parent.version} - 4.2.1 + 4.2.2 0.1.2 10.17.0 1.20.1 From cdc68911501483f5f2f06b6e046c4251be1b7a00 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 29 Sep 2024 17:26:52 +0200 Subject: [PATCH 187/429] Bump checkstyle to 10.18.1 Signed-off-by: nscuro --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 05b969eaff..e382b85315 100644 --- a/pom.xml +++ b/pom.xml @@ -89,7 +89,7 @@ ${project.parent.version} 4.2.2 0.1.2 - 10.17.0 + 10.18.1 1.20.1 1.18.0 1.19.1 From 2da4a9088f74d655ea551660298256d9d3b9c7e9 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 29 Sep 2024 17:28:38 +0200 Subject: [PATCH 188/429] Bump cloud-sql-socket-factory to 1.20.1 Signed-off-by: nscuro --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index e382b85315..63a10fe6e5 100644 --- a/pom.xml +++ b/pom.xml @@ -91,8 +91,8 @@ 0.1.2 10.18.1 1.20.1 - 1.18.0 - 1.19.1 + 1.20.1 + 1.20.1 2.1.0 1.27.1 1.12.0 From 649d7d5277366db94fbd0bcb2c95012d10c0f443 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 29 Sep 2024 17:29:48 +0200 Subject: [PATCH 189/429] Remove datanucleus version overrides Signed-off-by: nscuro --- pom.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pom.xml b/pom.xml index 63a10fe6e5..e3d86812ab 100644 --- a/pom.xml +++ b/pom.xml @@ -99,13 +99,6 @@ 1.4.2 1.0.1 9.0.5 - - 6.0.3 - 6.0.8 - 6.0.8 2.0.1 2.17.1 2.17.1 From 0bfa2c85a2f5ddf2ce6aa25a9b7d5c9653378903 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 29 Sep 2024 17:32:44 +0200 Subject: [PATCH 190/429] Bump postgresql jdbc driver to 42.7.4 Signed-off-by: nscuro --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e3d86812ab..1fbf8b315b 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ 12.8.1.jre11 8.0.33 - 42.7.3 + 42.7.4 false 12.0.13 From 02a44accb6a1fe21bfa2b62d88347a8ddcf401d8 Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Sun, 29 Sep 2024 17:29:25 +0200 Subject: [PATCH 191/429] Update documentation Update documentation for globally configurable unauthenticated access to badges. Signed-off-by: Kirill.Sybin --- docs/_docs/integrations/badges.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/_docs/integrations/badges.md b/docs/_docs/integrations/badges.md index a7055368cd..e966fb3fe5 100644 --- a/docs/_docs/integrations/badges.md +++ b/docs/_docs/integrations/badges.md @@ -6,11 +6,25 @@ order: 10 --- Dependency-Track supports badges in Scalable Vector Graphics (SVG) format. Support for badges is configurable on a team -basis via permission. +basis via permission or globally for unauthenticated access. + +> **Deprecation Notice** +> +> Unauthenticated access to badges as a global configuration is deprecated and slated for removal in Dependency-Track +> v4.12. To enable badges for a team, activate the permission `VIEW_BADGES`. To deactivate badges, remove the permission. To retrieve a badge, use a team's API key either in the badge API header `X-API-Key` or in the URI parameter `apiKey`. +As a legacy feature, badges can also be accessed without authentication. On new Dependency-Track installations, this is +disabled by default. On Dependency-Track installations updated from ≤ v4.11, where (unauthenticated) badge support +was enabled, badges will remain accessible for unauthenticated requests. If this is disabled, badges will be accessible +for authenticated and authorized requests. + +> Enabling unauthenticated access to badges will provide vulnerability and policy violation metric information to +> unauthenticated users. Any anonymous user with network access to Dependency-Track and knowledge of a projects +> information will be able to view the SVG badge. + Dependency-Track ships with a default team "_Badge Viewers_" dedicated to badges that already has the necessary permission and an API key. From b75e9e99c04475f067e97bc46655415e6f7d0966 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sun, 29 Sep 2024 17:34:30 +0200 Subject: [PATCH 192/429] Bump jackson to 2.17.2 Signed-off-by: nscuro --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1fbf8b315b..2554dbaaf4 100644 --- a/pom.xml +++ b/pom.xml @@ -100,8 +100,8 @@ 1.0.1 9.0.5 2.0.1 - 2.17.1 - 2.17.1 + 2.17.2 + 2.17.2 20240303 3.4.1 4.13.2 From efb2504a68a4fe66f57ddbb402e9034c03544e6e Mon Sep 17 00:00:00 2001 From: "Kirill.Sybin" Date: Sun, 29 Sep 2024 20:48:42 +0200 Subject: [PATCH 193/429] Fix tests to take into account new default team Signed-off-by: Kirill.Sybin --- .../dependencytrack/persistence/DefaultObjectGeneratorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/dependencytrack/persistence/DefaultObjectGeneratorTest.java b/src/test/java/org/dependencytrack/persistence/DefaultObjectGeneratorTest.java index 410657f821..e7c5f6e12c 100644 --- a/src/test/java/org/dependencytrack/persistence/DefaultObjectGeneratorTest.java +++ b/src/test/java/org/dependencytrack/persistence/DefaultObjectGeneratorTest.java @@ -93,7 +93,7 @@ public void testLoadDefaultPersonas() throws Exception { Method method = generator.getClass().getDeclaredMethod("loadDefaultPersonas"); method.setAccessible(true); method.invoke(generator); - Assert.assertEquals(3, qm.getTeams().size()); + Assert.assertEquals(4, qm.getTeams().size()); } @Test From c1369c455a2d2e6369f69b010e2a94a940db9f04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:36:28 +0000 Subject: [PATCH 194/429] Bump actions/checkout from 4.1.7 to 4.2.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 4.2.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...d632683dd7b4114ad314bca15554477dd762a938) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 4 ++-- .github/workflows/ci-publish.yaml | 4 ++-- .github/workflows/ci-release.yaml | 6 +++--- .github/workflows/ci-test.yaml | 2 +- .github/workflows/dependency-review.yaml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index e1f3dc92dd..2e41fc75f7 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 - name: Set up JDK uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # tag=v4.3.0 @@ -78,7 +78,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 - name: Download Artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # tag=v4.1.8 diff --git a/.github/workflows/ci-publish.yaml b/.github/workflows/ci-publish.yaml index b885a7fdf0..fb4976852d 100644 --- a/.github/workflows/ci-publish.yaml +++ b/.github/workflows/ci-publish.yaml @@ -23,7 +23,7 @@ jobs: exit 1 fi - name: Checkout Repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 - name: Parse Version from POM id: parse @@ -52,7 +52,7 @@ jobs: - call-build steps: - name: Checkout Repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 - name: Download Artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # tag=v4.1.8 diff --git a/.github/workflows/ci-release.yaml b/.github/workflows/ci-release.yaml index 9e9401cbc4..40372d8eda 100644 --- a/.github/workflows/ci-release.yaml +++ b/.github/workflows/ci-release.yaml @@ -20,7 +20,7 @@ jobs: release-branch: ${{ steps.variables.outputs.release-branch }} steps: - name: Checkout Repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 - name: Setup Environment id: variables @@ -51,7 +51,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 - name: Set up JDK uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # tag=v4.3.0 @@ -118,7 +118,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 with: ref: ${{ needs.prepare-release.outputs.release-branch }} diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml index cbe2b73f6f..b2caddf261 100644 --- a/.github/workflows/ci-test.yaml +++ b/.github/workflows/ci-test.yaml @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 - name: Set up JDK uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # tag=v4.3.0 diff --git a/.github/workflows/dependency-review.yaml b/.github/workflows/dependency-review.yaml index 4126591878..8c0c9f7f4d 100644 --- a/.github/workflows/dependency-review.yaml +++ b/.github/workflows/dependency-review.yaml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 - name: Dependency Review uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # tag=v4.3.4 From 70d557910377525c8f8cbd438fabf70b6054e4ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:36:34 +0000 Subject: [PATCH 195/429] Bump github/codeql-action from 3.26.8 to 3.26.9 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.8 to 3.26.9. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/294a9d92911152fe08befb9ec03e240add280cb3...461ef6c76dfe95d5c364de2f431ddbd31a417628) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index e1f3dc92dd..8c393c895a 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -145,6 +145,6 @@ jobs: - name: Upload Trivy Scan Results to GitHub Security Tab if: ${{ inputs.publish-container }} - uses: github/codeql-action/upload-sarif@294a9d92911152fe08befb9ec03e240add280cb3 # tag=v3.26.8 + uses: github/codeql-action/upload-sarif@461ef6c76dfe95d5c364de2f431ddbd31a417628 # tag=v3.26.9 with: sarif_file: 'trivy-results.sarif' From 7ba75f4bbe8f6e7b6da34b8e98ef791a206195da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:36:39 +0000 Subject: [PATCH 196/429] Bump docker/build-push-action from 6.7.0 to 6.8.0 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.7.0 to 6.8.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/5cd11c3a4ced054e52742c5fd54dca954e0edd85...32945a339266b759abcbdc89316275140b0fc960) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index e1f3dc92dd..d03fec6c07 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -121,7 +121,7 @@ jobs: echo "tags=${TAGS}" >> $GITHUB_OUTPUT - name: Build multi-arch Container Image - uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # tag=v6.7.0 + uses: docker/build-push-action@32945a339266b759abcbdc89316275140b0fc960 # tag=v6.8.0 with: tags: ${{ steps.tags.outputs.tags }} build-args: |- From 13f09782c24af94845c61dc7891c7b18c7ffeaf3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:36:45 +0000 Subject: [PATCH 197/429] Bump actions/setup-java from 4.3.0 to 4.4.0 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.3.0 to 4.4.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/2dfa2011c5b2a0f1489bf9e433881c92c1631f88...b36c23c0d998641eff861008f374ee103c25ac73) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- .github/workflows/ci-release.yaml | 2 +- .github/workflows/ci-test.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index e1f3dc92dd..e29e422049 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -31,7 +31,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 - name: Set up JDK - uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # tag=v4.3.0 + uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # tag=v4.4.0 with: distribution: 'temurin' java-version: '21' diff --git a/.github/workflows/ci-release.yaml b/.github/workflows/ci-release.yaml index 9e9401cbc4..dfdf7c5db5 100644 --- a/.github/workflows/ci-release.yaml +++ b/.github/workflows/ci-release.yaml @@ -54,7 +54,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 - name: Set up JDK - uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # tag=v4.3.0 + uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # tag=v4.4.0 with: distribution: 'temurin' java-version: '21' diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml index cbe2b73f6f..cf92bb714c 100644 --- a/.github/workflows/ci-test.yaml +++ b/.github/workflows/ci-test.yaml @@ -36,7 +36,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # tag=v4.1.7 - name: Set up JDK - uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # tag=v4.3.0 + uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # tag=v4.4.0 with: distribution: 'temurin' java-version: '21' From b33e95924140b50d9eb43f89dffa8cd41ed7d681 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:55:44 +0000 Subject: [PATCH 198/429] Bump debian from `a75706a` to `939e69e` in /src/main/docker Bumps debian from `a75706a` to `939e69e`. --- updated-dependencies: - dependency-name: debian dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile index 21de995f5a..27b92d41d7 100644 --- a/src/main/docker/Dockerfile +++ b/src/main/docker/Dockerfile @@ -1,6 +1,6 @@ FROM eclipse-temurin:21.0.4_7-jre-jammy@sha256:870aae69d4521fdaf26e952f8026f75b37cb721e6302d4d4d7100f6b09823057 AS jre-build -FROM debian:stable-slim@sha256:a75706ac1838d761d95fe2690858392588310abcab67876a0c330252f1073373 +FROM debian:stable-slim@sha256:939e69ef5aa4dc178893a718ea567f1ca390df60793fd08c0bc7008362f72a57 # Arguments that can be passed at build time # Directory names must end with / to avoid errors when ADDing and COPYing From b198fb50251b3b90c9e7389be721d00e79403e20 Mon Sep 17 00:00:00 2001 From: Brent England Date: Mon, 30 Sep 2024 22:14:53 +1000 Subject: [PATCH 199/429] Refactor NugetMetaAnalyzer to simplify version filtering logic Signed-off-by: Brent England --- .../tasks/repositories/NugetMetaAnalyzer.java | 9 +++------ .../tasks/repositories/NugetMetaAnalyzerTest.java | 6 +++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java b/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java index 35140813f1..06da55a9c8 100644 --- a/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java +++ b/src/main/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzer.java @@ -113,10 +113,7 @@ private boolean performVersionCheck(final MetaModel meta, final Component compon var jsonObject = new JSONObject(responseString); final JSONArray versions = jsonObject.getJSONArray("versions"); - // if the version is a pre-release version, we should not exclude pre-release versions - final boolean excludePreRelease = component.getPurl().getVersion() != null && !component.getPurl().getVersion().contains("-"); - - final String latest = findLatestVersion(versions,excludePreRelease); // get the last version in the array + final String latest = findLatestVersion(versions); // get the last version in the array meta.setLatestVersion(latest); } return true; @@ -131,8 +128,8 @@ private boolean performVersionCheck(final MetaModel meta, final Component compon return false; } - private String findLatestVersion(JSONArray versions, boolean excludePreRelease) { - JSONArray filteredVersions = excludePreRelease ? filterPreReleaseVersions(versions) : versions; + private String findLatestVersion(JSONArray versions) { + JSONArray filteredVersions = filterPreReleaseVersions(versions); if (filteredVersions.length() < 1) { return null; diff --git a/src/test/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzerTest.java b/src/test/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzerTest.java index 7780a881f8..0cf52204f1 100644 --- a/src/test/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzerTest.java +++ b/src/test/java/org/dependencytrack/tasks/repositories/NugetMetaAnalyzerTest.java @@ -74,7 +74,7 @@ public void testAnalyzer() throws Exception { // The test is transitent depending on the current version of the package // retrieved from the repository at the time of running. // When it was created, the latest release version was 9.0.0-preview.1.24080.9 - //@Test + @Test public void testAnalyzerExcludingPreRelease() throws Exception { Component component = new Component(); component.setPurl(new PackageURL("pkg:nuget/Microsoft.Extensions.DependencyInjection@8.0.0")); @@ -94,7 +94,7 @@ public void testAnalyzerExcludingPreRelease() throws Exception { // The test is transitent depending on the current version of the package // retrieved from the repository at the time of running. // When it was created, the latest release version was 9.0.0-preview.1.24080.9 - //@Test + @Test public void testAnalyzerIncludingPreRelease() throws Exception { Component component = new Component(); component.setPurl(new PackageURL("pkg:nuget/Microsoft.Extensions.DependencyInjection@8.0.0-beta.21301.5")); @@ -107,7 +107,7 @@ public void testAnalyzerIncludingPreRelease() throws Exception { Assert.assertEquals(RepositoryType.NUGET, analyzer.supportedRepositoryType()); Assert.assertNotNull(metaModel.getLatestVersion()); - Assert.assertTrue(metaModel.getLatestVersion().contains("-")); + Assert.assertFalse(metaModel.getLatestVersion().contains("-")); } @Test From a69bb040560ee2787e15f987281efb46a6a66a58 Mon Sep 17 00:00:00 2001 From: nscuro Date: Mon, 30 Sep 2024 20:05:07 +0200 Subject: [PATCH 200/429] Ensure modifying project endpoints are transactional The `/api/v1/project` endpoints used to modify `Project` resources have historically not been transactional, potentially causing inconsistencies or too frequent commits (since every object modification implicitly creates a transaction behind the scenes). Wrap code in `runInTransaction` or `callInTransaction` to ensure the desired transactional behavior. Signed-off-by: nscuro --- .../resources/v1/ProjectResource.java | 403 +++++++++++------- .../resources/v1/ProjectResourceTest.java | 134 ++++++ 2 files changed, 377 insertions(+), 160 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java index fd66c5a466..83e5089e31 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ProjectResource.java @@ -50,6 +50,7 @@ import org.dependencytrack.resources.v1.vo.CloneProjectRequest; import jakarta.validation.Validator; +import jakarta.ws.rs.ClientErrorException; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; @@ -60,6 +61,7 @@ import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.ServerErrorException; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import javax.jdo.FetchGroup; @@ -67,6 +69,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.function.BiConsumer; @@ -358,106 +361,115 @@ public Response createProject(final Project jsonProject) { jsonProject.setClassifier(Classifier.APPLICATION); } try (final var qm = new QueryManager()) { - if (qm.doesProjectExist(StringUtils.trimToNull(jsonProject.getName()), - StringUtils.trimToNull(jsonProject.getVersion()))) { - return Response - .status(Response.Status.CONFLICT) - .entity("A project with the specified name already exists.") - .build(); - } - - if(jsonProject.isLatest()) { - final Project oldLatest = qm.getLatestProjectVersion(jsonProject.getName()); - if(oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { - return Response.status(Response.Status.FORBIDDEN) - .entity("Cannot create latest version for project with this name. Access to current latest " + - "version is forbidden!") - .build(); + final Project createdProject = qm.callInTransaction(() -> { + if (qm.doesProjectExist(StringUtils.trimToNull(jsonProject.getName()), + StringUtils.trimToNull(jsonProject.getVersion()))) { + throw new ClientErrorException(Response + .status(Response.Status.CONFLICT) + .entity("A project with the specified name already exists.") + .build()); } - } - - if (jsonProject.getParent() != null && jsonProject.getParent().getUuid() != null) { - Project parent = qm.getObjectByUuid(Project.class, jsonProject.getParent().getUuid()); - jsonProject.setParent(parent); - } - - final Principal principal = getPrincipal(); - final List chosenTeams = requireNonNullElseGet( - jsonProject.getAccessTeams(), Collections::emptyList); - jsonProject.setAccessTeams(null); - - for (final Team chosenTeam : chosenTeams) { - if (chosenTeam.getUuid() == null && chosenTeam.getName() == null) { - return Response - .status(Response.Status.BAD_REQUEST) - .entity(""" - accessTeams must either specify a UUID or a name,\ - but the team at index %d has neither.\ - """.formatted(chosenTeams.indexOf(chosenTeam))) - .build(); + if (jsonProject.isLatest()) { + final Project oldLatest = qm.getLatestProjectVersion(jsonProject.getName()); + if (oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { + throw new ClientErrorException(Response + .status(Response.Status.FORBIDDEN) + .entity("Cannot set this project version to latest. Access to current latest " + + "version is forbidden!") + .build()); + } } - } - if (!chosenTeams.isEmpty()) { - final List userTeams; - if (principal instanceof final UserPrincipal userPrincipal) { - userTeams = userPrincipal.getTeams(); - } else if (principal instanceof final ApiKey apiKey) { - userTeams = apiKey.getTeams(); - } else { - userTeams = Collections.emptyList(); + if (jsonProject.getParent() != null && jsonProject.getParent().getUuid() != null) { + Project parent = qm.getObjectByUuid(Project.class, jsonProject.getParent().getUuid()); + jsonProject.setParent(parent); } - final boolean isAdmin = qm.hasAccessManagementPermission(principal); - final List visibleTeams = isAdmin ? qm.getTeams() : userTeams; - final var visibleTeamByUuid = new HashMap(visibleTeams.size()); - final var visibleTeamByName = new HashMap(visibleTeams.size()); - for (final Team visibleTeam : visibleTeams) { - visibleTeamByUuid.put(visibleTeam.getUuid(), visibleTeam); - visibleTeamByName.put(visibleTeam.getName(), visibleTeam); - } + final Principal principal = getPrincipal(); - for (final Team chosenTeam : chosenTeams) { - Team visibleTeam = visibleTeamByUuid.getOrDefault( - chosenTeam.getUuid(), - visibleTeamByName.get(chosenTeam.getName())); + final List chosenTeams = requireNonNullElseGet( + jsonProject.getAccessTeams(), Collections::emptyList); + jsonProject.setAccessTeams(null); - if (visibleTeam == null) { - return Response + for (final Team chosenTeam : chosenTeams) { + if (chosenTeam.getUuid() == null && chosenTeam.getName() == null) { + throw new ClientErrorException(Response .status(Response.Status.BAD_REQUEST) .entity(""" - The team with %s can not be assigned because it does not exist, \ - or is not accessible to the authenticated principal.\ - """.formatted(chosenTeam.getUuid() != null - ? "UUID " + chosenTeam.getUuid() - : "name " + chosenTeam.getName())) - .build(); + accessTeams must either specify a UUID or a name,\ + but the team at index %d has neither.\ + """.formatted(chosenTeams.indexOf(chosenTeam))) + .build()); + } + } + + if (!chosenTeams.isEmpty()) { + final List userTeams; + if (principal instanceof final UserPrincipal userPrincipal) { + userTeams = userPrincipal.getTeams(); + } else if (principal instanceof final ApiKey apiKey) { + userTeams = apiKey.getTeams(); + } else { + userTeams = Collections.emptyList(); + } + + final boolean isAdmin = qm.hasAccessManagementPermission(principal); + final List visibleTeams = isAdmin ? qm.getTeams() : userTeams; + final var visibleTeamByUuid = new HashMap(visibleTeams.size()); + final var visibleTeamByName = new HashMap(visibleTeams.size()); + for (final Team visibleTeam : visibleTeams) { + visibleTeamByUuid.put(visibleTeam.getUuid(), visibleTeam); + visibleTeamByName.put(visibleTeam.getName(), visibleTeam); } - if (!isPersistent(visibleTeam)) { - // Teams sourced from the principal will not be in persistent state - // and need to be attached to the persistence context. - visibleTeam = qm.getObjectById(Team.class, visibleTeam.getId()); + for (final Team chosenTeam : chosenTeams) { + Team visibleTeam = visibleTeamByUuid.getOrDefault( + chosenTeam.getUuid(), + visibleTeamByName.get(chosenTeam.getName())); + + if (visibleTeam == null) { + throw new ClientErrorException(Response + .status(Response.Status.BAD_REQUEST) + .entity(""" + The team with %s can not be assigned because it does not exist, \ + or is not accessible to the authenticated principal.\ + """.formatted(chosenTeam.getUuid() != null + ? "UUID " + chosenTeam.getUuid() + : "name " + chosenTeam.getName())) + .build()); + } + + if (!isPersistent(visibleTeam)) { + // Teams sourced from the principal will not be in persistent state + // and need to be attached to the persistence context. + visibleTeam = qm.getObjectById(Team.class, visibleTeam.getId()); + } + + jsonProject.addAccessTeam(visibleTeam); } + } - jsonProject.addAccessTeam(visibleTeam); + final Project project; + try { + project = qm.createProject(jsonProject, jsonProject.getTags(), true); + } catch (IllegalArgumentException e) { + LOGGER.debug("Failed to create project %s".formatted(jsonProject), e); + throw new ClientErrorException(Response + .status(Response.Status.CONFLICT) + .entity("An inactive Parent cannot be selected as parent") + .build()); + } catch (RuntimeException e) { + LOGGER.error("Failed to create project %s".formatted(jsonProject), e); + throw new ServerErrorException(Response.Status.INTERNAL_SERVER_ERROR); } - } - final Project project; - try { - project = qm.createProject(jsonProject, jsonProject.getTags(), true); - } catch (IllegalArgumentException e) { - LOGGER.debug("Failed to create project", e); - return Response - .status(Response.Status.CONFLICT) - .entity("An inactive Parent cannot be selected as parent") - .build(); - } - qm.updateNewProjectACL(project, principal); - LOGGER.info("Project " + project.toString() + " created by " + super.getPrincipal().getName()); - return Response.status(Response.Status.CREATED).entity(project).build(); + qm.updateNewProjectACL(project, principal); + return project; + }); + + LOGGER.info("Project " + createdProject + " created by " + super.getPrincipal().getName()); + return Response.status(Response.Status.CREATED).entity(createdProject).build(); } } @@ -504,12 +516,22 @@ public Response updateProject(Project jsonProject) { if (jsonProject.getClassifier() == null) { jsonProject.setClassifier(Classifier.APPLICATION); } - try (QueryManager qm = new QueryManager()) { - Project project = qm.getObjectByUuid(Project.class, jsonProject.getUuid()); - if (project != null) { + try (final var qm = new QueryManager()) { + final Project updatedProject = qm.callInTransaction(() -> { + final Project project = qm.getObjectByUuid(Project.class, jsonProject.getUuid()); + if (project == null) { + throw new ClientErrorException(Response + .status(Response.Status.NOT_FOUND) + .entity("The UUID of the project could not be found.") + .build()); + } if (!qm.hasAccess(super.getPrincipal(), project)) { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + throw new ClientErrorException(Response + .status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden") + .build()); } + String name = StringUtils.trimToNull(jsonProject.getName()); // Name cannot be empty or null - prevent it if (name == null) { @@ -518,33 +540,39 @@ public Response updateProject(Project jsonProject) { } // if project is newly set to latest, ensure user has access to current latest version to modify it - if(jsonProject.isLatest() && !project.isLatest()) { + if (jsonProject.isLatest() && !project.isLatest()) { final Project oldLatest = qm.getLatestProjectVersion(name); - if(oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { - return Response.status(Response.Status.FORBIDDEN) + if (oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { + throw new ClientErrorException(Response + .status(Response.Status.FORBIDDEN) .entity("Cannot set this project version to latest. Access to current latest " + "version is forbidden!") - .build(); + .build()); } } final String version = StringUtils.trimToNull(jsonProject.getVersion()); final Project tmpProject = qm.getProject(name, version); - if (tmpProject == null || (tmpProject.getUuid().equals(project.getUuid()))) { - try { - project = qm.updateProject(jsonProject, true); - } catch (IllegalArgumentException e){ - LOGGER.debug(e.getMessage()); - return Response.status(Response.Status.CONFLICT).entity(e.getMessage()).build(); - } - LOGGER.info("Project " + project.toString() + " updated by " + super.getPrincipal().getName()); - return Response.ok(project).build(); - } else { - return Response.status(Response.Status.CONFLICT).entity("A project with the specified name and version already exists.").build(); + if (tmpProject != null && !tmpProject.getUuid().equals(project.getUuid())) { + throw new ClientErrorException(Response + .status(Response.Status.CONFLICT) + .entity("A project with the specified name and version already exists.") + .build()); } - } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); - } + + try { + return qm.updateProject(jsonProject, true); + } catch (IllegalArgumentException e) { + LOGGER.debug("Failed to update project %s".formatted(jsonProject.getUuid()), e); + throw new ClientErrorException(Response + .status(Response.Status.CONFLICT) + .entity(e.getMessage()) + .build()); + } + }); + + LOGGER.info("Project " + updatedProject + " updated by " + super.getPrincipal().getName()); + return Response.ok(updatedProject).build(); } } @@ -592,22 +620,32 @@ public Response patchProject( validator.validateProperty(jsonProject, "swidTagId") ); - try (QueryManager qm = new QueryManager()) { - Project project = qm.getObjectByUuid(Project.class, uuid); - if (project != null) { + try (final var qm = new QueryManager()) { + final Project updatedProject = qm.callInTransaction(() -> { + Project project = qm.getObjectByUuid(Project.class, uuid); + if (project == null) { + throw new ClientErrorException(Response + .status(Response.Status.NOT_FOUND) + .entity("The UUID of the project could not be found.") + .build()); + } if (!qm.hasAccess(super.getPrincipal(), project)) { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + throw new ClientErrorException(Response + .status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden") + .build()); } // if project is newly set to latest, ensure user has access to current latest version to modify it - if(jsonProject.isLatest() && !project.isLatest()) { + if (jsonProject.isLatest() && !project.isLatest()) { final var oldName = jsonProject.getName() != null ? jsonProject.getName() : project.getName(); final Project oldLatest = qm.getLatestProjectVersion(oldName); - if(oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { - return Response.status(Response.Status.FORBIDDEN) + if (oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { + throw new ClientErrorException(Response + .status(Response.Status.FORBIDDEN) .entity("Cannot set this project version to latest. Access to current latest " + "version is forbidden!") - .build(); + .build()); } } @@ -617,7 +655,10 @@ public Response patchProject( modified |= setIfDifferent(jsonProject, project, Project::getVersion, Project::setVersion); // if either name or version has been changed, verify that this new combination does not already exist if (modified && qm.doesProjectExist(project.getName(), project.getVersion())) { - return Response.status(Response.Status.CONFLICT).entity("A project with the specified name and version already exists.").build(); + throw new ClientErrorException(Response + .status(Response.Status.CONFLICT) + .entity("A project with the specified name and version already exists.") + .build()); } modified |= setIfDifferent(jsonProject, project, Project::getAuthors, Project::setAuthors); modified |= setIfDifferent(jsonProject, project, Project::getPublisher, Project::setPublisher); @@ -634,10 +675,16 @@ public Response patchProject( if (jsonProject.getParent() != null && jsonProject.getParent().getUuid() != null) { final Project parent = qm.getObjectByUuid(Project.class, jsonProject.getParent().getUuid()); if (parent == null) { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the parent project could not be found.").build(); + throw new ClientErrorException(Response + .status(Response.Status.NOT_FOUND) + .entity("The UUID of the parent project could not be found.") + .build()); } if (!qm.hasAccess(getPrincipal(), parent)) { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified parent project is forbidden").build(); + throw new ClientErrorException(Response + .status(Response.Status.FORBIDDEN) + .entity("Access to the specified parent project is forbidden") + .build()); } modified |= project.getParent() == null || !parent.getUuid().equals(project.getParent().getUuid()); project.setParent(parent); @@ -647,24 +694,34 @@ public Response patchProject( project.setTags(jsonProject.getTags()); } if (isCollectionModified(jsonProject.getExternalReferences(), project.getExternalReferences())) { - modified = true; - project.setExternalReferences(jsonProject.getExternalReferences()); - } - if (modified) { - try { - project = qm.updateProject(project, true); - } catch (IllegalArgumentException e){ - LOGGER.debug(e.getMessage()); - return Response.status(Response.Status.CONFLICT).entity(e.getMessage()).build(); - } - LOGGER.info("Project " + project.toString() + " updated by " + super.getPrincipal().getName()); - return Response.ok(project).build(); - } else { - return Response.notModified().build(); + modified = true; + project.setExternalReferences(jsonProject.getExternalReferences()); } - } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); + + if (!modified) { + return null; + } + + try { + return qm.updateProject(project, true); + } catch (IllegalArgumentException e) { + LOGGER.debug("Failed to patch project %s".formatted(uuid)); + throw new ClientErrorException(Response + .status(Response.Status.CONFLICT) + .entity(e.getMessage()) + .build()); + } catch (RuntimeException e) { + LOGGER.error("Failed to patch project %s".formatted(uuid), e); + throw new ServerErrorException(Response.Status.INTERNAL_SERVER_ERROR); + } + }); + + if (updatedProject == null) { + return Response.notModified().build(); } + + LOGGER.info("Project " + updatedProject + " updated by " + super.getPrincipal().getName()); + return Response.ok(updatedProject).build(); } } @@ -718,19 +775,32 @@ private boolean setIfDifferent(final Project source, final Project target, f public Response deleteProject( @Parameter(description = "The UUID of the project to delete", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { - try (QueryManager qm = new QueryManager()) { - final Project project = qm.getObjectByUuid(Project.class, uuid, Project.FetchGroup.ALL.name()); - if (project != null) { - if (qm.hasAccess(super.getPrincipal(), project)) { - LOGGER.info("Project " + project + " deletion request by " + super.getPrincipal().getName()); + try (final var qm = new QueryManager()) { + qm.runInTransaction(() -> { + final Project project = qm.getObjectByUuid(Project.class, uuid, Project.FetchGroup.ALL.name()); + if (project == null) { + throw new ClientErrorException(Response + .status(Response.Status.NOT_FOUND) + .entity("The UUID of the project could not be found.") + .build()); + } + if (!qm.hasAccess(super.getPrincipal(), project)) { + throw new ClientErrorException(Response + .status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden") + .build()); + } + + LOGGER.info("Project " + project + " deletion request by " + super.getPrincipal().getName()); + try { qm.recursivelyDelete(project, true); - return Response.status(Response.Status.NO_CONTENT).build(); - } else { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + } catch (RuntimeException e) { + LOGGER.error("Failed to delete project", e); + throw new ServerErrorException(Response.Status.INTERNAL_SERVER_ERROR); } - } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); - } + }); + + return Response.status(Response.Status.NO_CONTENT).build(); } } @@ -761,34 +831,47 @@ public Response cloneProject(CloneProjectRequest jsonRequest) { validator.validateProperty(jsonRequest, "project"), validator.validateProperty(jsonRequest, "version") ); - try (QueryManager qm = new QueryManager()) { - final Project sourceProject = qm.getObjectByUuid(Project.class, jsonRequest.getProject(), Project.FetchGroup.ALL.name()); - if (sourceProject != null) { + try (final var qm = new QueryManager()) { + final CloneProjectEvent cloneEvent = qm.callInTransaction(() -> { + final Project sourceProject = qm.getObjectByUuid(Project.class, jsonRequest.getProject(), Project.FetchGroup.ALL.name()); + if (sourceProject == null) { + throw new ClientErrorException(Response + .status(Response.Status.NOT_FOUND) + .entity("The UUID of the project could not be found.") + .build()); + } + if (!qm.hasAccess(super.getPrincipal(), sourceProject)) { - return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); + throw new ClientErrorException(Response + .status(Response.Status.FORBIDDEN) + .entity("Access to the specified project is forbidden") + .build()); } if (qm.doesProjectExist(sourceProject.getName(), StringUtils.trimToNull(jsonRequest.getVersion()))) { - return Response.status(Response.Status.CONFLICT).entity("A project with the specified name and version already exists.").build(); + throw new ClientErrorException(Response + .status(Response.Status.CONFLICT) + .entity("A project with the specified name and version already exists.") + .build()); } // if project is newly set to latest, ensure user has access to current latest version to modify it - if(jsonRequest.makeCloneLatest() && !sourceProject.isLatest()) { + if (jsonRequest.makeCloneLatest() && !sourceProject.isLatest()) { final Project oldLatest = qm.getLatestProjectVersion(sourceProject.getName()); - if(oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { - return Response.status(Response.Status.FORBIDDEN) - .entity("Cannot set cloned project version to latest. Access to current latest " + + if (oldLatest != null && !qm.hasAccess(super.getPrincipal(), oldLatest)) { + throw new ClientErrorException(Response + .status(Response.Status.FORBIDDEN) + .entity("Cannot set this project version to latest. Access to current latest " + "version is forbidden!") - .build(); + .build()); } } LOGGER.info("Project " + sourceProject + " is being cloned by " + super.getPrincipal().getName()); - CloneProjectEvent event = new CloneProjectEvent(jsonRequest); - Event.dispatch(event); - return Response.ok(java.util.Collections.singletonMap("token", event.getChainIdentifier())).build(); - } else { - return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the project could not be found.").build(); - } + return new CloneProjectEvent(jsonRequest); + }); + + Event.dispatch(cloneEvent); + return Response.ok(Map.of("token", cloneEvent.getChainIdentifier())).build(); } } diff --git a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java index b3c13396c4..8c1f152c2f 100644 --- a/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/ProjectResourceTest.java @@ -219,6 +219,42 @@ public void getProjectLookupTest() { Assert.assertEquals("100", json.getJsonArray("versions").getJsonObject(100).getString("version")); } + @Test + public void getProjectLookupNotFoundTest() { + final var project = new Project(); + project.setName("acme-app"); + project.setVersion("1.2.3"); + qm.persist(project); + + final Response response = jersey.target(V1_PROJECT + "/lookup") + .queryParam("name", "acme-app") + .queryParam("version", "3.2.1") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(404); + assertThat(getPlainTextBody(response)).isEqualTo("The project could not be found."); + } + + @Test + public void getProjectLookupNotPermittedTest() { + enablePortfolioAccessControl(); + + final var project = new Project(); + project.setName("acme-app"); + project.setVersion("1.2.3"); + qm.persist(project); + + final Response response = jersey.target(V1_PROJECT + "/lookup") + .queryParam("name", "acme-app") + .queryParam("version", "1.2.3") + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(403); + assertThat(getPlainTextBody(response)).isEqualTo("Access to the specified project is forbidden"); + } + @Test public void getProjectsAscOrderedRequestTest() { qm.createProject("ABC", null, "1.0", null, null, null, true, false); @@ -315,6 +351,22 @@ public void getProjectByUuidTest() { """); } + @Test + public void getProjectByUuidNotPermittedTest() { + enablePortfolioAccessControl(); + + final var project = new Project(); + project.setName("acme-app"); + qm.persist(project); + + final Response response = jersey.target(V1_PROJECT + "/" + project.getUuid()) + .request() + .header(X_API_KEY, apiKey) + .get(); + assertThat(response.getStatus()).isEqualTo(403); + assertThat(getPlainTextBody(response)).isEqualTo("Access to the specified project is forbidden"); + } + @Test public void validateProjectVersionsActiveInactiveTest() { Project project = qm.createProject("ABC", null, "1.0", null, null, null, true, false); @@ -451,6 +503,30 @@ public void createProjectDuplicateTest() { Assert.assertEquals("A project with the specified name already exists.", body); } + @Test + public void createProjectInactiveParentTest() { + final var parentProject = new Project(); + parentProject.setName("acme-app-parent"); + parentProject.setVersion("1.0.0"); + parentProject.setActive(false); + qm.persist(parentProject); + + final Response response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .put(Entity.json(""" + { + "parent": { + "uuid": "%s" + }, + "name": "acme-app", + "version": "1.2.3" + } + """.formatted(parentProject.getUuid()))); + assertThat(response.getStatus()).isEqualTo(409); + assertThat(getPlainTextBody(response)).isEqualTo("An inactive Parent cannot be selected as parent"); + } + @Test public void createProjectWithoutVersionDuplicateTest() { Project project = new Project(); @@ -897,6 +973,43 @@ public void updateProjectTest() { Assert.assertEquals("Test project", json.getString("description")); } + @Test + public void updateProjectNotFoundTest() { + final Response response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .post(Entity.json(""" + { + "uuid": "317fe231-01a4-4435-92ad-abd01017bb1a", + "name": "acme-app", + "version": "1.2.3" + } + """)); + assertThat(response.getStatus()).isEqualTo(404); + assertThat(getPlainTextBody(response)).isEqualTo("The UUID of the project could not be found."); + } + + @Test + public void updateProjectNotPermittedTest() { + enablePortfolioAccessControl(); + + final var project = new Project(); + project.setName("acme-app"); + qm.persist(project); + + final Response response = jersey.target(V1_PROJECT) + .request() + .header(X_API_KEY, apiKey) + .post(Entity.json(""" + { + "uuid": "%s", + "name": "acme-app-foo" + } + """.formatted(project.getUuid()))); + assertThat(response.getStatus()).isEqualTo(403); + assertThat(getPlainTextBody(response)).isEqualTo("Access to the specified project is forbidden"); + } + @Test public void updateProjectTestIsActiveEqualsNull() { Project project = qm.createProject("ABC", null, "1.0", null, null, null, true, false); @@ -1157,6 +1270,27 @@ public void patchProjectNotFoundTest() { Assert.assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); } + @Test + public void patchProjectNotPermittedTest() { + enablePortfolioAccessControl(); + + final var project = new Project(); + project.setName("acme-app"); + qm.persist(project); + + final Response response = jersey.target(V1_PROJECT + "/" + project.getUuid()) + .request() + .header(X_API_KEY, apiKey) + .property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true) + .method("PATCH", Entity.json(""" + { + "name": "acme-app-foo" + } + """)); + assertThat(response.getStatus()).isEqualTo(403); + assertThat(getPlainTextBody(response)).isEqualTo("Access to the specified project is forbidden"); + } + @Test public void patchProjectSuccessfullyPatchedTest() { final var tags = Stream.of("tag1", "tag2").map(qm::createTag).collect(Collectors.toUnmodifiableList()); From 9456eed830e6e1da348f00b9d6822e9facc7d07b Mon Sep 17 00:00:00 2001 From: nscuro Date: Mon, 30 Sep 2024 20:20:34 +0200 Subject: [PATCH 201/429] Fix metrics endpoint API docs erroneously claiming to return project and component data Fixes #3884 Supersedes #3918 Signed-off-by: nscuro --- src/main/java/org/dependencytrack/model/DependencyMetrics.java | 2 ++ src/main/java/org/dependencytrack/model/ProjectMetrics.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/model/DependencyMetrics.java b/src/main/java/org/dependencytrack/model/DependencyMetrics.java index 117cb20a6c..4ed1bc1b6b 100644 --- a/src/main/java/org/dependencytrack/model/DependencyMetrics.java +++ b/src/main/java/org/dependencytrack/model/DependencyMetrics.java @@ -53,11 +53,13 @@ public class DependencyMetrics implements Serializable { @Persistent @Column(name = "PROJECT_ID", allowsNull = "false") @NotNull + @JsonIgnore private Project project; @Persistent @Column(name = "COMPONENT_ID", allowsNull = "false") @NotNull + @JsonIgnore private Component component; @Persistent diff --git a/src/main/java/org/dependencytrack/model/ProjectMetrics.java b/src/main/java/org/dependencytrack/model/ProjectMetrics.java index 1be2453552..86706da802 100644 --- a/src/main/java/org/dependencytrack/model/ProjectMetrics.java +++ b/src/main/java/org/dependencytrack/model/ProjectMetrics.java @@ -51,7 +51,7 @@ public class ProjectMetrics implements Serializable { @Persistent @Column(name = "PROJECT_ID", allowsNull = "false") - @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + @JsonIgnore private Project project; @Persistent From 83ff16614a8b5787acb64a2bb7ef55c4124302bc Mon Sep 17 00:00:00 2001 From: Peter Kimball <4459325+peterakimball@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:36:24 -0700 Subject: [PATCH 202/429] Add test which reproduces behavior seen in #3826 Adds an OSV JSON which, when consumed, causes the behavior exhibited in #3826. Adds a test which raises the IndexOutOfBoundsException. Signed-off-by: Peter Kimball <4459325+peterakimball@users.noreply.github.com> --- .../tasks/OsvDownloadTaskTest.java | 6 + .../osv.jsons/osv-CURL-CVE-2009-0037.json | 145 ++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 src/test/resources/unit/osv.jsons/osv-CURL-CVE-2009-0037.json diff --git a/src/test/java/org/dependencytrack/tasks/OsvDownloadTaskTest.java b/src/test/java/org/dependencytrack/tasks/OsvDownloadTaskTest.java index c220a72e6d..73e646b8e6 100644 --- a/src/test/java/org/dependencytrack/tasks/OsvDownloadTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/OsvDownloadTaskTest.java @@ -428,6 +428,12 @@ public void testCalculateOSVSeverity() throws IOException { Assert.assertNotNull(advisory); severity = task.calculateOSVSeverity(advisory); Assert.assertEquals(Severity.UNASSIGNED, severity); + + prepareJsonObject("src/test/resources/unit/osv.jsons/osv-CURL-CVE-2009-0037.json"); + advisory = parser.parse(jsonObject); + Assert.assertNotNull(advisory); + severity = task.calculateOSVSeverity(advisory); + Assert.assertEquals(Severity.UNASSIGNED, severity); } @Test diff --git a/src/test/resources/unit/osv.jsons/osv-CURL-CVE-2009-0037.json b/src/test/resources/unit/osv.jsons/osv-CURL-CVE-2009-0037.json new file mode 100644 index 0000000000..a0f9115208 --- /dev/null +++ b/src/test/resources/unit/osv.jsons/osv-CURL-CVE-2009-0037.json @@ -0,0 +1,145 @@ +{ + "id": "CURL-CVE-2009-0037", + "summary": "Arbitrary File Access", + "details": "When told to follow a \"redirect\" automatically, libcurl does not question the\nnew target URL but follows it to any new URL that it understands. As libcurl\nsupports FILE:// URLs, a rogue server can thus \"trick\" a libcurl-using\napplication to read a local file instead of the remote one.\n\nThis is a problem, for example, when the application is running on a server\nand is written to upload or to otherwise provide the transferred data to a\nuser, to another server or to another application etc, as it can be used to\nexpose local files it was not meant to.\n\nThe problem can also be exploited for uploading, if the rogue server\nredirects the client to a local file and thus it would (over)write a local\nfile instead of sending it to the server.\n\nlibcurl compiled to support SCP can get tricked to get a file using embedded\nsemicolons, which can lead to execution of commands on the given\nserver. `Location: scp://name:passwd@host/a;date >/tmp/test;`.\n\nFiles on servers other than the one running libcurl are also accessible when\ncredentials for those servers are stored in the .netrc file of the user\nrunning libcurl. This is most common for FTP servers, but can occur with\nany protocol supported by libcurl. Files on remote SSH servers are also\naccessible when the user has an unencrypted SSH key.", + "aliases": [ + "CVE-2009-0037" + ], + "modified": "2024-07-02T09:22:24Z", + "published": "2009-03-03T08:00:00Z", + "database_specific": { + "CWE": { + "id": "CWE-142", + "desc": "Improper Neutralization of Value Delimiters" + }, + "package": "curl", + "URL": "https://curl.se/docs/CVE-2009-0037.json", + "severity": "Medium", + "www": "https://curl.se/docs/CVE-2009-0037.html", + "last_affected": "7.19.3" + }, + "affected": [ + { + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "5.11" + }, + { + "fixed": "7.19.4" + } + ] + }, + { + "type": "GIT", + "repo": "https://github.com/curl/curl.git", + "events": [ + { + "introduced": "ae1912cb0d494b48d514d937826c9fe83ec96c4d" + }, + { + "fixed": "042cc1f69ec0878f542667cb684378869f859911" + } + ] + } + ], + "versions": [ + "7.19.3", + "7.19.2", + "7.19.1", + "7.19.0", + "7.18.2", + "7.18.1", + "7.18.0", + "7.17.1", + "7.17.0", + "7.16.4", + "7.16.3", + "7.16.2", + "7.16.1", + "7.16.0", + "7.15.5", + "7.15.4", + "7.15.3", + "7.15.2", + "7.15.1", + "7.15.0", + "7.14.1", + "7.14.0", + "7.13.2", + "7.13.1", + "7.13.0", + "7.12.3", + "7.12.2", + "7.12.1", + "7.12.0", + "7.11.2", + "7.11.1", + "7.11.0", + "7.10.8", + "7.10.7", + "7.10.6", + "7.10.5", + "7.10.4", + "7.10.3", + "7.10.2", + "7.10.1", + "7.10", + "7.9.8", + "7.9.7", + "7.9.6", + "7.9.5", + "7.9.4", + "7.9.3", + "7.9.2", + "7.9.1", + "7.9", + "7.8.1", + "7.8", + "7.7.3", + "7.7.2", + "7.7.1", + "7.7", + "7.6.1", + "7.6", + "7.5.2", + "7.5.1", + "7.5", + "7.4.2", + "7.4.1", + "7.4", + "7.3", + "7.2.1", + "7.2", + "7.1.1", + "7.1", + "6.5.2", + "6.5.1", + "6.5", + "6.4", + "6.3.1", + "6.3", + "6.2", + "6.1", + "6.0", + "5.11" + ], + "database_specific": { + "source": "https://curl.se/docs/CURL-CVE-2009-0037.json" + } + } + ], + "schema_version": "1.6.0", + "credits": [ + { + "name": "David Kierznowski", + "type": "FINDER" + }, + { + "name": "Daniel Stenberg", + "type": "REMEDIATION_DEVELOPER" + } + ] +} \ No newline at end of file From 0fa5f3a9908224b5dcd8fef3ed595149764797db Mon Sep 17 00:00:00 2001 From: Peter Kimball <4459325+peterakimball@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:41:18 -0700 Subject: [PATCH 203/429] Fix exception when handling certain OSV advisories Do not attempt to calculate the severity of an advisory that has no CVSS vectors, no explicit severity, and no affected packages. Signed-off-by: Peter Kimball <4459325+peterakimball@users.noreply.github.com> --- src/main/java/org/dependencytrack/tasks/OsvDownloadTask.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/OsvDownloadTask.java b/src/main/java/org/dependencytrack/tasks/OsvDownloadTask.java index dcc34ff8dd..21272c6653 100644 --- a/src/main/java/org/dependencytrack/tasks/OsvDownloadTask.java +++ b/src/main/java/org/dependencytrack/tasks/OsvDownloadTask.java @@ -290,14 +290,13 @@ public Severity calculateOSVSeverity(OsvAdvisory advisory) { } } // get largest ecosystem_specific severity from its affected packages - if (advisory.getAffectedPackages() != null) { + if (!advisory.getAffectedPackages().isEmpty()) { List severityLevels = new ArrayList<>(); for (OsvAffectedPackage vuln : advisory.getAffectedPackages()) { severityLevels.add(vuln.getSeverity().getLevel()); } Collections.sort(severityLevels); - Collections.reverse(severityLevels); - return getSeverityByLevel(severityLevels.get(0)); + return getSeverityByLevel(severityLevels.getLast()); } return Severity.UNASSIGNED; } From 47b2967e42df62ae18ee4c9552a48ad23774a5f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 08:15:14 +0000 Subject: [PATCH 204/429] Bump io.github.jeremylong:open-vulnerability-clients from 6.2.0 to 7.0.0 Bumps [io.github.jeremylong:open-vulnerability-clients](https://github.com/jeremylong/vuln-tools) from 6.2.0 to 7.0.0. - [Release notes](https://github.com/jeremylong/vuln-tools/releases) - [Commits](https://github.com/jeremylong/vuln-tools/compare/v6.2.0...v7.0.0) --- updated-dependencies: - dependency-name: io.github.jeremylong:open-vulnerability-clients dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2554dbaaf4..2ed8f1c60b 100644 --- a/pom.xml +++ b/pom.xml @@ -108,7 +108,7 @@ 8.11.4 3.9.9 5.15.0 - 6.2.0 + 7.0.0 1.5.0 3.2.2 4.28.2 From 130bc0e9a46f12fbbe17ee8b10417bdc609963b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 08:15:26 +0000 Subject: [PATCH 205/429] Bump org.testcontainers:testcontainers from 1.20.1 to 1.20.2 Bumps [org.testcontainers:testcontainers](https://github.com/testcontainers/testcontainers-java) from 1.20.1 to 1.20.2. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.20.1...1.20.2) --- updated-dependencies: - dependency-name: org.testcontainers:testcontainers dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2554dbaaf4..c7f0c5105d 100644 --- a/pom.xml +++ b/pom.xml @@ -115,7 +115,7 @@ 2.2.0 2.1.22 1.19.0 - 1.20.1 + 1.20.2 2.35.2 7.0.0 1.1.1 From c6ae7576b1b1182a3d2f8115a469f437f5da070d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Tue, 1 Oct 2024 11:10:19 +0200 Subject: [PATCH 206/429] Fix: Projects the Principal has no access to are removed before sending MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../resources/v1/VulnerabilityResource.java | 19 +++ .../v1/VulnerabilityResourceTest.java | 137 ++++++++++++++++-- 2 files changed, 140 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/VulnerabilityResource.java b/src/main/java/org/dependencytrack/resources/v1/VulnerabilityResource.java index b233d29e3b..e48c587d97 100644 --- a/src/main/java/org/dependencytrack/resources/v1/VulnerabilityResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/VulnerabilityResource.java @@ -35,6 +35,7 @@ import org.dependencytrack.auth.Permissions; import org.dependencytrack.model.AffectedVersionAttribution; import org.dependencytrack.model.Component; +import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.Cwe; import org.dependencytrack.model.Project; import org.dependencytrack.model.Vulnerability; @@ -65,6 +66,7 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.math.BigDecimal; +import java.security.Principal; import java.util.ArrayList; import java.util.List; @@ -219,6 +221,14 @@ public Response getVulnerabilityByVulnId(@PathParam("source") String source, affectedComponents.add(affectedComponent); } vulnerability.setAffectedComponents(affectedComponents); + qm.makeTransient(vulnerability); + Principal principal = super.getPrincipal(); + boolean shouldFilter = qm.isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED); + if (shouldFilter) { + vulnerability.setComponents( + vulnerability.getComponents().stream().filter(component -> qm.hasAccess(principal, + component.getProject())).toList()); + } return Response.ok(vulnerability).build(); } else { return Response.status(Response.Status.NOT_FOUND).entity("The vulnerability could not be found.").build(); @@ -256,6 +266,15 @@ public Response getAffectedProject(@PathParam("source") String source, final long filteredCount = filteredProjects.size(); return Response.ok(filteredProjects).header(TOTAL_COUNT_HEADER, filteredCount).build(); } + qm.makeTransient(vulnerability); + Principal principal = super.getPrincipal(); + boolean shouldFilter = qm.isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED); + if (shouldFilter) { + vulnerability.setComponents( + vulnerability.getComponents().stream().filter(component -> qm.hasAccess(principal, + component.getProject())).toList()); + } + final List projects = qm.getAffectedProjects(vulnerability); final long totalCount = projects.size(); return Response.ok(projects).header(TOTAL_COUNT_HEADER, totalCount).build(); diff --git a/src/test/java/org/dependencytrack/resources/v1/VulnerabilityResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/VulnerabilityResourceTest.java index d217b0a290..bb12664832 100644 --- a/src/test/java/org/dependencytrack/resources/v1/VulnerabilityResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/VulnerabilityResourceTest.java @@ -19,6 +19,7 @@ package org.dependencytrack.resources.v1; import alpine.common.util.UuidUtil; +import alpine.model.Team; import alpine.server.filters.ApiFilter; import alpine.server.filters.AuthenticationFilter; import org.dependencytrack.JerseyTestRule; @@ -28,6 +29,7 @@ import org.dependencytrack.model.AnalysisResponse; import org.dependencytrack.model.AnalysisState; import org.dependencytrack.model.Component; +import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.Project; import org.dependencytrack.model.Severity; import org.dependencytrack.model.Vulnerability; @@ -62,10 +64,10 @@ public void getVulnerabilitiesByComponentUuidTest() { .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(200, response.getStatus(), 0); - Assert.assertEquals(String.valueOf(2), response.getHeaderString(TOTAL_COUNT_HEADER)); + Assert.assertEquals(String.valueOf(3), response.getHeaderString(TOTAL_COUNT_HEADER)); JsonArray json = parseJsonArray(response); Assert.assertNotNull(json); - Assert.assertEquals(2, json.size()); + Assert.assertEquals(3, json.size()); Assert.assertEquals("INT-1", json.getJsonObject(0).getString("vulnId")); Assert.assertEquals("INTERNAL", json.getJsonObject(0).getString("source")); Assert.assertEquals("Description 1", json.getJsonObject(0).getString("description")); @@ -103,10 +105,10 @@ public void getVulnerabilitiesByComponentUuidIncludeSuppressedTest() { .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(200, response.getStatus(), 0); - Assert.assertEquals(String.valueOf(3), response.getHeaderString(TOTAL_COUNT_HEADER)); + Assert.assertEquals(String.valueOf(4), response.getHeaderString(TOTAL_COUNT_HEADER)); JsonArray json = parseJsonArray(response); Assert.assertNotNull(json); - Assert.assertEquals(3, json.size()); + Assert.assertEquals(4, json.size()); Assert.assertEquals("INT-1", json.getJsonObject(0).getString("vulnId")); Assert.assertEquals("INTERNAL", json.getJsonObject(0).getString("source")); Assert.assertEquals("Description 1", json.getJsonObject(0).getString("description")); @@ -131,10 +133,10 @@ public void getVulnerabilitiesByProjectTest() { .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(200, response.getStatus(), 0); - Assert.assertEquals(String.valueOf(4), response.getHeaderString(TOTAL_COUNT_HEADER)); + Assert.assertEquals(String.valueOf(5), response.getHeaderString(TOTAL_COUNT_HEADER)); JsonArray json = parseJsonArray(response); Assert.assertNotNull(json); - Assert.assertEquals(4, json.size()); + Assert.assertEquals(5, json.size()); Assert.assertEquals("INT-1", json.getJsonObject(0).getString("vulnId")); Assert.assertEquals("INTERNAL", json.getJsonObject(0).getString("source")); Assert.assertEquals("Description 1", json.getJsonObject(0).getString("description")); @@ -145,16 +147,21 @@ public void getVulnerabilitiesByProjectTest() { Assert.assertEquals("Description 2", json.getJsonObject(1).getString("description")); Assert.assertEquals("HIGH", json.getJsonObject(1).getString("severity")); Assert.assertTrue(UuidUtil.isValidUUID(json.getJsonObject(1).getString("uuid"))); - Assert.assertEquals("INT-4", json.getJsonObject(2).getString("vulnId")); + Assert.assertEquals("INT-6", json.getJsonObject(2).getString("vulnId")); Assert.assertEquals("INTERNAL", json.getJsonObject(2).getString("source")); - Assert.assertEquals("Description 4", json.getJsonObject(2).getString("description")); - Assert.assertEquals("LOW", json.getJsonObject(2).getString("severity")); + Assert.assertEquals("Description 6", json.getJsonObject(2).getString("description")); + Assert.assertEquals("CRITICAL", json.getJsonObject(2).getString("severity")); Assert.assertTrue(UuidUtil.isValidUUID(json.getJsonObject(2).getString("uuid"))); - Assert.assertEquals("INT-5", json.getJsonObject(3).getString("vulnId")); + Assert.assertEquals("INT-4", json.getJsonObject(3).getString("vulnId")); Assert.assertEquals("INTERNAL", json.getJsonObject(3).getString("source")); - Assert.assertEquals("Description 5", json.getJsonObject(3).getString("description")); - Assert.assertEquals("CRITICAL", json.getJsonObject(3).getString("severity")); + Assert.assertEquals("Description 4", json.getJsonObject(3).getString("description")); + Assert.assertEquals("LOW", json.getJsonObject(3).getString("severity")); Assert.assertTrue(UuidUtil.isValidUUID(json.getJsonObject(3).getString("uuid"))); + Assert.assertEquals("INT-5", json.getJsonObject(4).getString("vulnId")); + Assert.assertEquals("INTERNAL", json.getJsonObject(4).getString("source")); + Assert.assertEquals("Description 5", json.getJsonObject(4).getString("description")); + Assert.assertEquals("CRITICAL", json.getJsonObject(4).getString("severity")); + Assert.assertTrue(UuidUtil.isValidUUID(json.getJsonObject(4).getString("uuid"))); } @Test @@ -166,10 +173,10 @@ public void getVulnerabilitiesByProjectIncludeProjectSuppressedTest() { .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(200, response.getStatus(), 0); - Assert.assertEquals(String.valueOf(2), response.getHeaderString(TOTAL_COUNT_HEADER)); + Assert.assertEquals(String.valueOf(3), response.getHeaderString(TOTAL_COUNT_HEADER)); JsonArray json = parseJsonArray(response); Assert.assertNotNull(json); - Assert.assertEquals(2, json.size()); + Assert.assertEquals(3, json.size()); Assert.assertEquals("INT-4", json.getJsonObject(0).getString("vulnId")); Assert.assertEquals("INT-5", json.getJsonObject(1).getString("vulnId")); } @@ -241,6 +248,50 @@ public void getVulnerabilityByVulnIdInvalidTest() { Assert.assertEquals("The vulnerability could not be found.", body); } + @Test + public void getVulnerabilityByVulnIdACLEnabledTest() { + SampleData sampleData = new SampleData(); + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + Response response = jersey.target(V1_VULNERABILITY + "/source/" + sampleData.v6.getSource() + "/vuln/" + sampleData.v6.getVulnId()).request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + JsonObject json = parseJsonObject(response); + Assert.assertNotNull(json); + Assert.assertEquals("INT-6", json.getString("vulnId")); + JsonArray components = json.getJsonArray("components"); + Assert.assertNotNull(components); + Assert.assertEquals(1, components.size()); + } + + @Test + public void getVulnerabilityByVulnIdACLDisabledTest() { + SampleData sampleData = new SampleData(); + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "false", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + Response response = jersey.target(V1_VULNERABILITY + "/source/" + sampleData.v6.getSource() + "/vuln/" + sampleData.v6.getVulnId()).request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + JsonObject json = parseJsonObject(response); + Assert.assertNotNull(json); + Assert.assertEquals("INT-6", json.getString("vulnId")); + JsonArray components = json.getJsonArray("components"); + Assert.assertNotNull(components); + Assert.assertEquals(2, components.size()); + } + @Test public void getAffectedProjectTest() { SampleData sampleData = new SampleData(); @@ -268,6 +319,48 @@ public void getAffectedProjectInvalidTest() { Assert.assertEquals("The vulnerability could not be found.", body); } + @Test + public void getAffectedProjectACLEnabledTest() { + SampleData sampleData = new SampleData(); + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + Response response = jersey.target(V1_VULNERABILITY + "/source/" + sampleData.v6.getSource() + "/vuln/" + sampleData.v6.getVulnId() + "/projects").request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + JsonArray json = parseJsonArray(response); + Assert.assertNotNull(json); + Assert.assertEquals(1, json.size()); + Assert.assertEquals("Project 1", json.getJsonObject(0).getString("name")); + Assert.assertEquals(sampleData.p1.getUuid().toString(), json.getJsonObject(0).getString("uuid")); + } + + @Test + public void getAffectedProjectACLDisabledTest() { + SampleData sampleData = new SampleData(); + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "false", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + Response response = jersey.target(V1_VULNERABILITY + "/source/" + sampleData.v6.getSource() + "/vuln/" + sampleData.v6.getVulnId() + "/projects").request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + JsonArray json = parseJsonArray(response); + Assert.assertNotNull(json); + Assert.assertEquals(2, json.size()); + Assert.assertEquals("Project 2", json.getJsonObject(0).getString("name")); + Assert.assertEquals(sampleData.p2.getUuid().toString(), json.getJsonObject(0).getString("uuid")); + Assert.assertEquals("Project 1", json.getJsonObject(1).getString("name")); + Assert.assertEquals(sampleData.p1.getUuid().toString(), json.getJsonObject(1).getString("uuid")); + } + @Test public void getAllVulnerabilitiesTest() { new SampleData(); @@ -275,10 +368,10 @@ public void getAllVulnerabilitiesTest() { .header(X_API_KEY, apiKey) .get(Response.class); Assert.assertEquals(200, response.getStatus(), 0); - Assert.assertEquals(String.valueOf(5), response.getHeaderString(TOTAL_COUNT_HEADER)); + Assert.assertEquals(String.valueOf(6), response.getHeaderString(TOTAL_COUNT_HEADER)); JsonArray json = parseJsonArray(response); Assert.assertNotNull(json); - Assert.assertEquals(5, json.size()); + Assert.assertEquals(6, json.size()); Assert.assertEquals("INT-1", json.getJsonObject(0).getString("vulnId")); Assert.assertEquals("INT-2", json.getJsonObject(1).getString("vulnId")); Assert.assertEquals("INT-3", json.getJsonObject(2).getString("vulnId")); @@ -731,10 +824,13 @@ private class SampleData { final Vulnerability v3; final Vulnerability v4; final Vulnerability v5; + final Vulnerability v6; final VulnerableSoftware vs1; SampleData() { p1 = qm.createProject("Project 1", null, null, null, null, null, true, false); + p1.addAccessTeam(team); + p2 = qm.createProject("Project 2", null, null, null, null, null, true, false); c1 = new Component(); @@ -798,11 +894,18 @@ private class SampleData { v5.setSeverity(Severity.CRITICAL); v5.setDescription("Description 5"); + v6 = new Vulnerability(); + v6.setVulnId("INT-6"); + v6.setSource(Vulnerability.Source.INTERNAL); + v6.setSeverity(Severity.CRITICAL); + v6.setDescription("Description 6"); + qm.createVulnerability(v1, false); qm.createVulnerability(v2, false); qm.createVulnerability(v3, false); qm.createVulnerability(v4, false); qm.createVulnerability(v5, false); + qm.createVulnerability(v6, false); qm.addVulnerability(v1, c1, AnalyzerIdentity.NONE); qm.addVulnerability(v2, c1, AnalyzerIdentity.NONE); qm.addVulnerability(v3, c1, AnalyzerIdentity.NONE); @@ -810,6 +913,8 @@ private class SampleData { qm.addVulnerability(v5, c2, AnalyzerIdentity.NONE); qm.addVulnerability(v4, c3, AnalyzerIdentity.NONE); qm.addVulnerability(v5, c3, AnalyzerIdentity.NONE); + qm.addVulnerability(v6, c1, AnalyzerIdentity.NONE); + qm.addVulnerability(v6, c3, AnalyzerIdentity.NONE); qm.makeAnalysis(c1, v3, AnalysisState.FALSE_POSITIVE, AnalysisJustification.CODE_NOT_REACHABLE, AnalysisResponse.WILL_NOT_FIX, "Analysis details here", true); qm.makeAnalysis(c3, v5, AnalysisState.NOT_AFFECTED, AnalysisJustification.CODE_NOT_REACHABLE, AnalysisResponse.WILL_NOT_FIX, "Analysis details here", true); From 06f44a6014ebc70643ccfd52451dc3cbad5ddbf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Tue, 1 Oct 2024 11:59:45 +0200 Subject: [PATCH 207/429] Added this for getting all vulns too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../resources/v1/VulnerabilityResource.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/VulnerabilityResource.java b/src/main/java/org/dependencytrack/resources/v1/VulnerabilityResource.java index e48c587d97..be5862ab34 100644 --- a/src/main/java/org/dependencytrack/resources/v1/VulnerabilityResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/VulnerabilityResource.java @@ -222,9 +222,9 @@ public Response getVulnerabilityByVulnId(@PathParam("source") String source, } vulnerability.setAffectedComponents(affectedComponents); qm.makeTransient(vulnerability); - Principal principal = super.getPrincipal(); boolean shouldFilter = qm.isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED); if (shouldFilter) { + Principal principal = super.getPrincipal(); vulnerability.setComponents( vulnerability.getComponents().stream().filter(component -> qm.hasAccess(principal, component.getProject())).toList()); @@ -267,9 +267,9 @@ public Response getAffectedProject(@PathParam("source") String source, return Response.ok(filteredProjects).header(TOTAL_COUNT_HEADER, filteredCount).build(); } qm.makeTransient(vulnerability); - Principal principal = super.getPrincipal(); boolean shouldFilter = qm.isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED); if (shouldFilter) { + Principal principal = super.getPrincipal(); vulnerability.setComponents( vulnerability.getComponents().stream().filter(component -> qm.hasAccess(principal, component.getProject())).toList()); @@ -304,6 +304,15 @@ public Response getAffectedProject(@PathParam("source") String source, public Response getAllVulnerabilities() { try (QueryManager qm = new QueryManager(getAlpineRequest())) { final PaginatedResult result = qm.getVulnerabilities(); + boolean shouldFilter = qm.isEnabled(ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED); + if (shouldFilter) { + Principal principal = super.getPrincipal(); + for (final Vulnerability vulnerability : result.getList(Vulnerability.class)) { + vulnerability.setComponents( + vulnerability.getComponents().stream().filter(component -> qm.hasAccess(principal, + component.getProject())).toList()); + } + } return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); } } From 202061945c4747d7027b523282586a8090582dd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Tue, 1 Oct 2024 13:23:58 +0200 Subject: [PATCH 208/429] Fixed old test and added new ones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../v1/VulnerabilityResourceTest.java | 68 +++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/VulnerabilityResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/VulnerabilityResourceTest.java index bb12664832..45e1529d30 100644 --- a/src/test/java/org/dependencytrack/resources/v1/VulnerabilityResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/VulnerabilityResourceTest.java @@ -42,6 +42,7 @@ import jakarta.json.Json; import jakarta.json.JsonArray; +import jakarta.json.JsonValue; import jakarta.json.JsonObject; import jakarta.ws.rs.client.Entity; import jakarta.ws.rs.core.MediaType; @@ -355,10 +356,10 @@ public void getAffectedProjectACLDisabledTest() { JsonArray json = parseJsonArray(response); Assert.assertNotNull(json); Assert.assertEquals(2, json.size()); - Assert.assertEquals("Project 2", json.getJsonObject(0).getString("name")); - Assert.assertEquals(sampleData.p2.getUuid().toString(), json.getJsonObject(0).getString("uuid")); - Assert.assertEquals("Project 1", json.getJsonObject(1).getString("name")); - Assert.assertEquals(sampleData.p1.getUuid().toString(), json.getJsonObject(1).getString("uuid")); + Assert.assertEquals("Project 1", json.getJsonObject(0).getString("name")); + Assert.assertEquals(sampleData.p1.getUuid().toString(), json.getJsonObject(0).getString("uuid")); + Assert.assertEquals("Project 2", json.getJsonObject(1).getString("name")); + Assert.assertEquals(sampleData.p2.getUuid().toString(), json.getJsonObject(1).getString("uuid")); } @Test @@ -377,8 +378,67 @@ public void getAllVulnerabilitiesTest() { Assert.assertEquals("INT-3", json.getJsonObject(2).getString("vulnId")); Assert.assertEquals("INT-4", json.getJsonObject(3).getString("vulnId")); Assert.assertEquals("INT-5", json.getJsonObject(4).getString("vulnId")); + Assert.assertEquals("INT-6", json.getJsonObject(5).getString("vulnId")); } + @Test + public void getAllVulnerabilitiesACLEnabledTest() { + new SampleData(); + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "true", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + Response response = jersey.target(V1_VULNERABILITY).request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals(String.valueOf(6), response.getHeaderString(TOTAL_COUNT_HEADER)); + JsonArray jsons = parseJsonArray(response); + Assert.assertNotNull(jsons); + Assert.assertEquals(6, jsons.size()); + for (JsonValue json : jsons) { + JsonArray components = json.asJsonObject().getJsonArray("components"); + Assert.assertNotNull(components); + Assert.assertEquals(1, components.size()); + } + } + + @Test + public void getAllVulnerabilitiesACLDisabledTest() { + new SampleData(); + qm.createConfigProperty( + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getGroupName(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyName(), + "false", + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getPropertyType(), + ConfigPropertyConstants.ACCESS_MANAGEMENT_ACL_ENABLED.getDescription()); + Response response = jersey.target(V1_VULNERABILITY).request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals(String.valueOf(6), response.getHeaderString(TOTAL_COUNT_HEADER)); + JsonArray jsons = parseJsonArray(response); + Assert.assertNotNull(jsons); + Assert.assertEquals(6, jsons.size()); + for (int i = 0; i < 3; i++) { + JsonArray components = jsons.get(i).asJsonObject().getJsonArray("components"); + Assert.assertNotNull(components); + Assert.assertEquals(1, components.size()); + } + JsonArray components = jsons.get(3).asJsonObject().getJsonArray("components"); + Assert.assertNotNull(components); + Assert.assertEquals(2, components.size()); + components = jsons.get(4).asJsonObject().getJsonArray("components"); + Assert.assertNotNull(components); + Assert.assertEquals(2, components.size()); + components = jsons.get(5).asJsonObject().getJsonArray("components"); + Assert.assertNotNull(components); + Assert.assertEquals(2, components.size()); + } + + @Test public void createVulnerabilityTest() { JsonObject payload = Json.createObjectBuilder() From c85bf47e68b466da504cfc1209d10b467e160c48 Mon Sep 17 00:00:00 2001 From: nscuro Date: Tue, 1 Oct 2024 15:04:02 +0200 Subject: [PATCH 209/429] Fix redundant `ConfigProperty` queries in `BadgeResource` If unauthenticated badge access is enabled, the same `ConfigProperty` was previously queried three times. Since badges are embedded in public places, it'd be beneficial if the respective resources avoided unnecessary work. Signed-off-by: nscuro --- .../resources/v1/BadgeResource.java | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java b/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java index 6ecc04d254..1284713d3c 100644 --- a/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/BadgeResource.java @@ -19,9 +19,7 @@ package org.dependencytrack.resources.v1; import alpine.common.logging.Logger; -import alpine.common.util.BooleanUtil; import alpine.model.ApiKey; -import alpine.model.ConfigProperty; import alpine.model.UserPrincipal; import alpine.model.LdapUser; import alpine.model.ManagedUser; @@ -80,12 +78,6 @@ public class BadgeResource extends AlpineResource { private final Logger LOGGER = Logger.getLogger(AuthenticationFilter.class); - private boolean isUnauthenticatedBadgeAccessEnabled(final QueryManager qm) { - ConfigProperty property = qm.getConfigProperty( - GENERAL_BADGE_ENABLED.getGroupName(), GENERAL_BADGE_ENABLED.getPropertyName()); - return BooleanUtil.valueOf(property.getPropertyValue()); - } - // Stand-in methods for alpine.server.filters.AuthenticationFilter and // alpine.server.filters.AuthorizationFilter to allow enabling and disabling of // unauthenticated access to the badges API during runtime, used solely to offer @@ -191,15 +183,16 @@ public Response getProjectVulnerabilitiesBadge( @Parameter(description = "The UUID of the project to retrieve metrics for", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { - if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthentication()) { + final boolean shouldBypassAuth = qm.isEnabled(GENERAL_BADGE_ENABLED); + if (!shouldBypassAuth && !passesAuthentication()) { return Response.status(Response.Status.UNAUTHORIZED).build(); } - if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthorization(qm)) { + if (!shouldBypassAuth && !passesAuthorization(qm)) { return Response.status(Response.Status.FORBIDDEN).build(); } final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { - if (!isUnauthenticatedBadgeAccessEnabled(qm) && !qm.hasAccess(super.getPrincipal(), project)) { + if (!shouldBypassAuth && !qm.hasAccess(super.getPrincipal(), project)) { return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); @@ -235,15 +228,16 @@ public Response getProjectVulnerabilitiesBadge( @Parameter(description = "The version of the project to query on", required = true) @PathParam("version") String version) { try (QueryManager qm = new QueryManager()) { - if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthentication()) { + final boolean shouldBypassAuth = qm.isEnabled(GENERAL_BADGE_ENABLED); + if (!shouldBypassAuth && !passesAuthentication()) { return Response.status(Response.Status.UNAUTHORIZED).build(); } - if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthorization(qm)) { + if (!shouldBypassAuth && !passesAuthorization(qm)) { return Response.status(Response.Status.FORBIDDEN).build(); } final Project project = qm.getProject(name, version); if (project != null) { - if (!isUnauthenticatedBadgeAccessEnabled(qm) && !qm.hasAccess(super.getPrincipal(), project)) { + if (!shouldBypassAuth && !qm.hasAccess(super.getPrincipal(), project)) { return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); @@ -277,15 +271,16 @@ public Response getProjectPolicyViolationsBadge( @Parameter(description = "The UUID of the project to retrieve a badge for", schema = @Schema(type = "string", format = "uuid"), required = true) @PathParam("uuid") @ValidUuid String uuid) { try (QueryManager qm = new QueryManager()) { - if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthentication()) { + final boolean shouldBypassAuth = qm.isEnabled(GENERAL_BADGE_ENABLED); + if (!shouldBypassAuth && !passesAuthentication()) { return Response.status(Response.Status.UNAUTHORIZED).build(); } - if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthorization(qm)) { + if (!shouldBypassAuth && !passesAuthorization(qm)) { return Response.status(Response.Status.FORBIDDEN).build(); } final Project project = qm.getObjectByUuid(Project.class, uuid); if (project != null) { - if (!isUnauthenticatedBadgeAccessEnabled(qm) && !qm.hasAccess(super.getPrincipal(), project)) { + if (!shouldBypassAuth && !qm.hasAccess(super.getPrincipal(), project)) { return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); @@ -321,15 +316,16 @@ public Response getProjectPolicyViolationsBadge( @Parameter(description = "The version of the project to query on", required = true) @PathParam("version") String version) { try (QueryManager qm = new QueryManager()) { - if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthentication()) { + final boolean shouldBypassAuth = qm.isEnabled(GENERAL_BADGE_ENABLED); + if (!shouldBypassAuth && !passesAuthentication()) { return Response.status(Response.Status.UNAUTHORIZED).build(); } - if (!isUnauthenticatedBadgeAccessEnabled(qm) && !passesAuthorization(qm)) { + if (!shouldBypassAuth && !passesAuthorization(qm)) { return Response.status(Response.Status.FORBIDDEN).build(); } final Project project = qm.getProject(name, version); if (project != null) { - if (!isUnauthenticatedBadgeAccessEnabled(qm) && !qm.hasAccess(super.getPrincipal(), project)) { + if (!shouldBypassAuth && !qm.hasAccess(super.getPrincipal(), project)) { return Response.status(Response.Status.FORBIDDEN).entity("Access to the specified project is forbidden").build(); } final ProjectMetrics metrics = qm.getMostRecentProjectMetrics(project); From b4ec66426f46b5e4c3dd6f98fc450f4c5c01572c Mon Sep 17 00:00:00 2001 From: nscuro Date: Tue, 1 Oct 2024 15:09:59 +0200 Subject: [PATCH 210/429] Fix potential race condition in `PolicyEngineTest#notificationTest` Since notification dispatch happens asynchronously, it's possible that the assertion happens before the notification is received. Await notifications with a small timeout instead. Signed-off-by: nscuro --- .../policy/PolicyEngineTest.java | 55 ++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java b/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java index 75cb8a068f..71f30b06c4 100644 --- a/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java +++ b/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java @@ -51,6 +51,7 @@ import org.junit.jupiter.api.Assertions; import java.sql.Date; +import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Collections; @@ -59,6 +60,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; public class PolicyEngineTest extends PersistenceCapableTest { @@ -394,32 +396,33 @@ public void notificationTest() { // and re-evaluate policies. Ensure that only one notification per newly violated condition was sent. final var policyConditionB = qm.createPolicyCondition(policy, Subject.VERSION, PolicyCondition.Operator.NUMERIC_EQUAL, "1.2.3"); assertThat(policyEngine.evaluate(List.of(component))).hasSize(2); - assertThat(NOTIFICATIONS).satisfiesExactly( - notification -> { - assertThat(notification.getScope()).isEqualTo(NotificationScope.PORTFOLIO.name()); - assertThat(notification.getGroup()).isEqualTo(NotificationGroup.PROJECT_CREATED.name()); - }, - notification -> { - assertThat(notification.getScope()).isEqualTo(NotificationScope.PORTFOLIO.name()); - assertThat(notification.getGroup()).isEqualTo(NotificationGroup.POLICY_VIOLATION.name()); - assertThat(notification.getLevel()).isEqualTo(NotificationLevel.INFORMATIONAL); - assertThat(notification.getSubject()).isInstanceOf(PolicyViolationIdentified.class); - final var subject = (PolicyViolationIdentified) notification.getSubject(); - assertThat(subject.getComponent().getUuid()).isEqualTo(component.getUuid()); - assertThat(subject.getProject().getUuid()).isEqualTo(project.getUuid()); - assertThat(subject.getPolicyViolation().getPolicyCondition().getUuid()).isEqualTo(policyConditionA.getUuid()); - }, - notification -> { - assertThat(notification.getScope()).isEqualTo(NotificationScope.PORTFOLIO.name()); - assertThat(notification.getGroup()).isEqualTo(NotificationGroup.POLICY_VIOLATION.name()); - assertThat(notification.getLevel()).isEqualTo(NotificationLevel.INFORMATIONAL); - assertThat(notification.getSubject()).isInstanceOf(PolicyViolationIdentified.class); - final var subject = (PolicyViolationIdentified) notification.getSubject(); - assertThat(subject.getComponent().getUuid()).isEqualTo(component.getUuid()); - assertThat(subject.getProject().getUuid()).isEqualTo(project.getUuid()); - assertThat(subject.getPolicyViolation().getPolicyCondition().getUuid()).isEqualTo(policyConditionB.getUuid()); - } - ); + await("Notifications") + .atMost(Duration.ofSeconds(3)) + .untilAsserted(() -> assertThat(NOTIFICATIONS).satisfiesExactly( + notification -> { + assertThat(notification.getScope()).isEqualTo(NotificationScope.PORTFOLIO.name()); + assertThat(notification.getGroup()).isEqualTo(NotificationGroup.PROJECT_CREATED.name()); + }, + notification -> { + assertThat(notification.getScope()).isEqualTo(NotificationScope.PORTFOLIO.name()); + assertThat(notification.getGroup()).isEqualTo(NotificationGroup.POLICY_VIOLATION.name()); + assertThat(notification.getLevel()).isEqualTo(NotificationLevel.INFORMATIONAL); + assertThat(notification.getSubject()).isInstanceOf(PolicyViolationIdentified.class); + final var subject = (PolicyViolationIdentified) notification.getSubject(); + assertThat(subject.getComponent().getUuid()).isEqualTo(component.getUuid()); + assertThat(subject.getProject().getUuid()).isEqualTo(project.getUuid()); + assertThat(subject.getPolicyViolation().getPolicyCondition().getUuid()).isEqualTo(policyConditionA.getUuid()); + }, + notification -> { + assertThat(notification.getScope()).isEqualTo(NotificationScope.PORTFOLIO.name()); + assertThat(notification.getGroup()).isEqualTo(NotificationGroup.POLICY_VIOLATION.name()); + assertThat(notification.getLevel()).isEqualTo(NotificationLevel.INFORMATIONAL); + assertThat(notification.getSubject()).isInstanceOf(PolicyViolationIdentified.class); + final var subject = (PolicyViolationIdentified) notification.getSubject(); + assertThat(subject.getComponent().getUuid()).isEqualTo(component.getUuid()); + assertThat(subject.getProject().getUuid()).isEqualTo(project.getUuid()); + assertThat(subject.getPolicyViolation().getPolicyCondition().getUuid()).isEqualTo(policyConditionB.getUuid()); + })); // Delete a policy condition and re-evaluate policies again. No new notifications should be sent. qm.deletePolicyCondition(policyConditionA); From 97058c5bd224b163a05000cc2cb6ec31a6fb050e Mon Sep 17 00:00:00 2001 From: nscuro Date: Tue, 1 Oct 2024 15:23:37 +0200 Subject: [PATCH 211/429] Bump mysql-connector-j to 8.2.0 Addresses CVE-2023-22102. Note that 8.2.0 is the highest possible version that still supports MySQL 5.7. We cannot upgrade further without investing more time into ensuring compatibility with MySQL 8. Signed-off-by: nscuro --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 317db4df13..62bbecce54 100644 --- a/pom.xml +++ b/pom.xml @@ -126,7 +126,7 @@ 1.323 12.8.1.jre11 - 8.0.33 + 8.2.0 42.7.4 false From b401770eb4ad9c91ef6fa08f66dbcd48a80210cb Mon Sep 17 00:00:00 2001 From: nscuro Date: Tue, 1 Oct 2024 16:07:39 +0200 Subject: [PATCH 212/429] Fix `getAffectedProjectACLDisabledTest` flakiness The projects array returned by this endpoint is not guaranteed to have consistent ordering, causing occasional test failures. Example test failure: https://github.com/DependencyTrack/dependency-track/actions/runs/11126300521/job/30915909435?pr=4204 Signed-off-by: nscuro --- .../v1/VulnerabilityResourceTest.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/dependencytrack/resources/v1/VulnerabilityResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/VulnerabilityResourceTest.java index 45e1529d30..87f2c24cc9 100644 --- a/src/test/java/org/dependencytrack/resources/v1/VulnerabilityResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/VulnerabilityResourceTest.java @@ -19,7 +19,6 @@ package org.dependencytrack.resources.v1; import alpine.common.util.UuidUtil; -import alpine.model.Team; import alpine.server.filters.ApiFilter; import alpine.server.filters.AuthenticationFilter; import org.dependencytrack.JerseyTestRule; @@ -42,14 +41,16 @@ import jakarta.json.Json; import jakarta.json.JsonArray; -import jakarta.json.JsonValue; import jakarta.json.JsonObject; +import jakarta.json.JsonValue; import jakarta.ws.rs.client.Entity; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.util.List; import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; + public class VulnerabilityResourceTest extends ResourceTest { @ClassRule @@ -356,10 +357,17 @@ public void getAffectedProjectACLDisabledTest() { JsonArray json = parseJsonArray(response); Assert.assertNotNull(json); Assert.assertEquals(2, json.size()); - Assert.assertEquals("Project 1", json.getJsonObject(0).getString("name")); - Assert.assertEquals(sampleData.p1.getUuid().toString(), json.getJsonObject(0).getString("uuid")); - Assert.assertEquals("Project 2", json.getJsonObject(1).getString("name")); - Assert.assertEquals(sampleData.p2.getUuid().toString(), json.getJsonObject(1).getString("uuid")); + assertThat(json).satisfiesExactlyInAnyOrder( + jsonValue -> { + final JsonObject projectObject = jsonValue.asJsonObject(); + assertThat(projectObject.getString("name")).isEqualTo("Project 1"); + assertThat(projectObject.getString("uuid")).isEqualTo(sampleData.p1.getUuid().toString()); + }, + jsonValue -> { + final JsonObject projectObject = jsonValue.asJsonObject(); + assertThat(projectObject.getString("name")).isEqualTo("Project 2"); + assertThat(projectObject.getString("uuid")).isEqualTo(sampleData.p2.getUuid().toString()); + }); } @Test From f316ec94bdc400f3fdb8a544d7d0e98299c34282 Mon Sep 17 00:00:00 2001 From: nscuro Date: Tue, 1 Oct 2024 16:45:07 +0200 Subject: [PATCH 213/429] Work around ghcr.io rate limiting for Trivy database downloads See: * https://github.com/aquasecurity/trivy-action/issues/389 * https://github.com/orgs/community/discussions/139074 Signed-off-by: nscuro --- .github/workflows/_meta-build.yaml | 4 ++++ .../tasks/scanners/TrivyAnalysisTaskIntegrationTest.java | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 008a21d7c1..890f76be5e 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -136,6 +136,10 @@ jobs: - name: Run Trivy Vulnerability Scanner if: ${{ inputs.publish-container }} uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # tag=0.24.0 + env: + # https://github.com/aquasecurity/trivy-action/issues/389 + TRIVY_DB_REPOSITORY: "public.ecr.aws/aquasecurity/trivy-db:2" + TRIVY_JAVA_DB_REPOSITORY: "public.ecr.aws/aquasecurity/trivy-java-db:1" with: image-ref: docker.io/dependencytrack/${{ matrix.distribution }}:${{ inputs.app-version }} format: 'sarif' diff --git a/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskIntegrationTest.java b/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskIntegrationTest.java index 50bfb51832..160c709931 100644 --- a/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskIntegrationTest.java +++ b/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskIntegrationTest.java @@ -93,7 +93,10 @@ public void before() throws Exception { .withExposedPorts(8080) .withCreateContainerCmdModifier(cmd -> cmd.getHostConfig() .withBinds(Bind.parse("%s:/tmp/cache".formatted(trivyCacheVolumeName)))) - .waitingFor(forLogMessage(".*Listening :8080.*", 1)); + .waitingFor(forLogMessage(".*Listening :8080.*", 1)) + // https://github.com/aquasecurity/trivy-action/issues/389 + .withEnv("TRIVY_DB_REPOSITORY", "public.ecr.aws/aquasecurity/trivy-db:2") + .withEnv("TRIVY_JAVA_DB_REPOSITORY", "public.ecr.aws/aquasecurity/trivy-java-db:1"); trivyContainer.start(); qm.createConfigProperty( From 7c2db97ec97c70caba0978ab545578064bc65f50 Mon Sep 17 00:00:00 2001 From: nscuro Date: Tue, 1 Oct 2024 21:25:37 +0200 Subject: [PATCH 214/429] Fix `affectedComponents` getting removed when updating an internal vulnerability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attributions were not correctly added for `VulnerableSoftware` records when creating or updating internal vulnerabilities via REST API. Re-using the `synchronizeVulnerableSoftware` logic from NVD mirroring tasks fixes the issue. Co-authored-by: Thomas Schauer-Köckeis Signed-off-by: nscuro --- .../persistence/QueryManager.java | 21 ++ .../VulnerableSoftwareQueryManager.java | 170 +++++++++++++++ .../resources/v1/VulnerabilityResource.java | 35 +-- .../tasks/AbstractNistMirrorTask.java | 127 ----------- .../tasks/NistApiMirrorTask.java | 2 +- .../dependencytrack/tasks/NistMirrorTask.java | 2 +- .../v1/VulnerabilityResourceTest.java | 200 ++++++++++++++++++ 7 files changed, 415 insertions(+), 142 deletions(-) diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index 75ee9f26a5..01c73ebcf2 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -915,6 +915,13 @@ public boolean hasAffectedVersionAttribution(final Vulnerability vulnerability, return getVulnerabilityQueryManager().hasAffectedVersionAttribution(vulnerability, vulnerableSoftware, source); } + public void synchronizeVulnerableSoftware( + final Vulnerability persistentVuln, + final List vsList, + final Vulnerability.Source source) { + getVulnerableSoftwareQueryManager().synchronizeVulnerableSoftware(persistentVuln, vsList, source); + } + public boolean contains(Vulnerability vulnerability, Component component) { return getVulnerabilityQueryManager().contains(vulnerability, component); } @@ -939,6 +946,20 @@ public VulnerableSoftware getVulnerableSoftwareByPurl(String purlType, String pu return getVulnerableSoftwareQueryManager().getVulnerableSoftwareByPurl(purlType, purlNamespace, purlName, versionEndExcluding, versionEndIncluding, versionStartExcluding, versionStartIncluding); } + public VulnerableSoftware getVulnerableSoftwareByPurl( + final String purl, + final String versionEndExcluding, + final String versionEndIncluding, + final String versionStartExcluding, + final String versionStartIncluding) { + return getVulnerableSoftwareQueryManager().getVulnerableSoftwareByPurl( + purl, + versionEndExcluding, + versionEndIncluding, + versionStartExcluding, + versionStartIncluding); + } + public List getVulnerableSoftwareByVulnId(final String source, final String vulnId) { return getVulnerableSoftwareQueryManager().getVulnerableSoftwareByVulnId(source, vulnId); } diff --git a/src/main/java/org/dependencytrack/persistence/VulnerableSoftwareQueryManager.java b/src/main/java/org/dependencytrack/persistence/VulnerableSoftwareQueryManager.java index 3142bf46d9..1b160000e5 100644 --- a/src/main/java/org/dependencytrack/persistence/VulnerableSoftwareQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/VulnerableSoftwareQueryManager.java @@ -18,22 +18,32 @@ */ package org.dependencytrack.persistence; +import alpine.common.logging.Logger; import alpine.persistence.PaginatedResult; import alpine.resources.AlpineRequest; import com.github.packageurl.PackageURL; +import org.dependencytrack.model.AffectedVersionAttribution; import org.dependencytrack.model.Vulnerability; import org.dependencytrack.model.VulnerableSoftware; +import org.dependencytrack.util.PersistenceUtil; import javax.jdo.PersistenceManager; import javax.jdo.Query; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import static java.util.stream.Collectors.groupingBy; +import static org.dependencytrack.util.PersistenceUtil.assertNonPersistentAll; +import static org.dependencytrack.util.PersistenceUtil.assertPersistent; + final class VulnerableSoftwareQueryManager extends QueryManager implements IQueryManager { + private static final Logger LOGGER = Logger.getLogger(VulnerableSoftwareQueryManager.class); + /** * Constructs a new QueryManager. * @param pm a PersistenceManager object @@ -143,6 +153,27 @@ public VulnerableSoftware getVulnerableSoftwareByPurl(String purlType, String pu return singleResult(query.executeWithArray(purlType, purlNamespace, purlName, versionEndExcluding, versionEndIncluding, versionStartExcluding, versionStartIncluding)); } + /** + * @since 4.12.0 + */ + public VulnerableSoftware getVulnerableSoftwareByPurl( + final String purl, + final String versionEndExcluding, + final String versionEndIncluding, + final String versionStartExcluding, + final String versionStartIncluding) { + final Query query = pm.newQuery(VulnerableSoftware.class); + query.setFilter(""" + purl == :purl \ + && versionEndExcluding == :versionEndExcluding \ + && versionEndIncluding == :versionEndIncluding \ + && versionStartExcluding == :versionStartExcluding \ + && versionStartIncluding == :versionStartIncluding"""); + query.setParameters(purl, versionEndExcluding, versionEndIncluding, versionStartExcluding, versionStartIncluding); + query.setRange(0, 1); + return executeAndCloseUnique(query); + } + /** * Fetch all {@link VulnerableSoftware} instances associated with a given {@link Vulnerability}. * @@ -316,4 +347,143 @@ public List getAllVulnerableSoftware(final String cpePart, f } } + /** + * @since 4.12.0 + */ + public void synchronizeVulnerableSoftware( + final Vulnerability persistentVuln, + final List vsList, + final Vulnerability.Source source) { + assertPersistent(persistentVuln, "vuln must be persistent"); + assertNonPersistentAll(vsList, "vsList must not be persistent"); + + runInTransaction(() -> { + // Get all VulnerableSoftware records that are currently associated with the vulnerability. + // Note: For SOME ODD REASON, duplicate (as in, same database ID and all) VulnerableSoftware + // records are returned, when operating on data that was originally created by the feed-based + // NistMirrorTask. We thus have to deduplicate here. + final List vsOldList = persistentVuln.getVulnerableSoftware().stream().distinct().toList(); + LOGGER.trace("%s: Existing VS: %d".formatted(persistentVuln.getVulnId(), vsOldList.size())); + + // Get attributions for all existing VulnerableSoftware records. + final Map> attributionsByVsId = + getAffectedVersionAttributions(persistentVuln, vsOldList).stream() + .collect(groupingBy(attribution -> attribution.getVulnerableSoftware().getId())); + for (final VulnerableSoftware vsOld : vsOldList) { + vsOld.setAffectedVersionAttributions(attributionsByVsId.get(vsOld.getId())); + } + + // Based on the lists of currently reported, and previously reported VulnerableSoftware records, + // divide the previously reported ones into lists of records to keep, and records to remove. + // Records to keep are removed from vsList. Remaining records in vsList thus are entirely new. + final var vsListToRemove = new ArrayList(); + final var vsListToKeep = new ArrayList(); + for (final VulnerableSoftware vsOld : vsOldList) { + if (vsList.removeIf(vsOld::equalsIgnoringDatastoreIdentity)) { + vsListToKeep.add(vsOld); + } else { + final List attributions = vsOld.getAffectedVersionAttributions(); + if (attributions == null || attributions.isEmpty()) { + // DT versions prior to 4.7.0 did not record attributions. + // Drop the VulnerableSoftware for now. If it was previously + // reported by another source, it will be recorded and attributed + // whenever that source is mirrored again. + vsListToRemove.add(vsOld); + continue; + } + + final boolean previouslyReportedBySource = attributions.stream() + .anyMatch(attr -> attr.getSource() == source); + final boolean previouslyReportedByOthers = !previouslyReportedBySource; + + if (previouslyReportedByOthers) { + vsListToKeep.add(vsOld); + } else { + vsListToRemove.add(vsOld); + } + } + } + LOGGER.trace("%s: vsListToKeep: %d".formatted(persistentVuln.getVulnId(), vsListToKeep.size())); + LOGGER.trace("%s: vsListToRemove: %d".formatted(persistentVuln.getVulnId(), vsListToRemove.size())); + + // Remove attributions for VulnerableSoftware records that are no longer reported. + if (!vsListToRemove.isEmpty()) { + deleteAffectedVersionAttributions(persistentVuln, vsListToRemove, source); + } + + final var attributionDate = new Date(); + + // For VulnerableSoftware records that existed before, update the lastSeen timestamp. + for (final VulnerableSoftware oldVs : vsListToKeep) { + oldVs.getAffectedVersionAttributions().stream() + .filter(attribution -> attribution.getSource() == source) + .findAny() + .ifPresent(attribution -> attribution.setLastSeen(attributionDate)); + } + + // For VulnerableSoftware records that are newly reported for this vulnerability, check if any matching + // records exist in the database that are currently associated with other (or no) vulnerabilities. + for (final VulnerableSoftware vs : vsList) { + final VulnerableSoftware existingVs; + if (vs.getCpe23() != null) { + existingVs = getVulnerableSoftwareByCpe23( + vs.getCpe23(), + vs.getVersionEndExcluding(), + vs.getVersionEndIncluding(), + vs.getVersionStartExcluding(), + vs.getVersionStartIncluding()); + } else if (vs.getPurl() != null) { + existingVs = getVulnerableSoftwareByPurl( + vs.getPurl(), + vs.getVersionEndExcluding(), + vs.getVersionEndIncluding(), + vs.getVersionStartExcluding(), + vs.getVersionStartIncluding()); + } else { + throw new IllegalStateException("VulnerableSoftware must define a CPE or PURL, but %s has neither".formatted(vs)); + } + if (existingVs != null) { + final boolean hasAttribution = hasAffectedVersionAttribution(persistentVuln, existingVs, source); + if (!hasAttribution) { + LOGGER.trace("%s: Adding attribution".formatted(persistentVuln.getVulnId())); + final AffectedVersionAttribution attribution = createAttribution(persistentVuln, existingVs, attributionDate, source); + persist(attribution); + } else { + LOGGER.debug("%s: Encountered dangling attribution; Re-using by updating firstSeen and lastSeen timestamps".formatted(persistentVuln.getVulnId())); + final AffectedVersionAttribution existingAttribution = getAffectedVersionAttribution(persistentVuln, existingVs, source); + existingAttribution.setFirstSeen(attributionDate); + existingAttribution.setLastSeen(attributionDate); + } + vsListToKeep.add(existingVs); + } else { + LOGGER.trace("%s: Creating new VS".formatted(persistentVuln.getVulnId())); + final VulnerableSoftware persistentVs = persist(vs); + final AffectedVersionAttribution attribution = createAttribution(persistentVuln, persistentVs, attributionDate, source); + persist(attribution); + vsListToKeep.add(persistentVs); + } + } + + LOGGER.trace("%s: Final vsList: %d".formatted(persistentVuln.getVulnId(), vsListToKeep.size())); + if (!Objects.equals(persistentVuln.getVulnerableSoftware(), vsListToKeep)) { + LOGGER.trace("%s: vsList has changed: %s".formatted(persistentVuln.getVulnId(), new PersistenceUtil.Diff(persistentVuln.getVulnerableSoftware(), vsListToKeep))); + persistentVuln.setVulnerableSoftware(vsListToKeep); + } + }); + } + + private static AffectedVersionAttribution createAttribution( + final Vulnerability vuln, + final VulnerableSoftware vs, + final Date attributionDate, + final Vulnerability.Source source) { + final var attribution = new AffectedVersionAttribution(); + attribution.setSource(source); + attribution.setVulnerability(vuln); + attribution.setVulnerableSoftware(vs); + attribution.setFirstSeen(attributionDate); + attribution.setLastSeen(attributionDate); + return attribution; + } + } diff --git a/src/main/java/org/dependencytrack/resources/v1/VulnerabilityResource.java b/src/main/java/org/dependencytrack/resources/v1/VulnerabilityResource.java index be5862ab34..7efa798415 100644 --- a/src/main/java/org/dependencytrack/resources/v1/VulnerabilityResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/VulnerabilityResource.java @@ -379,12 +379,17 @@ public Response createVulnerability(Vulnerability jsonVulnerability) { } recalculateScoresAndSeverityFromVectors(jsonVulnerability); jsonVulnerability.setSource(Vulnerability.Source.INTERNAL); - vulnerability = qm.createVulnerability(jsonVulnerability, true); - qm.persist(vsList); - qm.updateAffectedVersionAttributions(vulnerability, vsList, Vulnerability.Source.INTERNAL); - vulnerability.setVulnerableSoftware(vsList); - qm.persist(vulnerability); - return Response.status(Response.Status.CREATED).entity(vulnerability).build(); + return qm.callInTransaction(() -> { + final Vulnerability persistentVuln = qm.createVulnerability(jsonVulnerability, true); + qm.synchronizeVulnerableSoftware(persistentVuln, vsList, Vulnerability.Source.INTERNAL); + if (persistentVuln.getVulnerableSoftware() != null && !persistentVuln.getVulnerableSoftware().isEmpty()) { + persistentVuln.setAffectedComponents(persistentVuln.getVulnerableSoftware().stream() + .peek(vs -> vs.setAffectedVersionAttributions(qm.getAffectedVersionAttributions(persistentVuln, vs))) + .map(AffectedComponent::new) + .toList()); + } + return Response.status(Response.Status.CREATED).entity(persistentVuln).build(); + }); } else { return Response.status(Response.Status.CONFLICT).entity("A vulnerability with the specified vulnId already exists.").build(); } @@ -447,7 +452,6 @@ public Response updateVulnerability(Vulnerability jsonVuln) { jsonVuln.setCwes(cweIds); } - final List vsListOld = qm.getVulnerableSoftwareByVulnId(vulnerability.getSource(), vulnerability.getVulnId()); List vsList = new ArrayList<>(); if (jsonVuln.getAffectedComponents() != null) { for (final AffectedComponent ac : jsonVuln.getAffectedComponents()) { @@ -458,12 +462,17 @@ public Response updateVulnerability(Vulnerability jsonVuln) { } } recalculateScoresAndSeverityFromVectors(jsonVuln); - vulnerability = qm.updateVulnerability(jsonVuln, true); - qm.persist(vsList); - vsList = qm.reconcileVulnerableSoftware(vulnerability, vsListOld, vsList, Vulnerability.Source.INTERNAL); - vulnerability.setVulnerableSoftware(vsList); - qm.persist(vulnerability); - return Response.ok(vulnerability).build(); + return qm.callInTransaction(() -> { + final Vulnerability persistentVuln = qm.updateVulnerability(jsonVuln, true); + qm.synchronizeVulnerableSoftware(persistentVuln, vsList, Vulnerability.Source.INTERNAL); + if (persistentVuln.getVulnerableSoftware() != null && !persistentVuln.getVulnerableSoftware().isEmpty()) { + persistentVuln.setAffectedComponents(persistentVuln.getVulnerableSoftware().stream() + .peek(vs -> vs.setAffectedVersionAttributions(qm.getAffectedVersionAttributions(persistentVuln, vs))) + .map(AffectedComponent::new) + .toList()); + } + return Response.ok(persistentVuln).build(); + }); } else { return Response.status(Response.Status.NOT_FOUND).entity("The vulnerability could not be found.").build(); } diff --git a/src/main/java/org/dependencytrack/tasks/AbstractNistMirrorTask.java b/src/main/java/org/dependencytrack/tasks/AbstractNistMirrorTask.java index c5d0763fe2..c84246bfdc 100644 --- a/src/main/java/org/dependencytrack/tasks/AbstractNistMirrorTask.java +++ b/src/main/java/org/dependencytrack/tasks/AbstractNistMirrorTask.java @@ -20,21 +20,13 @@ import alpine.common.logging.Logger; import alpine.persistence.Transaction; -import org.dependencytrack.model.AffectedVersionAttribution; import org.dependencytrack.model.Vulnerability; -import org.dependencytrack.model.VulnerableSoftware; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.util.PersistenceUtil; import javax.jdo.Query; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; import java.util.Map; -import java.util.Objects; -import static java.util.stream.Collectors.groupingBy; -import static org.dependencytrack.util.PersistenceUtil.assertNonPersistentAll; import static org.dependencytrack.util.PersistenceUtil.assertPersistent; /** @@ -65,125 +57,6 @@ Vulnerability synchronizeVulnerability(final QueryManager qm, final Vulnerabilit }); } - void synchronizeVulnerableSoftware(final QueryManager qm, final Vulnerability persistentVuln, final List vsList) { - assertPersistent(persistentVuln, "vuln must be persistent"); - assertNonPersistentAll(vsList, "vsList must not be persistent"); - - qm.runInTransaction(() -> { - // Get all VulnerableSoftware records that are currently associated with the vulnerability. - // Note: For SOME ODD REASON, duplicate (as in, same database ID and all) VulnerableSoftware - // records are returned, when operating on data that was originally created by the feed-based - // NistMirrorTask. We thus have to deduplicate here. - final List vsOldList = persistentVuln.getVulnerableSoftware().stream().distinct().toList(); - logger.trace("%s: Existing VS: %d".formatted(persistentVuln.getVulnId(), vsOldList.size())); - - // Get attributions for all existing VulnerableSoftware records. - final Map> attributionsByVsId = - qm.getAffectedVersionAttributions(persistentVuln, vsOldList).stream() - .collect(groupingBy(attribution -> attribution.getVulnerableSoftware().getId())); - for (final VulnerableSoftware vsOld : vsOldList) { - vsOld.setAffectedVersionAttributions(attributionsByVsId.get(vsOld.getId())); - } - - // Based on the lists of currently reported, and previously reported VulnerableSoftware records, - // divide the previously reported ones into lists of records to keep, and records to remove. - // Records to keep are removed from vsList. Remaining records in vsList thus are entirely new. - final var vsListToRemove = new ArrayList(); - final var vsListToKeep = new ArrayList(); - for (final VulnerableSoftware vsOld : vsOldList) { - if (vsList.removeIf(vsOld::equalsIgnoringDatastoreIdentity)) { - vsListToKeep.add(vsOld); - } else { - final List attributions = vsOld.getAffectedVersionAttributions(); - if (attributions == null || attributions.isEmpty()) { - // DT versions prior to 4.7.0 did not record attributions. - // Drop the VulnerableSoftware for now. If it was previously - // reported by another source, it will be recorded and attributed - // whenever that source is mirrored again. - vsListToRemove.add(vsOld); - continue; - } - - final boolean previouslyReportedByNvd = attributions.stream() - .anyMatch(attr -> attr.getSource() == Vulnerability.Source.NVD); - final boolean previouslyReportedByOthers = !previouslyReportedByNvd; - - if (previouslyReportedByOthers) { - vsListToKeep.add(vsOld); - } else { - vsListToRemove.add(vsOld); - } - } - } - logger.trace("%s: vsListToKeep: %d".formatted(persistentVuln.getVulnId(), vsListToKeep.size())); - logger.trace("%s: vsListToRemove: %d".formatted(persistentVuln.getVulnId(), vsListToRemove.size())); - - // Remove attributions for VulnerableSoftware records that are no longer reported. - if (!vsListToRemove.isEmpty()) { - qm.deleteAffectedVersionAttributions(persistentVuln, vsListToRemove, Vulnerability.Source.NVD); - } - - final var attributionDate = new Date(); - - // For VulnerableSoftware records that existed before, update the lastSeen timestamp. - for (final VulnerableSoftware oldVs : vsListToKeep) { - oldVs.getAffectedVersionAttributions().stream() - .filter(attribution -> attribution.getSource() == Vulnerability.Source.NVD) - .findAny() - .ifPresent(attribution -> attribution.setLastSeen(attributionDate)); - } - - // For VulnerableSoftware records that are newly reported for this vulnerability, check if any matching - // records exist in the database that are currently associated with other (or no) vulnerabilities. - for (final VulnerableSoftware vs : vsList) { - final VulnerableSoftware existingVs = qm.getVulnerableSoftwareByCpe23( - vs.getCpe23(), - vs.getVersionEndExcluding(), - vs.getVersionEndIncluding(), - vs.getVersionStartExcluding(), - vs.getVersionStartIncluding() - ); - if (existingVs != null) { - final boolean hasAttribution = qm.hasAffectedVersionAttribution(persistentVuln, existingVs, Vulnerability.Source.NVD); - if (!hasAttribution) { - logger.trace("%s: Adding attribution".formatted(persistentVuln.getVulnId())); - final AffectedVersionAttribution attribution = createAttribution(persistentVuln, existingVs, attributionDate); - qm.getPersistenceManager().makePersistent(attribution); - } else { - logger.debug("%s: Encountered dangling attribution; Re-using by updating firstSeen and lastSeen timestamps".formatted(persistentVuln.getVulnId())); - final AffectedVersionAttribution existingAttribution = qm.getAffectedVersionAttribution(persistentVuln, existingVs, Vulnerability.Source.NVD); - existingAttribution.setFirstSeen(attributionDate); - existingAttribution.setLastSeen(attributionDate); - } - vsListToKeep.add(existingVs); - } else { - logger.trace("%s: Creating new VS".formatted(persistentVuln.getVulnId())); - final VulnerableSoftware persistentVs = qm.getPersistenceManager().makePersistent(vs); - final AffectedVersionAttribution attribution = createAttribution(persistentVuln, persistentVs, attributionDate); - qm.getPersistenceManager().makePersistent(attribution); - vsListToKeep.add(persistentVs); - } - } - - logger.trace("%s: Final vsList: %d".formatted(persistentVuln.getVulnId(), vsListToKeep.size())); - if (!Objects.equals(persistentVuln.getVulnerableSoftware(), vsListToKeep)) { - logger.trace("%s: vsList has changed: %s".formatted(persistentVuln.getVulnId(), new PersistenceUtil.Diff(persistentVuln.getVulnerableSoftware(), vsListToKeep))); - persistentVuln.setVulnerableSoftware(vsListToKeep); - } - }); - } - - private static AffectedVersionAttribution createAttribution(final Vulnerability vuln, final VulnerableSoftware vs, - final Date attributionDate) { - final var attribution = new AffectedVersionAttribution(); - attribution.setSource(Vulnerability.Source.NVD); - attribution.setVulnerability(vuln); - attribution.setVulnerableSoftware(vs); - attribution.setFirstSeen(attributionDate); - attribution.setLastSeen(attributionDate); - return attribution; - } - /** * Get a {@link Vulnerability} by its CVE ID (implying the source {@link Vulnerability.Source#NVD}). *

          diff --git a/src/main/java/org/dependencytrack/tasks/NistApiMirrorTask.java b/src/main/java/org/dependencytrack/tasks/NistApiMirrorTask.java index ebea83b0c3..8dbd1ee52f 100644 --- a/src/main/java/org/dependencytrack/tasks/NistApiMirrorTask.java +++ b/src/main/java/org/dependencytrack/tasks/NistApiMirrorTask.java @@ -172,7 +172,7 @@ public void inform(final Event e) { AffectedVersionAttribution.class, Vulnerability.class, VulnerableSoftware.class); final Vulnerability persistentVuln = synchronizeVulnerability(qm, vuln); - synchronizeVulnerableSoftware(qm, persistentVuln, vsList); + qm.synchronizeVulnerableSoftware(persistentVuln, vsList, Vulnerability.Source.NVD); } catch (RuntimeException ex) { LOGGER.error("An unexpected error occurred while processing %s".formatted(vuln.getVulnId()), ex); } finally { diff --git a/src/main/java/org/dependencytrack/tasks/NistMirrorTask.java b/src/main/java/org/dependencytrack/tasks/NistMirrorTask.java index 32d6b1b440..73de5399a7 100644 --- a/src/main/java/org/dependencytrack/tasks/NistMirrorTask.java +++ b/src/main/java/org/dependencytrack/tasks/NistMirrorTask.java @@ -408,7 +408,7 @@ private void processVulnerability(final Vulnerability vuln, final List Date: Sat, 28 Sep 2024 18:15:04 +0200 Subject: [PATCH 215/429] Update changelog for v4.12.0 with recent changes Signed-off-by: nscuro --- docs/_docs/integrations/badges.md | 2 +- ...xx-xx-v4.12.0.md => 2024-10-01-v4.12.0.md} | 100 +++++++++++++++++- 2 files changed, 98 insertions(+), 4 deletions(-) rename docs/_posts/{2024-xx-xx-v4.12.0.md => 2024-10-01-v4.12.0.md} (54%) diff --git a/docs/_docs/integrations/badges.md b/docs/_docs/integrations/badges.md index e966fb3fe5..f05c6c9ada 100644 --- a/docs/_docs/integrations/badges.md +++ b/docs/_docs/integrations/badges.md @@ -11,7 +11,7 @@ basis via permission or globally for unauthenticated access. > **Deprecation Notice** > > Unauthenticated access to badges as a global configuration is deprecated and slated for removal in Dependency-Track -> v4.12. +> v4.13. To enable badges for a team, activate the permission `VIEW_BADGES`. To deactivate badges, remove the permission. To retrieve a badge, use a team's API key either in the badge API header `X-API-Key` or in the URI parameter `apiKey`. diff --git a/docs/_posts/2024-xx-xx-v4.12.0.md b/docs/_posts/2024-10-01-v4.12.0.md similarity index 54% rename from docs/_posts/2024-xx-xx-v4.12.0.md rename to docs/_posts/2024-10-01-v4.12.0.md index 38399118bb..af94052d22 100644 --- a/docs/_posts/2024-xx-xx-v4.12.0.md +++ b/docs/_posts/2024-10-01-v4.12.0.md @@ -3,8 +3,32 @@ title: v4.12.0 type: major --- +**Highlights:** + +* **Tags, Tags, Tags**. This release contains a breadth of tag-related features: + * Alerts can be limited to projects with specific tags + * Projects can be included or excluded from BOM validation using tags + * Projects can be tagged as part of a BOM upload request + * Tag input fields of the frontend now offer auto-complete +* **Tag Management**. It is now possible to view and manage tags in the system through the new +*tag management* view, and associated REST API endpoints. This makes it possible to see how many, +and which projects, policies, and alerts are associated with a given tag. Projects, policies, +and alerts can be un-tagged, and tags can be deleted altogether. + * *This feature was discussed and demoed in our July community meeting! Watch it [here](https://www.youtube.com/watch?v=rvigQKVvoN8&t=543s)* +* **Global Policy Violation Audit View**. Analog to the *Global Vulnerability Audit View* shipped in version 4.11.0, +this release includes a new interface to discover and filter policy violations across all projects in the portfolio. +* **Authorization for Badges**. Badges were previously not protected by authentication and authorization, and thus +were disabled by default. With this release, unauthenticated access is deprecated. Instead, authenticating +as a team with `VIEW_BADGES` permission is required. This can be combined with portfolio access control, +such that a key can only access the badges of a subset of projects. Refer to the [badges documentation] for details. +* **Modernization**. Behind the scenes, the tech stack that Dependency-Track is built on was upgraded +to the latest and greatest. We moved from Java 17 to Java 21, from Java EE to Jakarta EE 10, from Jetty 10 +to Jetty 12, and from Swagger v2 to OpenAPI v3. + **Features:** +* Exclude pre-releases from NuGet latest version check - [apiserver/#3468] +* Add global audit view for policy violations - [apiserver/#3544] * Raise baseline Java version to 21 - [apiserver/#3682] * Include whether a project's version is active in the `/api/v1/project/{uuid}` response - [apiserver/#3691] * Remove legacy `BomUploadProcessingTask` - [apiserver/#3722] @@ -16,7 +40,6 @@ type: major * Add notification for BOM validation failures - [apiserver/#3796] * Bump CWE dictionary to v4.14 - [apiserver/#3819] * Add ability to tag project upon BOM upload - [apiserver/#3843] -* Bump SPDX license list to v3.24.0, bringing in 25 new licenses - [apiserver/#3846] * Improve performance of finding retrieval via REST API - [apiserver/#3869] * Add REST endpoints for tag retrieval - [apiserver/#3881] * Deprecate `/api/v1/tag/{policyUuid}` in favor of `/api/v1/tag/policy/{uuid}` - [apiserver/#3887] @@ -28,12 +51,25 @@ type: major * Support the `component.authors` field of CycloneDX v1.6 - [apiserver/#3969] * Make project cloning an atomic operation - [apiserver/#3982] * Add option to test notifications - [apiserver/#3983] + * *This feature was demoed in our September community meeting! Watch it [here](https://www.youtube.com/watch?v=hzelt7jv6dE&t=684s)* * Log warning when dependency graph is missing the root node - [apiserver/#3990] * Add ability to limit notifications to projects with specific tags - [apiserver/#4031] + * *This feature was demoed in our September community meeting! Watch it [here](https://www.youtube.com/watch?v=hzelt7jv6dE&t=778s)* +* Enhance badge API to require authorization - [apiserver/#4059] +* Support assigning of teams for portfolio ACL when creating a project - [apiserver/#4093] * Disable redundant shutdown hook of the embedded H2 database - [apiserver/#4106] * Support inclusion and exclusion of projects from BOM validation with tags - [apiserver/#4109] + * *This feature was demoed in our September community meeting! Watch it [here](https://www.youtube.com/watch?v=hzelt7jv6dE&t=568s)* * Update Dependency-Track's own BOM to CycloneDX v1.5 - [apiserver/#4110] * Migrate Trivy integration to use Protobuf instead of JSON - [apiserver/#4116] +* Support customizable welcome message to display on login page - [apiserver/#4131] +* Raise maximum length of team names from 50 to 255 characters - [apiserver/#4134] +* Improve Jetty startup time - [apiserver/#4134] +* Support configuration of system-wide default locale - [apiserver/#4136] +* Bump SPDX license list to v3.25.0, bringing in 34 new licenses - [apiserver/#4145] +* Include team name in audit trail when auditing vulnerabilities with API key - [apiserver/#4154] +* Introduce `isLatest` flag for projects, and allow policies to be limited to latest version - [apiserver/#4184] +* Ensure modifying project endpoints are transactional - [apiserver/#4194] * Support for serving the frontend from a custom path - [frontend/#801] * Add dynamic policy violation badges - [frontend/#810] * Add quick search for projects also using a component - [frontend/#848] @@ -41,6 +77,9 @@ type: major * Make *Severity* and *CWE* columns of findings table sortable - [frontend/#907] * Raise baseline Node version to 20 - [frontend/#927] * Add autocomplete support for tag inputs - [frontend/#936] +* Save user preference for expanded navigation sidebar - [frontend/#988] +* Add ability to download component table as CSV - [frontend/#993] +* Add confirmation prompt for project deletion - [frontend/#996] **Fixes:** @@ -53,6 +92,19 @@ type: major * Fix occasional `column list index is out of range` exceptions - [apiserver/#4104] * Fix missing URL encoding for repository metadata analyzers - [apiserver/#4107] * Fix project being rendered as PURL in email notifications - [apiserver/#4108] +* Fix incorrect rendering of special characters in email notifications - [apiserver/#4141] +* Use empty string instead of SNAPSHOT as version in BOM download if project doesn't have a version - [apiserver/#4142] +* Handle empty component and service names in uploaded BOMs - [apiserver/#4146] +* Handle existing duplicate component properties - [apiserver/#4147] +* Fix infinite recursion during policy condition serialization - [apiserver/#4165] +* Fix `directDependencies` of cloned projects referring to original component UUIDs - [apiserver/#4153] +* Fix CPE not being imported from CycloneDX `metadata.component` - [apiserver/#4174] +* Fix update of an internal vulnerability clearing associated *Affected Components* - [apiserver/#4208] +* Fix metrics endpoint API docs erroneously claiming to return project and component data - [apiserver/#4195] +* Fix `IndexOutOfBoundsException` when mirroring OSV vulnerability without severity - [apiserver/#4196] +* Fix vulnerability endpoints returning projects and components that the principal shouldn't have +access to when portfolio ACL is enabled - [apiserver/#4201] +* Fix links with `href="#"` being pushed to Vue router - [frontend/#1012] **Upgrade Notes:** @@ -68,6 +120,13 @@ are encouraged to migrate to the new endpoint. * The legacy BOM processing logic was removed. The *BOM Processing V2* option introduced in v4.11 is now the default and the only available option. To gauge the impact of this change, consider enabling the experimental option in an existing v4.11 deployment first. +* Deletion of tags requires the new `TAG_MANAGEMENT` permission. The permission is *not* added to existing +users or teams automatically. Administrators should assign it to users and teams as needed. +* Accessing badges requires the new `VIEW_BADGES` permission. The permission is *not* added to existing +users or teams automatically. Administrators should assign it to users and teams as needed. +* Unauthenticated access to badges is *deprecated* and will be fully removed in v4.13. +* To support serving of the frontend from custom paths ([frontend/#801]), frontend containers can currently not +function with a read-only filesystem (as commonly used in Kubernetes environments). Refer to [frontend/#940] for details. For a complete list of changes, refer to the respective GitHub milestones: @@ -77,8 +136,8 @@ For a complete list of changes, refer to the respective GitHub milestones: We thank all organizations and individuals who contributed to this release, from logging issues to taking part in discussions on GitHub & Slack to testing of fixes. Special thanks to everyone who contributed code to implement enhancements and fix defects: -[@2000rosser], [@JCHacking], [@SaberStrat], [@Squixx], [@aravindparappil46], [@fupgang], [@gbonnefille], [@mehab], -[@rcsilva83], [@rh0dy], [@setchy] +[@2000rosser], [@Gepardgame], [@JCHacking], [@SaberStrat], [@Squixx], [@aravindparappil46], [@brentos99] [@fupgang], +[@gbonnefille], [@mehab], [@nvcastelli], [@peterakimball], [@rbt-mm], [@rcsilva83], [@rh0dy], [@rkg-mm], [@setchy] ###### dependency-track-apiserver.jar @@ -106,6 +165,10 @@ Special thanks to everyone who contributed code to implement enhancements and fi * API Server: [bom.json](https://github.com/DependencyTrack/dependency-track/releases/download/4.12.0/bom.json) * Frontend: [bom.json](https://github.com/DependencyTrack/frontend/releases/download/4.12.0/bom.json) +[badges documentation]: {{ site.baseurl }}{% link _docs/integrations/badges.md %} + +[apiserver/#3468]: https://github.com/DependencyTrack/dependency-track/pull/3468 +[apiserver/#3544]: https://github.com/DependencyTrack/dependency-track/pull/3544 [apiserver/#3682]: https://github.com/DependencyTrack/dependency-track/pull/3682 [apiserver/#3691]: https://github.com/DependencyTrack/dependency-track/pull/3691 [apiserver/#3722]: https://github.com/DependencyTrack/dependency-track/pull/3722 @@ -137,6 +200,8 @@ Special thanks to everyone who contributed code to implement enhancements and fi [apiserver/#4023]: https://github.com/DependencyTrack/dependency-track/pull/4023 [apiserver/#4026]: https://github.com/DependencyTrack/dependency-track/pull/4026 [apiserver/#4031]: https://github.com/DependencyTrack/dependency-track/pull/4031 +[apiserver/#4059]: https://github.com/DependencyTrack/dependency-track/pull/4059 +[apiserver/#4093]: https://github.com/DependencyTrack/dependency-track/pull/4093 [apiserver/#4104]: https://github.com/DependencyTrack/dependency-track/pull/4104 [apiserver/#4106]: https://github.com/DependencyTrack/dependency-track/pull/4106 [apiserver/#4107]: https://github.com/DependencyTrack/dependency-track/pull/4107 @@ -144,6 +209,24 @@ Special thanks to everyone who contributed code to implement enhancements and fi [apiserver/#4109]: https://github.com/DependencyTrack/dependency-track/pull/4109 [apiserver/#4110]: https://github.com/DependencyTrack/dependency-track/pull/4110 [apiserver/#4116]: https://github.com/DependencyTrack/dependency-track/pull/4116 +[apiserver/#4131]: https://github.com/DependencyTrack/dependency-track/pull/4131 +[apiserver/#4134]: https://github.com/DependencyTrack/dependency-track/pull/4134 +[apiserver/#4136]: https://github.com/DependencyTrack/dependency-track/pull/4136 +[apiserver/#4141]: https://github.com/DependencyTrack/dependency-track/pull/4141 +[apiserver/#4142]: https://github.com/DependencyTrack/dependency-track/pull/4142 +[apiserver/#4145]: https://github.com/DependencyTrack/dependency-track/pull/4145 +[apiserver/#4146]: https://github.com/DependencyTrack/dependency-track/pull/4146 +[apiserver/#4147]: https://github.com/DependencyTrack/dependency-track/pull/4147 +[apiserver/#4153]: https://github.com/DependencyTrack/dependency-track/pull/4171 +[apiserver/#4154]: https://github.com/DependencyTrack/dependency-track/pull/4154 +[apiserver/#4165]: https://github.com/DependencyTrack/dependency-track/pull/4165 +[apiserver/#4174]: https://github.com/DependencyTrack/dependency-track/pull/4174 +[apiserver/#4184]: https://github.com/DependencyTrack/dependency-track/pull/4184 +[apiserver/#4208]: https://github.com/DependencyTrack/dependency-track/pull/4208 +[apiserver/#4194]: https://github.com/DependencyTrack/dependency-track/pull/4194 +[apiserver/#4195]: https://github.com/DependencyTrack/dependency-track/pull/4195 +[apiserver/#4196]: https://github.com/DependencyTrack/dependency-track/pull/4196 +[apiserver/#4201]: https://github.com/DependencyTrack/dependency-track/pull/4201 [frontend/#801]: https://github.com/DependencyTrack/frontend/pull/801 [frontend/#810]: https://github.com/DependencyTrack/frontend/pull/810 @@ -152,15 +235,26 @@ Special thanks to everyone who contributed code to implement enhancements and fi [frontend/#907]: https://github.com/DependencyTrack/frontend/pull/907 [frontend/#927]: https://github.com/DependencyTrack/frontend/pull/927 [frontend/#936]: https://github.com/DependencyTrack/frontend/pull/936 +[frontend/#940]: https://github.com/DependencyTrack/frontend/issues/940 +[frontend/#988]: https://github.com/DependencyTrack/frontend/pull/988 +[frontend/#993]: https://github.com/DependencyTrack/frontend/pull/993 +[frontend/#996]: https://github.com/DependencyTrack/frontend/pull/996 +[frontend/#1012]: https://github.com/DependencyTrack/frontend/pull/1012 [@2000rosser]: https://github.com/2000rosser +[@Gepardgame]: https://github.com/Gepardgame [@JCHacking]: https://github.com/JCHacking [@SaberStrat]: https://github.com/SaberStrat [@Squixx]: https://github.com/Squixx [@aravindparappil46]: https://github.com/aravindparappil46 +[@brentos99]: https://github.com/brentos99 [@fupgang]: https://github.com/fupgang [@gbonnefille]: https://github.com/gbonnefille [@mehab]: https://github.com/mehab +[@nvcastelli]: https://github.com/nvcastelli +[@peterakimball]: https://github.com/peterakimball +[@rbt-mm]: https://github.com/rbt-mm [@rcsilva83]: https://github.com/rcsilva83 [@rh0dy]: https://github.com/rh0dy +[@rkg-mm]: https://github.com/rkg-mm [@setchy]: https://github.com/setchy \ No newline at end of file From 9c2884fa00320e2f66b1e55c6a3319a24f472742 Mon Sep 17 00:00:00 2001 From: nscuro Date: Tue, 1 Oct 2024 22:12:21 +0200 Subject: [PATCH 216/429] Bump bundled frontend to 4.12.0 Signed-off-by: nscuro --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 62bbecce54..9246f0c0a2 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ 21 - 4.11.6 + 4.12.0 ${project.parent.version} 4.2.2 0.1.2 From 5dc0383498e778f292e31f1981533785cb2732e4 Mon Sep 17 00:00:00 2001 From: Niklas Date: Tue, 1 Oct 2024 22:42:00 +0200 Subject: [PATCH 217/429] Add release artifact checksums for frontend v4.12.0 Signed-off-by: Niklas --- docs/_posts/2024-10-01-v4.12.0.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/_posts/2024-10-01-v4.12.0.md b/docs/_posts/2024-10-01-v4.12.0.md index af94052d22..6a038e782e 100644 --- a/docs/_posts/2024-10-01-v4.12.0.md +++ b/docs/_posts/2024-10-01-v4.12.0.md @@ -155,10 +155,10 @@ Special thanks to everyone who contributed code to implement enhancements and fi ###### frontend-dist.zip -| Algorithm | Checksum | -|:----------|:---------| -| SHA-1 | | -| SHA-256 | | +| Algorithm | Checksum | +|:----------|:-----------------------------------------------------------------| +| SHA-1 | 312dd2186deb81e50da00f2d42888711352f7853 | +| SHA-256 | 589eb0aae9a3fbdfde4bdd4dda000a2fb6e08a27e66a52ef9b17c1eaa022d46e | ###### Software Bill of Materials (SBOM) @@ -257,4 +257,4 @@ Special thanks to everyone who contributed code to implement enhancements and fi [@rcsilva83]: https://github.com/rcsilva83 [@rh0dy]: https://github.com/rh0dy [@rkg-mm]: https://github.com/rkg-mm -[@setchy]: https://github.com/setchy \ No newline at end of file +[@setchy]: https://github.com/setchy From f550f411e747acf659e6388a72290835714fb9f9 Mon Sep 17 00:00:00 2001 From: Niklas Date: Tue, 1 Oct 2024 22:42:31 +0200 Subject: [PATCH 218/429] Bump docs version to 4.12 Signed-off-by: Niklas --- docs/_config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_config.yml b/docs/_config.yml index 44eecfc77f..56103d0157 100755 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -6,7 +6,7 @@ url: "https://docs.dependencytrack.org" baseurl: show_full_navigation: true -version: v4.11 +version: v4.12 # Values for the jekyll-seo-tag gem (https://github.com/jekyll/jekyll-seo-tag) logo: /siteicon.png From 6820623b2892fa13b4131b5db4299059d5689622 Mon Sep 17 00:00:00 2001 From: Dependency-Track Bot <106437498+dependencytrack-bot@users.noreply.github.com> Date: Tue, 1 Oct 2024 20:44:02 +0000 Subject: [PATCH 219/429] prepare-release: set version to 4.12.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9246f0c0a2..164101be23 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ org.dependencytrack dependency-track war - 4.12.0-SNAPSHOT + 4.12.0 Dependency-Track https://dependencytrack.org/ From 3b8c428e0ffa87a217fa2e6d83d0f355c3fb5ba7 Mon Sep 17 00:00:00 2001 From: Niklas Date: Tue, 1 Oct 2024 22:53:38 +0200 Subject: [PATCH 220/429] Add release artifact checksums for v4.12.0 Signed-off-by: Niklas --- docs/_posts/2024-10-01-v4.12.0.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/_posts/2024-10-01-v4.12.0.md b/docs/_posts/2024-10-01-v4.12.0.md index 6a038e782e..a082f2db3e 100644 --- a/docs/_posts/2024-10-01-v4.12.0.md +++ b/docs/_posts/2024-10-01-v4.12.0.md @@ -143,15 +143,15 @@ Special thanks to everyone who contributed code to implement enhancements and fi | Algorithm | Checksum | |:----------|:---------| -| SHA-1 | | -| SHA-256 | | +| SHA-1 | 0cfe5d6cd014a0a25cdb0379e5a75596adc3d448 | +| SHA-256 | 83d31e132643249f7752154adc49690353484a66de6e77db7e25f0c1309528eb | ###### dependency-track-bundled.jar | Algorithm | Checksum | |:----------|:---------| -| SHA-1 | | -| SHA-256 | | +| SHA-1 | f7a1af3a5bf5f5b864d0db519fe2944391496f32 | +| SHA-256 | 3b4e27b29fd8a19cc5a250d394df43e0b046781f4d37c11720f8db8b9714d669 | ###### frontend-dist.zip From 7ca407941906f779321ea3aa6994f4c9e6de935f Mon Sep 17 00:00:00 2001 From: Niklas Date: Tue, 1 Oct 2024 22:54:32 +0200 Subject: [PATCH 221/429] Update versions in issue template for defects Signed-off-by: Niklas --- .github/ISSUE_TEMPLATE/defect-report.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/defect-report.yml b/.github/ISSUE_TEMPLATE/defect-report.yml index c2c23fb7ad..41fd2407f5 100644 --- a/.github/ISSUE_TEMPLATE/defect-report.yml +++ b/.github/ISSUE_TEMPLATE/defect-report.yml @@ -64,15 +64,8 @@ body: - 4.8.x - 4.9.x - 4.10.x - - 4.11.0 - - 4.11.1 - - 4.11.2 - - 4.11.3 - - 4.11.4 - - 4.11.5 - - 4.11.6 - - 4.11.7 - - 4.12.0-SNAPSHOT + - 4.11.x + - 4.12.0 validations: required: true - type: dropdown From 4e6d8253ffb3c8b4131dc082e68bcb3fa7fac413 Mon Sep 17 00:00:00 2001 From: Niklas Date: Tue, 1 Oct 2024 22:55:46 +0200 Subject: [PATCH 222/429] Update versions in issue template for defects Signed-off-by: Niklas --- .github/ISSUE_TEMPLATE/defect-report.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/defect-report.yml b/.github/ISSUE_TEMPLATE/defect-report.yml index 41fd2407f5..51eefc71c2 100644 --- a/.github/ISSUE_TEMPLATE/defect-report.yml +++ b/.github/ISSUE_TEMPLATE/defect-report.yml @@ -66,6 +66,7 @@ body: - 4.10.x - 4.11.x - 4.12.0 + - 4.13.0-SNAPSHOT validations: required: true - type: dropdown From 45df41e87b92c73b164448234aa51ce3a0959c03 Mon Sep 17 00:00:00 2001 From: Niklas Date: Tue, 1 Oct 2024 22:59:09 +0200 Subject: [PATCH 223/429] Bump version to 4.13.0-SNAPSHOT Signed-off-by: Niklas --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 164101be23..b7c0071de5 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ org.dependencytrack dependency-track war - 4.12.0 + 4.13.0-SNAPSHOT Dependency-Track https://dependencytrack.org/ From 8c70d69b7f1dca06ac67d5419c6d88f77bf7edab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 08:44:21 +0000 Subject: [PATCH 224/429] Bump org.eclipse.jetty.ee10:jetty-ee10-maven-plugin Bumps org.eclipse.jetty.ee10:jetty-ee10-maven-plugin from 12.0.13 to 12.0.14. --- updated-dependencies: - dependency-name: org.eclipse.jetty.ee10:jetty-ee10-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b7c0071de5..bc42c3d21c 100644 --- a/pom.xml +++ b/pom.xml @@ -130,7 +130,7 @@ 42.7.4 false - 12.0.13 + 12.0.14 3.11.4 src/main/webapp/** From 3acc7797ce3e2088979997fb649c41f6a9edd413 Mon Sep 17 00:00:00 2001 From: nscuro Date: Fri, 4 Oct 2024 00:09:38 +0200 Subject: [PATCH 225/429] Prevent duplicate policy violations Introduces project-level locking for BOM upload processing, policy evaluation, and vulnerability analysis. This prevents duplicate records from being created during any of the mentioned activities. The locking happens in-memory. Refactors policy violation reconciliation to be more deterministic and able to remove duplicates. In a later release, a `UNIQUE` constraint should be added to the `POLICYVIOLATION` table to prevent duplicate records on the database-level (already done in Hyades). Fixes #4215 Signed-off-by: nscuro --- .../persistence/PolicyQueryManager.java | 94 ++++++++++++++----- .../dependencytrack/policy/PolicyEngine.java | 9 +- .../tasks/BomUploadProcessingTask.java | 8 +- .../tasks/PolicyEvaluationTask.java | 38 ++++++-- .../tasks/VulnerabilityAnalysisTask.java | 65 +++++++++---- .../org/dependencytrack/util/LockUtil.java | 78 +++++++++++++++ .../resources/v1/PolicyResourceTest.java | 1 - 7 files changed, 233 insertions(+), 60 deletions(-) create mode 100644 src/main/java/org/dependencytrack/util/LockUtil.java diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index 0f0ded45c9..2f2637aa2a 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -43,10 +43,13 @@ import java.util.Collection; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; +import static org.dependencytrack.util.PersistenceUtil.assertNonPersistentAll; import static org.dependencytrack.util.PersistenceUtil.assertPersistent; import static org.dependencytrack.util.PersistenceUtil.assertPersistentAll; @@ -153,38 +156,79 @@ public PolicyCondition updatePolicyCondition(final PolicyCondition policyConditi return persist(pc); } + private record ViolationIdentity( + long componentId, + long conditionId, + PolicyViolation.Type type) { + + private ViolationIdentity(final PolicyViolation violation) { + this(violation.getComponent().getId(), violation.getPolicyCondition().getId(), violation.getType()); + } + + } + /** * Intelligently adds dependencies for components that are not already a dependency * of the specified project and removes the dependency relationship for components * that are not in the list of specified components. * @param component the project to bind components to - * @param policyViolations the complete list of existing dependent components - */ - public synchronized void reconcilePolicyViolations(final Component component, final List policyViolations) { - // Removes violations as dependencies to the project for all - // components not included in the list provided - List markedForDeletion = new ArrayList<>(); - for (final PolicyViolation existingViolation: getAllPolicyViolations(component)) { - boolean keep = false; - for (final PolicyViolation violation: policyViolations) { - if (violation.getType() == existingViolation.getType() - && violation.getPolicyCondition().getId() == existingViolation.getPolicyCondition().getId() - && violation.getComponent().getId() == existingViolation.getComponent().getId()) - { - keep = true; - break; + * @param reportedViolations the complete list of existing dependent components + */ + public synchronized void reconcilePolicyViolations( + final Component component, + final List reportedViolations) { + assertPersistent(component, "component must be persistent"); + assertNonPersistentAll(reportedViolations, "reportedViolations must not be persistent"); + + runInTransaction(() -> { + final List existingViolations = getAllPolicyViolations(component); + final var violationsToCreate = new ArrayList(); + final var violationsToDelete = new ArrayList(); + + final var existingViolationByIdentity = new HashMap(); + for (final PolicyViolation violation : existingViolations) { + // Previous reconciliation logic allowed for duplicate violations + // to exist. Take that into consideration and ensure their deletion. + final boolean isDuplicate = existingViolationByIdentity.putIfAbsent( + new ViolationIdentity(violation), violation) != null; + if (isDuplicate) { + violationsToDelete.add(violation); } } - if (!keep) { - markedForDeletion.add(existingViolation); + + final var reportedViolationsByIdentity = new HashMap(); + for (final PolicyViolation violation : reportedViolations) { + reportedViolationsByIdentity.put(new ViolationIdentity(violation), violation); } - } - if (!markedForDeletion.isEmpty()) { - for (final PolicyViolation violation : markedForDeletion) { - deleteViolationAnalysisTrail(violation); + + final Set violationIdentities = new HashSet<>( + existingViolationByIdentity.size() + reportedViolationsByIdentity.size()); + violationIdentities.addAll(existingViolationByIdentity.keySet()); + violationIdentities.addAll(reportedViolationsByIdentity.keySet()); + + for (final ViolationIdentity identity : violationIdentities) { + final PolicyViolation existingViolation = existingViolationByIdentity.get(identity); + final PolicyViolation reportedViolation = reportedViolationsByIdentity.get(identity); + + if (existingViolation == null) { + violationsToCreate.add(reportedViolation); + } else if (reportedViolation == null) { + violationsToDelete.add(existingViolation); + } } - delete(markedForDeletion); - } + + if (!violationsToCreate.isEmpty()) { + persist(violationsToCreate); + } + + if (!violationsToDelete.isEmpty()) { + for (final PolicyViolation violation : violationsToDelete) { + deleteViolationAnalysisTrail(violation); + } + + delete(violationsToDelete); + } + }); } /** @@ -222,7 +266,7 @@ public PolicyViolation clonePolicyViolation(PolicyViolation sourcePolicyViolatio if(comments != null){ violationAnalysis.setAnalysisComments(comments); } - policyViolation.setAnalysis(violationAnalysis); + policyViolation.setAnalysis(violationAnalysis); policyViolation.getAnalysis().setPolicyViolation(policyViolation); policyViolation.setUuid(sourcePolicyViolation.getUuid()); return policyViolation; @@ -465,7 +509,7 @@ public List cloneViolationAnalysisComments(PolicyViola comments.add(comment); } } - + return comments; } diff --git a/src/main/java/org/dependencytrack/policy/PolicyEngine.java b/src/main/java/org/dependencytrack/policy/PolicyEngine.java index 67db3db1a9..b6ee0a2d20 100644 --- a/src/main/java/org/dependencytrack/policy/PolicyEngine.java +++ b/src/main/java/org/dependencytrack/policy/PolicyEngine.java @@ -27,6 +27,7 @@ import org.dependencytrack.model.Tag; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.util.NotificationUtil; + import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -102,10 +103,10 @@ private List evaluate(final QueryManager qm, final List } if (Policy.Operator.ANY == policy.getOperator()) { if (policyConditionsViolated > 0) { - policyViolations.addAll(createPolicyViolations(qm, policyConditionViolations)); + policyViolations.addAll(createPolicyViolations(policyConditionViolations)); } } else if (Policy.Operator.ALL == policy.getOperator() && policyConditionsViolated == policy.getPolicyConditions().size()) { - policyViolations.addAll(createPolicyViolations(qm, policyConditionViolations)); + policyViolations.addAll(createPolicyViolations(policyConditionViolations)); } } } @@ -125,7 +126,7 @@ private boolean isPolicyAssignedToProject(Policy policy, Project project) { return (policy.getProjects().stream().anyMatch(p -> p.getId() == project.getId()) || (Boolean.TRUE.equals(policy.isIncludeChildren()) && isPolicyAssignedToParentProject(policy, project))); } - private List createPolicyViolations(final QueryManager qm, final List pcvList) { + private List createPolicyViolations(final List pcvList) { final List policyViolations = new ArrayList<>(); for (PolicyConditionViolation pcv : pcvList) { final PolicyViolation pv = new PolicyViolation(); @@ -133,7 +134,7 @@ private List createPolicyViolations(final QueryManager qm, fina pv.setPolicyCondition(pcv.getPolicyCondition()); pv.setType(determineViolationType(pcv.getPolicyCondition().getSubject())); pv.setTimestamp(new Date()); - policyViolations.add(qm.addPolicyViolationIfNotExist(pv)); + policyViolations.add(pv); } return policyViolations; } diff --git a/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java b/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java index cc234968af..f21b52c88c 100644 --- a/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java +++ b/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java @@ -77,6 +77,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Predicate; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -99,6 +100,7 @@ import static org.dependencytrack.parser.cyclonedx.util.ModelConverter.convertToProject; import static org.dependencytrack.parser.cyclonedx.util.ModelConverter.convertToProjectMetadata; import static org.dependencytrack.parser.cyclonedx.util.ModelConverter.flatten; +import static org.dependencytrack.util.LockUtil.getLockForProjectAndNamespace; import static org.dependencytrack.util.PersistenceUtil.applyIfChanged; import static org.dependencytrack.util.PersistenceUtil.assertPersistent; @@ -169,10 +171,12 @@ private void processEvent(final Context ctx, final BomUploadEvent event) { ctx.bomSerialNumber = cdxBom.getSerialNumber().replaceFirst("^urn:uuid:", ""); } + final ReentrantLock lock = getLockForProjectAndNamespace(ctx.project, getClass().getSimpleName()); try (var ignoredMdcBomFormat = MDC.putCloseable(MDC_BOM_FORMAT, ctx.bomFormat.getFormatShortName()); var ignoredMdcBomSpecVersion = MDC.putCloseable(MDC_BOM_SPEC_VERSION, ctx.bomSpecVersion); var ignoredMdcBomSerialNumber = MDC.putCloseable(MDC_BOM_SERIAL_NUMBER, ctx.bomSerialNumber); var ignoredMdcBomVersion = MDC.putCloseable(MDC_BOM_VERSION, String.valueOf(ctx.bomVersion))) { + lock.lock(); processBom(ctx, cdxBom); LOGGER.debug("Dispatching %d events".formatted(eventsToDispatch.size())); @@ -180,6 +184,8 @@ private void processEvent(final Context ctx, final BomUploadEvent event) { } catch (RuntimeException e) { LOGGER.error("Failed to process BOM", e); dispatchBomProcessingFailedNotification(ctx, e); + } finally { + lock.unlock(); } } @@ -272,8 +278,6 @@ private void processBom(final Context ctx, final org.cyclonedx.model.Bom cdxBom) // by project; This is not the case for vulnerabilities. We don't want the entire TRX to fail, // just because another TRX created or modified the same vulnerability record. - // TODO: Introduce locking by project ID / UUID to avoid processing BOMs for the same project concurrently. - qm.runInTransaction(() -> { final Project persistentProject = processProject(ctx, qm, project, projectMetadata); diff --git a/src/main/java/org/dependencytrack/tasks/PolicyEvaluationTask.java b/src/main/java/org/dependencytrack/tasks/PolicyEvaluationTask.java index 93100fcc69..7be4477124 100644 --- a/src/main/java/org/dependencytrack/tasks/PolicyEvaluationTask.java +++ b/src/main/java/org/dependencytrack/tasks/PolicyEvaluationTask.java @@ -18,7 +18,6 @@ */ package org.dependencytrack.tasks; -import alpine.common.logging.Logger; import alpine.event.framework.Event; import alpine.event.framework.Subscriber; import org.dependencytrack.event.PolicyEvaluationEvent; @@ -26,26 +25,45 @@ import org.dependencytrack.model.Component; import org.dependencytrack.model.Project; import org.dependencytrack.policy.PolicyEngine; +import org.slf4j.MDC; + import java.util.ArrayList; import java.util.List; +import java.util.concurrent.locks.ReentrantLock; -public class PolicyEvaluationTask implements Subscriber { +import static org.dependencytrack.common.MdcKeys.MDC_EVENT_TOKEN; +import static org.dependencytrack.common.MdcKeys.MDC_PROJECT_NAME; +import static org.dependencytrack.common.MdcKeys.MDC_PROJECT_UUID; +import static org.dependencytrack.common.MdcKeys.MDC_PROJECT_VERSION; +import static org.dependencytrack.util.LockUtil.getLockForProjectAndNamespace; - private static final Logger LOGGER = Logger.getLogger(PolicyEvaluationTask.class); +public class PolicyEvaluationTask implements Subscriber { /** * {@inheritDoc} */ @Override public void inform(final Event e) { - if (e instanceof PolicyEvaluationEvent event) { - if (event.getProject() != null) { - if (event.getComponents() != null && !event.getComponents().isEmpty()) { - performPolicyEvaluation(event.getProject(), event.getComponents()); - } else { - performPolicyEvaluation(event.getProject(), new ArrayList<>()); - } + if (!(e instanceof final PolicyEvaluationEvent event)) { + return; + } + if (event.getProject() == null) { + return; + } + + final ReentrantLock lock = getLockForProjectAndNamespace(event.getProject(), getClass().getSimpleName()); + try (var ignoredMdcProjectUuid = MDC.putCloseable(MDC_PROJECT_UUID, event.getProject().getUuid().toString()); + var ignoredMdcProjectName = MDC.putCloseable(MDC_PROJECT_NAME, event.getProject().getName()); + var ignoredMdcProjectVersion = MDC.putCloseable(MDC_PROJECT_VERSION, event.getProject().getVersion()); + var ignoredMdcEventToken = MDC.putCloseable(MDC_EVENT_TOKEN, event.getChainIdentifier().toString())) { + lock.lock(); + if (event.getComponents() != null && !event.getComponents().isEmpty()) { + performPolicyEvaluation(event.getProject(), event.getComponents()); + } else { + performPolicyEvaluation(event.getProject(), new ArrayList<>()); } + } finally { + lock.unlock(); } } diff --git a/src/main/java/org/dependencytrack/tasks/VulnerabilityAnalysisTask.java b/src/main/java/org/dependencytrack/tasks/VulnerabilityAnalysisTask.java index b351bc99e1..06747afa59 100644 --- a/src/main/java/org/dependencytrack/tasks/VulnerabilityAnalysisTask.java +++ b/src/main/java/org/dependencytrack/tasks/VulnerabilityAnalysisTask.java @@ -43,12 +43,15 @@ import org.dependencytrack.tasks.scanners.SnykAnalysisTask; import org.dependencytrack.tasks.scanners.TrivyAnalysisTask; import org.dependencytrack.tasks.scanners.VulnDbAnalysisTask; + import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import java.util.stream.Collectors; +import java.util.concurrent.locks.ReentrantLock; + +import static org.dependencytrack.util.LockUtil.getLockForProjectAndNamespace; public class VulnerabilityAnalysisTask implements Subscriber { @@ -60,33 +63,59 @@ public class VulnerabilityAnalysisTask implements Subscriber { @Override public void inform(final Event e) { if (e instanceof VulnerabilityAnalysisEvent event) { - if (event.getComponents() != null && event.getComponents().size() > 0) { - final List components = new ArrayList<>(); - try (final QueryManager qm = new QueryManager()) { - for (final Component c : event.getComponents()) { - // Ensures the current component (and related objects such as Project) are attached to the - // current persistence manager. This may cause duplicate projects to be created and other - // unexpected behavior. - components.add(qm.getObjectByUuid(Component.class, c.getUuid())); + final ReentrantLock projectLock; + if (event.getProject() != null) { + projectLock = getLockForProjectAndNamespace(event.getProject(), getClass().getSimpleName()); + } else { + projectLock = null; + } + + try { + if (projectLock != null) { + projectLock.lock(); + } + + if (event.getComponents() != null && !event.getComponents().isEmpty()) { + final List components = new ArrayList<>(); + try (final QueryManager qm = new QueryManager()) { + for (final Component c : event.getComponents()) { + // Ensures the current component (and related objects such as Project) are attached to the + // current persistence manager. This may cause duplicate projects to be created and other + // unexpected behavior. + components.add(qm.getObjectByUuid(Component.class, c.getUuid())); + } + analyzeComponents(qm, components, e); } - analyzeComponents(qm, components, e); + } + } finally { + if (projectLock != null) { + projectLock.unlock(); } } - } else if (e instanceof PortfolioVulnerabilityAnalysisEvent event) { + } else if (e instanceof PortfolioVulnerabilityAnalysisEvent) { LOGGER.info("Analyzing portfolio"); try (final QueryManager qm = new QueryManager()) { final List projectUuids = qm.getAllProjects(true) .stream() .map(Project::getUuid) - .collect(Collectors.toList()); + .toList(); for (final UUID projectUuid : projectUuids) { final Project project = qm.getObjectByUuid(Project.class, projectUuid); - if (project == null) continue; - final List components = qm.getAllComponents(project); - LOGGER.info("Analyzing " + components.size() + " components in project: " + project.getUuid()); - analyzeComponents(qm, components, e); - performPolicyEvaluation(project, components); - LOGGER.info("Completed scheduled analysis of " + components.size() + " components in project: " + project.getUuid()); + if (project == null) { + continue; + } + + final ReentrantLock projectLock = getLockForProjectAndNamespace(project, getClass().getSimpleName()); + try { + projectLock.lock(); + final List components = qm.getAllComponents(project); + LOGGER.info("Analyzing " + components.size() + " components in project: " + project.getUuid()); + analyzeComponents(qm, components, e); + performPolicyEvaluation(project, components); + LOGGER.info("Completed scheduled analysis of " + components.size() + " components in project: " + project.getUuid()); + } finally { + projectLock.unlock(); + } } } LOGGER.info("Portfolio analysis complete"); diff --git a/src/main/java/org/dependencytrack/util/LockUtil.java b/src/main/java/org/dependencytrack/util/LockUtil.java new file mode 100644 index 0000000000..407a5582c4 --- /dev/null +++ b/src/main/java/org/dependencytrack/util/LockUtil.java @@ -0,0 +1,78 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.util; + +import alpine.Config; +import alpine.common.metrics.Metrics; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import io.micrometer.core.instrument.binder.cache.CaffeineCacheMetrics; +import org.dependencytrack.model.Project; + +import java.time.Duration; +import java.util.Collections; +import java.util.concurrent.locks.ReentrantLock; + +import static alpine.Config.AlpineKey.METRICS_ENABLED; +import static java.util.Objects.requireNonNull; + +/** + * @since 4.13.0 + */ +public final class LockUtil { + + private static final LoadingCache CACHE = buildCache(); + + private LockUtil() { + } + + public static ReentrantLock getLockForName(final String name) { + requireNonNull(name, "name must not be null"); + return CACHE.get(name); + } + + public static ReentrantLock getLockForProjectAndNamespace(final Project project, final String namespace) { + requireNonNull(namespace, "namespace must not be null"); + requireNonNull(project, "project must not be null"); + requireNonNull(project.getUuid(), "project UUID must not be null"); + return getLockForName(namespace + ":" + project.getUuid()); + } + + private static LoadingCache buildCache() { + final boolean metricsEnabled = Config.getInstance() + .getPropertyAsBoolean(METRICS_ENABLED); + + final Caffeine cacheBuilder = Caffeine.newBuilder() + .expireAfterAccess(Duration.ofMinutes(1)); + if (metricsEnabled) { + cacheBuilder.recordStats(); + } + + final LoadingCache cache = cacheBuilder + .build(key -> new ReentrantLock()); + + if (metricsEnabled) { + new CaffeineCacheMetrics<>(cache, "dtrack_locks", Collections.emptyList()) + .bindTo(Metrics.getRegistry()); + } + + return cache; + } + +} diff --git a/src/test/java/org/dependencytrack/resources/v1/PolicyResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/PolicyResourceTest.java index f2a1d6d402..0e7b5f2f06 100644 --- a/src/test/java/org/dependencytrack/resources/v1/PolicyResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/PolicyResourceTest.java @@ -211,7 +211,6 @@ public void deletePolicyCascadingTest() { violation.setPolicyCondition(condition); violation.setType(PolicyViolation.Type.OPERATIONAL); violation.setTimestamp(new Date()); - violation = qm.addPolicyViolationIfNotExist(violation); qm.reconcilePolicyViolations(component, singletonList(violation)); From 5e56fda8e8f360728b1a2d462df0058ef3855920 Mon Sep 17 00:00:00 2001 From: nscuro Date: Fri, 4 Oct 2024 19:52:48 +0200 Subject: [PATCH 226/429] Fix unintended manual flushing mode due to DataNucleus `ExecutionContext` pooling For BOM upload processing, we customize the `PersistenceManager` for better performance: https://github.com/DependencyTrack/dependency-track/blob/6f7c49c0f2f163895b95e5b80554a49a3ec500f8/src/main/java/org/dependencytrack/tasks/BomUploadProcessingTask.java#L231-L259 It turns out that the custom options are internally delegated to a DataNucleus `ExecutionContext`. Other than `PersistenceManager`s, `ExecutionContext`s are never really closed, but instead reused (https://github.com/datanucleus/datanucleus-core/blob/master/src/main/java/org/datanucleus/ExecutionContextPool.java). Per default, DN pools up to 20 ECs. The issue with this behavior is that customizing a PM in code location A, can unintentionally impact code location B. An example of this is project cloning, where an EC can get picked that has `FlushMode=MANUAL`, whereas the cloning logic assumes the default `FlushMode=AUTO` (https://github.com/DependencyTrack/dependency-track/issues/4220). Disable EC pooling to prevent such unintended behavior changes. Fixes #4220 Signed-off-by: nscuro --- src/main/resources/application.properties | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index b271bd40d8..d52be78281 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -146,6 +146,13 @@ alpine.database.pool.max.lifetime=600000 # DO NOT CHANGE UNLESS THERE IS A GOOD REASON TO. # alpine.datanucleus.cache.level2.type= +# Required +# Controls the maximum number of ExecutionContext objects that are pooled by DataNucleus. +# The default defined by DataNucleus is 20. However, since Dependency-Track occasionally +# tweaks ExecutionContexts (e.g. to disable auto-flushing for insert-heavy tasks), +# they are not safe for reuse. We thus disable EC pooling completely. +alpine.datanucleus.executioncontext.maxidle=0 + # Required # Specifies the number of bcrypt rounds to use when hashing a users password. # The higher the number the more secure the password, at the expense of From ca6de5cbe779ef344e15170a3f20d717d463a79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Mon, 7 Oct 2024 07:52:03 +0200 Subject: [PATCH 227/429] Log contains now username when user gets deleted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../java/org/dependencytrack/resources/v1/UserResource.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/UserResource.java b/src/main/java/org/dependencytrack/resources/v1/UserResource.java index 27685eb9d1..e8110efed6 100644 --- a/src/main/java/org/dependencytrack/resources/v1/UserResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/UserResource.java @@ -453,7 +453,7 @@ public Response deleteLdapUser(LdapUser jsonUser) { if (user != null) { final LdapUser detachedUser = qm.getPersistenceManager().detachCopy(user); qm.delete(user); - super.logSecurityEvent(LOGGER, SecurityMarkers.SECURITY_AUDIT, "LDAP user deleted: " + detachedUser); + super.logSecurityEvent(LOGGER, SecurityMarkers.SECURITY_AUDIT, "LDAP user deleted: " + detachedUser.getUsername()); Notification.dispatch(new Notification() .scope(NotificationScope.SYSTEM) .group(NotificationGroup.USER_DELETED) @@ -593,7 +593,7 @@ public Response deleteManagedUser(ManagedUser jsonUser) { if (user != null) { final ManagedUser detachedUser = qm.getPersistenceManager().detachCopy(user); qm.delete(user); - super.logSecurityEvent(LOGGER, SecurityMarkers.SECURITY_AUDIT, "Managed user deleted: " +detachedUser); + super.logSecurityEvent(LOGGER, SecurityMarkers.SECURITY_AUDIT, "Managed user deleted: " + detachedUser.getUsername()); Notification.dispatch(new Notification() .scope(NotificationScope.SYSTEM) .group(NotificationGroup.USER_DELETED) @@ -670,7 +670,7 @@ public Response deleteOidcUser(final OidcUser jsonUser) { if (user != null) { final OidcUser detachedUser = qm.getPersistenceManager().detachCopy(user); qm.delete(user); - super.logSecurityEvent(LOGGER, SecurityMarkers.SECURITY_AUDIT, "OpenID Connect user deleted: " + detachedUser); + super.logSecurityEvent(LOGGER, SecurityMarkers.SECURITY_AUDIT, "OpenID Connect user deleted: " + detachedUser.getUsername()); Notification.dispatch(new Notification() .scope(NotificationScope.SYSTEM) .group(NotificationGroup.USER_DELETED) From ddf097fef3ec21b5f015b5f0d524125786c7fca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Schauer-K=C3=B6ckeis?= Date: Mon, 7 Oct 2024 09:34:57 +0200 Subject: [PATCH 228/429] Added test for Oidc user deletion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Schauer-Köckeis --- .../resources/v1/UserResourceAuthenticatedTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/java/org/dependencytrack/resources/v1/UserResourceAuthenticatedTest.java b/src/test/java/org/dependencytrack/resources/v1/UserResourceAuthenticatedTest.java index c8babba726..7dc543d6fe 100644 --- a/src/test/java/org/dependencytrack/resources/v1/UserResourceAuthenticatedTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/UserResourceAuthenticatedTest.java @@ -499,6 +499,19 @@ public void createOidcUserDuplicateUsernameTest() { Assert.assertEquals("A user with the same username already exists. Cannot create new user.", body); } + @Test + public void deleteOidcUserTest() { + qm.createOidcUser("blackbeard"); + OidcUser user = new OidcUser(); + user.setUsername("blackbeard"); + Response response = jersey.target(V1_USER + "/oidc").request() + .header(X_API_KEY, apiKey) + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true) // HACK + .method("DELETE", Entity.entity(user, MediaType.APPLICATION_JSON)); // HACK + // Hack: Workaround to https://github.com/eclipse-ee4j/jersey/issues/3798 + Assert.assertEquals(204, response.getStatus(), 0); + } + @Test public void addTeamToUserTest() { qm.createManagedUser("blackbeard", "Captain BlackBeard", "blackbeard@example.com", TEST_USER_PASSWORD_HASH, false, false, false); From adc10bef2ffa3d11144f86680f7f80c9ef626536 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 08:31:00 +0000 Subject: [PATCH 229/429] Bump docker/build-push-action from 6.8.0 to 6.9.0 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.8.0 to 6.9.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/32945a339266b759abcbdc89316275140b0fc960...4f58ea79222b3b9dc2c8bbdd6debcef730109a75) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 890f76be5e..6da734e96c 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -121,7 +121,7 @@ jobs: echo "tags=${TAGS}" >> $GITHUB_OUTPUT - name: Build multi-arch Container Image - uses: docker/build-push-action@32945a339266b759abcbdc89316275140b0fc960 # tag=v6.8.0 + uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # tag=v6.9.0 with: tags: ${{ steps.tags.outputs.tags }} build-args: |- From 2098ebabd9a146c627aeea3921cd664170c9c868 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 08:31:04 +0000 Subject: [PATCH 230/429] Bump docker/setup-buildx-action from 3.6.1 to 3.7.1 Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.6.1 to 3.7.1. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/988b5a0280414f521da01fcc63a27aeeb4b104db...c47758b77c9736f4b2ef4073d4d51994fabfe349) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 890f76be5e..19032a9351 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -90,7 +90,7 @@ jobs: uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # tag=v3.2.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # tag=v3.6.1 + uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # tag=v3.7.1 id: buildx with: install: true From 0ddf6fd32d589acda3446214db8d325e29f05f6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 08:31:12 +0000 Subject: [PATCH 231/429] Bump github/codeql-action from 3.26.9 to 3.26.11 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.9 to 3.26.11. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/461ef6c76dfe95d5c364de2f431ddbd31a417628...6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 890f76be5e..a9c90d2fec 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -149,6 +149,6 @@ jobs: - name: Upload Trivy Scan Results to GitHub Security Tab if: ${{ inputs.publish-container }} - uses: github/codeql-action/upload-sarif@461ef6c76dfe95d5c364de2f431ddbd31a417628 # tag=v3.26.9 + uses: github/codeql-action/upload-sarif@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # tag=v3.26.11 with: sarif_file: 'trivy-results.sarif' From 065bdd4e98cc843bd60928a21382322f0f303219 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 08:35:08 +0000 Subject: [PATCH 232/429] Bump com.icegreen:greenmail-junit4 from 2.0.1 to 2.1.0 Bumps [com.icegreen:greenmail-junit4](https://github.com/greenmail-mail-test/greenmail) from 2.0.1 to 2.1.0. - [Release notes](https://github.com/greenmail-mail-test/greenmail/releases) - [Commits](https://github.com/greenmail-mail-test/greenmail/compare/release-2.0.1...release-2.1.0) --- updated-dependencies: - dependency-name: com.icegreen:greenmail-junit4 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bc42c3d21c..a0376427d6 100644 --- a/pom.xml +++ b/pom.xml @@ -99,7 +99,7 @@ 1.4.2 1.0.1 9.0.5 - 2.0.1 + 2.1.0 2.17.2 2.17.2 20240303 From 01e7dfd029e87629751b7bb00d376b551b4c4e26 Mon Sep 17 00:00:00 2001 From: nscuro Date: Wed, 9 Oct 2024 14:20:49 +0200 Subject: [PATCH 233/429] Enhance policy violation de-duplication logic Instead of just discarding *any* duplicate, compare them first, and only keep the most "useful" one. https://github.com/DependencyTrack/dependency-track/issues/4215#issuecomment-2400099379 Signed-off-by: nscuro --- .../persistence/PolicyQueryManager.java | 54 ++++++- .../policy/PolicyEngineTest.java | 139 +++++++++++++++++- 2 files changed, 186 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index 2f2637aa2a..3aa3b07190 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -46,6 +46,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; @@ -187,13 +188,54 @@ public synchronized void reconcilePolicyViolations( final var existingViolationByIdentity = new HashMap(); for (final PolicyViolation violation : existingViolations) { - // Previous reconciliation logic allowed for duplicate violations - // to exist. Take that into consideration and ensure their deletion. - final boolean isDuplicate = existingViolationByIdentity.putIfAbsent( - new ViolationIdentity(violation), violation) != null; - if (isDuplicate) { + // Previous (<= 4.12.0) reconciliation logic allowed for duplicate violations to exist. + // Take that into consideration and ensure their deletion. + existingViolationByIdentity.compute(new ViolationIdentity(violation), (ignored, duplicateViolation) -> { + if (duplicateViolation == null) { + return violation; + } + + // Prefer to keep violations with existing analysis. + if (violation.getAnalysis() != null && duplicateViolation.getAnalysis() == null) { + violationsToDelete.add(duplicateViolation); + return violation; + } else if (violation.getAnalysis() == null && duplicateViolation.getAnalysis() != null) { + violationsToDelete.add(violation); + return duplicateViolation; + } + + // If none of the violations have an analysis, prefer to keep the oldest. + if (violation.getAnalysis() == null && duplicateViolation.getAnalysis() == null) { + final int timestampComparisonResult = Objects.compare( + violation.getTimestamp(), duplicateViolation.getTimestamp(), Date::compareTo); + if (timestampComparisonResult < 0) { + // Duplicate violation is newer. + violationsToDelete.add(duplicateViolation); + return violation; + } else if (timestampComparisonResult > 0) { + // Duplicate violation is older. + violationsToDelete.add(violation); + return duplicateViolation; + } + + // Everything else being equal, keep the duplicate violation. + violationsToDelete.add(violation); + return duplicateViolation; + } + + // If both violations have an analysis, prefer to keep the suppressed one. + if (violation.getAnalysis().isSuppressed() && !duplicateViolation.getAnalysis().isSuppressed()) { + violationsToDelete.add(duplicateViolation); + return violation; + } else if (!violation.getAnalysis().isSuppressed() && duplicateViolation.getAnalysis().isSuppressed()) { + violationsToDelete.add(violation); + return duplicateViolation; + } + + // Everything else being equal, keep the duplicate violation. violationsToDelete.add(violation); - } + return duplicateViolation; + }); } final var reportedViolationsByIdentity = new HashMap(); diff --git a/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java b/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java index 71f30b06c4..e5683a1c9d 100644 --- a/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java +++ b/src/test/java/org/dependencytrack/policy/PolicyEngineTest.java @@ -36,6 +36,7 @@ import org.dependencytrack.model.Project; import org.dependencytrack.model.Severity; import org.dependencytrack.model.Tag; +import org.dependencytrack.model.ViolationAnalysis; import org.dependencytrack.model.ViolationAnalysisState; import org.dependencytrack.model.Vulnerability; import org.dependencytrack.notification.NotificationGroup; @@ -50,11 +51,12 @@ import org.junit.Test; import org.junit.jupiter.api.Assertions; -import java.sql.Date; import java.time.Duration; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.UUID; import java.util.concurrent.ConcurrentLinkedQueue; @@ -468,4 +470,139 @@ public void violationReconciliationTest() { assertThat(violation.getPolicyCondition().getPolicy().getName()).isEqualTo("Policy A")); } + @Test + public void violationReconciliationWithDuplicatesTest() { + final var project = new Project(); + project.setName("acme-app"); + qm.persist(project); + + final var componentA = new Component(); + componentA.setProject(project); + componentA.setName("acme-lib-a"); + qm.persist(componentA); + final var componentB = new Component(); + componentB.setProject(project); + componentB.setName("acme-lib-b"); + qm.persist(componentB); + final var componentC = new Component(); + componentC.setProject(project); + componentC.setName("acme-lib-c"); + qm.persist(componentC); + final var componentD = new Component(); + componentD.setProject(project); + componentD.setName("acme-lib-d"); + qm.persist(componentD); + + final var policy = new Policy(); + policy.setName("policy"); + policy.setOperator(Operator.ALL); + policy.setViolationState(ViolationState.INFO); + qm.persist(policy); + + final var policyCondition = new PolicyCondition(); + policyCondition.setPolicy(policy); + policyCondition.setSubject(Subject.COORDINATES); + policyCondition.setOperator(PolicyCondition.Operator.MATCHES); + policyCondition.setValue(""" + {name: "*"} + """); + qm.persist(policyCondition); + + final var violationTimestamp = new Date(); + + // Create two identical violations for component A. + final var policyViolationComponentA = new PolicyViolation(); + policyViolationComponentA.setPolicyCondition(policyCondition); + policyViolationComponentA.setComponent(componentA); + policyViolationComponentA.setTimestamp(violationTimestamp); + policyViolationComponentA.setType(PolicyViolation.Type.OPERATIONAL); + qm.persist(policyViolationComponentA); + final var policyViolationDuplicateComponentA = new PolicyViolation(); + policyViolationDuplicateComponentA.setPolicyCondition(policyCondition); + policyViolationDuplicateComponentA.setComponent(componentA); + policyViolationDuplicateComponentA.setTimestamp(violationTimestamp); + policyViolationDuplicateComponentA.setType(PolicyViolation.Type.OPERATIONAL); + qm.persist(policyViolationDuplicateComponentA); + + // Create two almost identical violations for component B, + // where one of them is older than the other. + final var policyViolationComponentB = new PolicyViolation(); + policyViolationComponentB.setPolicyCondition(policyCondition); + policyViolationComponentB.setComponent(componentB); + policyViolationComponentB.setTimestamp(violationTimestamp); + policyViolationComponentB.setType(PolicyViolation.Type.OPERATIONAL); + qm.persist(policyViolationComponentB); + final var policyViolationDuplicateComponentB = new PolicyViolation(); + policyViolationDuplicateComponentB.setPolicyCondition(policyCondition); + policyViolationDuplicateComponentB.setComponent(componentB); + policyViolationDuplicateComponentB.setTimestamp(Date.from(Instant.now().minus(5, ChronoUnit.MINUTES))); + policyViolationDuplicateComponentB.setType(PolicyViolation.Type.OPERATIONAL); + qm.persist(policyViolationDuplicateComponentB); + + // Create two identical violations for component C. + // Only one of them has an analysis. + final var policyViolationComponentC = new PolicyViolation(); + policyViolationComponentC.setPolicyCondition(policyCondition); + policyViolationComponentC.setComponent(componentC); + policyViolationComponentC.setTimestamp(violationTimestamp); + policyViolationComponentC.setType(PolicyViolation.Type.OPERATIONAL); + qm.persist(policyViolationComponentC); + final var policyViolationDuplicateComponentC = new PolicyViolation(); + policyViolationDuplicateComponentC.setPolicyCondition(policyCondition); + policyViolationDuplicateComponentC.setComponent(componentC); + policyViolationDuplicateComponentC.setTimestamp(violationTimestamp); + policyViolationDuplicateComponentC.setType(PolicyViolation.Type.OPERATIONAL); + qm.persist(policyViolationDuplicateComponentC); + final var violationAnalysisDuplicateComponentC = new ViolationAnalysis(); + violationAnalysisDuplicateComponentC.setPolicyViolation(policyViolationDuplicateComponentC); + violationAnalysisDuplicateComponentC.setComponent(componentC); + violationAnalysisDuplicateComponentC.setViolationAnalysisState(ViolationAnalysisState.APPROVED); + qm.persist(violationAnalysisDuplicateComponentC); + + // Create two identical violations for component D. + // Both have an analysis, but only one of them is suppressed. + final var policyViolationComponentD = new PolicyViolation(); + policyViolationComponentD.setPolicyCondition(policyCondition); + policyViolationComponentD.setComponent(componentD); + policyViolationComponentD.setTimestamp(violationTimestamp); + policyViolationComponentD.setType(PolicyViolation.Type.OPERATIONAL); + qm.persist(policyViolationComponentD); + final var violationAnalysisComponentD = new ViolationAnalysis(); + violationAnalysisComponentD.setPolicyViolation(policyViolationComponentD); + violationAnalysisComponentD.setComponent(componentD); + violationAnalysisComponentD.setViolationAnalysisState(ViolationAnalysisState.REJECTED); + qm.persist(violationAnalysisComponentD); + final var policyViolationDuplicateComponentD = new PolicyViolation(); + policyViolationDuplicateComponentD.setPolicyCondition(policyCondition); + policyViolationDuplicateComponentD.setComponent(componentD); + policyViolationDuplicateComponentD.setTimestamp(violationTimestamp); + policyViolationDuplicateComponentD.setType(PolicyViolation.Type.OPERATIONAL); + qm.persist(policyViolationDuplicateComponentD); + final var violationAnalysisDuplicateComponentD = new ViolationAnalysis(); + violationAnalysisDuplicateComponentD.setPolicyViolation(policyViolationDuplicateComponentD); + violationAnalysisDuplicateComponentD.setComponent(componentD); + violationAnalysisDuplicateComponentD.setViolationAnalysisState(ViolationAnalysisState.REJECTED); + violationAnalysisDuplicateComponentD.setSuppressed(true); + qm.persist(violationAnalysisDuplicateComponentD); + + final var policyEngine = new PolicyEngine(); + policyEngine.evaluate(List.of(componentA, componentB, componentC, componentD)); + + // For component A, the first violation (i.e. lower ID) must be kept. + assertThat(qm.getAllPolicyViolations(componentA, /* includeSuppressed */ true)).satisfiesExactlyInAnyOrder( + violation -> assertThat(violation.getId()).isEqualTo(policyViolationComponentA.getId())); + + // For component B, the older violation must be kept. + assertThat(qm.getAllPolicyViolations(componentB, /* includeSuppressed */ true)).satisfiesExactlyInAnyOrder( + violation -> assertThat(violation.getId()).isEqualTo(policyViolationDuplicateComponentB.getId())); + + // For component C, the violation with analysis must be kept. + assertThat(qm.getAllPolicyViolations(componentC, /* includeSuppressed */ true)).satisfiesExactlyInAnyOrder( + violation -> assertThat(violation.getId()).isEqualTo(policyViolationDuplicateComponentC.getId())); + + // For component D, the suppressed violation must be kept. + assertThat(qm.getAllPolicyViolations(componentD, /* includeSuppressed */ true)).satisfiesExactlyInAnyOrder( + violation -> assertThat(violation.getId()).isEqualTo(policyViolationDuplicateComponentD.getId())); + } + } \ No newline at end of file From 42c9f70e0abb48efd6b903ba33dd4bf8e64e74bf Mon Sep 17 00:00:00 2001 From: nscuro Date: Fri, 11 Oct 2024 19:53:23 +0200 Subject: [PATCH 234/429] Fix `trivy:SrcEpoch` property not being forwarded to Trivy Signed-off-by: nscuro --- .../org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java b/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java index 9ea48954a8..17c853f5a6 100644 --- a/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java +++ b/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java @@ -221,6 +221,7 @@ public void analyze(final List components) { String srcName = null; String srcVersion = null; String srcRelease = null; + Integer srcEpoch = null; String pkgType = component.getPurl().getType(); String arch = null; @@ -251,6 +252,8 @@ public void analyze(final List components) { srcVersion = property.getPropertyValue(); } else if (property.getPropertyName().equals("trivy:SrcRelease")) { srcRelease = property.getPropertyValue(); + } else if (property.getPropertyName().equals("trivy:SrcEpoch")) { + srcEpoch = Integer.parseInt(property.getPropertyValue()); } else if (!pkgType.contains("-") && property.getPropertyName().equals("trivy:PkgType")) { pkgType = property.getPropertyValue(); @@ -278,6 +281,7 @@ public void analyze(final List components) { .setSrcVersion(srcVersion != null ? srcVersion : component.getVersion()); Optional.ofNullable(srcRelease).ifPresent(packageBuilder::setSrcRelease); Optional.ofNullable(epoch).ifPresent(packageBuilder::setEpoch); + Optional.ofNullable(srcEpoch).ifPresent(packageBuilder::setSrcEpoch); pkg.addPackages(packageBuilder); } } From ec8bcba9ded9ebf3284469487c101beef2b7912f Mon Sep 17 00:00:00 2001 From: nscuro Date: Fri, 11 Oct 2024 20:16:02 +0200 Subject: [PATCH 235/429] Use component name and version from PURL for Trivy analysis It turns out that `Component#version` differs from the PURL's version, in that it contains the epoch (e.g. `1:`). Trivy does not expect the epoch as part of the version field, since it has its own dedicated field. For example: ```json { "type": "library", "name": "dbus-common", "version": "1:1.12.20-8.el9", "purl": "pkg:rpm/redhat/dbus-common@1.12.20-8.el9?arch=noarch&distro=redhat-9.4&epoch=1" }, ``` Signed-off-by: nscuro --- .../tasks/scanners/TrivyAnalysisTask.java | 42 +++++++++---------- .../tasks/scanners/TrivyAnalysisTaskTest.java | 2 +- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java b/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java index 17c853f5a6..bf7d7ea7b4 100644 --- a/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java +++ b/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java @@ -60,6 +60,7 @@ import trivy.proto.common.OS; import trivy.proto.common.Package; import trivy.proto.common.PackageInfo; +import trivy.proto.common.PkgIdentifier; import trivy.proto.scanner.v1.Result; import trivy.proto.scanner.v1.ScanOptions; import trivy.proto.scanner.v1.ScanResponse; @@ -191,32 +192,33 @@ public void analyze(final List components) { final var pkgs = new HashMap(); final var apps = new HashMap(); final var os = new HashMap(); - final var map = new HashMap(); + final var componentByPurl = new HashMap(); for (final Component component : components) { if (component.getPurl() != null) { var appType = PurlType.getApp(component.getPurl().getType()); - var name = component.getName(); + var name = component.getPurl().getName(); - if (component.getGroup() != null) { - name = component.getGroup() + ":" + name; + if (component.getPurl().getNamespace() != null) { + name = component.getPurl().getNamespace() + ":" + name; } if (!PurlType.UNKNOWN.getAppType().equals(appType)) { if (!PurlType.Constants.PACKAGES.equals(appType)) { final Application.Builder app = apps.computeIfAbsent(appType, Application.newBuilder()::setType); - final String key = name + ":" + component.getVersion(); + final String key = component.getPurl().toString(); LOGGER.debug("Add key %s to map".formatted(key)); - map.put(key, component); + componentByPurl.put(key, component); LOGGER.debug("add library %s".formatted(component.toString())); app.addPackages(Package.newBuilder() .setName(name) - .setVersion(component.getVersion()) + .setVersion(component.getPurl().getVersion()) .setSrcName(name) - .setSrcVersion(component.getVersion())); + .setSrcVersion(component.getPurl().getVersion()) + .setIdentifier(PkgIdentifier.newBuilder().setPurl(component.getPurl().toString()))); } else { String srcName = null; String srcVersion = null; @@ -226,7 +228,6 @@ public void analyze(final List components) { String pkgType = component.getPurl().getType(); String arch = null; Integer epoch = null; - String versionKey = ""; if (component.getPurl().getQualifiers() != null) { arch = component.getPurl().getQualifiers().get("arch"); @@ -234,7 +235,6 @@ public void analyze(final List components) { String tmpEpoch = component.getPurl().getQualifiers().get("epoch"); if (tmpEpoch != null) { epoch = Integer.parseInt(tmpEpoch); - versionKey = tmpEpoch + ":"; } String distro = component.getPurl().getQualifiers().get("distro"); @@ -267,18 +267,18 @@ public void analyze(final List components) { final PackageInfo.Builder pkg = pkgs.computeIfAbsent(pkgType, ignored -> PackageInfo.newBuilder()); - versionKey += component.getVersion(); - final String key = name + ":" + versionKey; + final String key = component.getPurl().toString(); LOGGER.debug("Add key %s to map".formatted(key)); - map.put(key, component); + componentByPurl.put(key, component); LOGGER.debug("add package %s".formatted(component.toString())); final Package.Builder packageBuilder = Package.newBuilder() - .setName(component.getName()) - .setVersion(component.getVersion()) + .setName(component.getPurl().getName()) + .setVersion(component.getPurl().getVersion()) .setArch(arch != null ? arch : "x86_64") - .setSrcName(srcName != null ? srcName : component.getName()) - .setSrcVersion(srcVersion != null ? srcVersion : component.getVersion()); + .setSrcName(srcName != null ? srcName : component.getPurl().getName()) + .setSrcVersion(srcVersion != null ? srcVersion : component.getPurl().getVersion()) + .setIdentifier(PkgIdentifier.newBuilder().setPurl(component.getPurl().toString())); Optional.ofNullable(srcRelease).ifPresent(packageBuilder::setSrcRelease); Optional.ofNullable(epoch).ifPresent(packageBuilder::setEpoch); Optional.ofNullable(srcEpoch).ifPresent(packageBuilder::setSrcEpoch); @@ -318,7 +318,7 @@ public void analyze(final List components) { try { final var results = analyzeBlob(infos); - handleResults(map, results); + handleResults(componentByPurl, results); } catch (Throwable ex) { handleRequestException(LOGGER, ex); } @@ -338,14 +338,14 @@ public void applyAnalysisFromCache(final Component component) { component.getPurl().getCoordinates(), component, getAnalyzerIdentity(), vulnerabilityAnalysisLevel)); } - private void handleResults(final Map components, final ArrayList input) { + private void handleResults(final Map componentByPurl, final ArrayList input) { for (final Result result : input) { for (int idx = 0; idx < result.getVulnerabilitiesCount(); idx++) { var vulnerability = result.getVulnerabilities(idx); - var key = vulnerability.getPkgName() + ":" + vulnerability.getInstalledVersion(); + var key = vulnerability.getPkgIdentifier().getPurl(); LOGGER.debug("Searching key %s in map".formatted(key)); if (!super.isEnabled(ConfigPropertyConstants.SCANNER_TRIVY_IGNORE_UNFIXED) || vulnerability.getStatus() == 3) { - handle(components.get(key), vulnerability); + handle(componentByPurl.get(key), vulnerability); } } } diff --git a/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java b/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java index 3bdb010338..7908440c77 100644 --- a/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java +++ b/src/test/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTaskTest.java @@ -203,7 +203,7 @@ public void testAnalyzeWithRetry() throws ParseException { .setVulnerabilityId("CVE-2022-40152") .setPkgName("com.fasterxml.woodstox:woodstox-core") .setPkgIdentifier(PkgIdentifier.newBuilder() - .setPurl("pkg:maven/com.fasterxml.woodstox/woodstox-core@5.0.0") + .setPurl("pkg:maven/com.fasterxml.woodstox/woodstox-core@5.0.0?foo=bar#baz") .build()) .setInstalledVersion("5.0.0") .setFixedVersion("6.4.0, 5.4.0") From 8df9bc0a35538ffcf85e50a79eb62768c608790e Mon Sep 17 00:00:00 2001 From: nscuro Date: Fri, 11 Oct 2024 20:21:53 +0200 Subject: [PATCH 236/429] Fix redundant query for "ignore unfixed" config during Trivy analysis Signed-off-by: nscuro # Conflicts: # src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java --- .../dependencytrack/tasks/scanners/TrivyAnalysisTask.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java b/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java index bf7d7ea7b4..51f7587c96 100644 --- a/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java +++ b/src/main/java/org/dependencytrack/tasks/scanners/TrivyAnalysisTask.java @@ -120,6 +120,7 @@ public class TrivyAnalysisTask extends BaseComponentAnalyzerTask implements Cach private String apiBaseUrl; private String apiToken; + private boolean shouldIgnoreUnfixed; private VulnerabilityAnalysisLevel vulnerabilityAnalysisLevel; @Override @@ -151,6 +152,8 @@ public void inform(final Event e) { LOGGER.error("An error occurred decrypting the Trivy API token; Skipping", ex); return; } + + shouldIgnoreUnfixed = qm.isEnabled(ConfigPropertyConstants.SCANNER_TRIVY_IGNORE_UNFIXED); } vulnerabilityAnalysisLevel = event.getVulnerabilityAnalysisLevel(); @@ -344,7 +347,7 @@ private void handleResults(final Map componentByPurl, final A var vulnerability = result.getVulnerabilities(idx); var key = vulnerability.getPkgIdentifier().getPurl(); LOGGER.debug("Searching key %s in map".formatted(key)); - if (!super.isEnabled(ConfigPropertyConstants.SCANNER_TRIVY_IGNORE_UNFIXED) || vulnerability.getStatus() == 3) { + if (!shouldIgnoreUnfixed || vulnerability.getStatus() == 3) { handle(componentByPurl.get(key), vulnerability); } } From 5cfc6e130955e34f8f468ea24ae52eb6823325ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:03:17 +0000 Subject: [PATCH 237/429] Bump actions/checkout from 4.2.0 to 4.2.1 Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.0 to 4.2.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/d632683dd7b4114ad314bca15554477dd762a938...eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 4 ++-- .github/workflows/ci-publish.yaml | 4 ++-- .github/workflows/ci-release.yaml | 6 +++--- .github/workflows/ci-test.yaml | 2 +- .github/workflows/dependency-review.yaml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 04aa07dcf1..38cb6322e9 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # tag=v4.2.1 - name: Set up JDK uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # tag=v4.4.0 @@ -78,7 +78,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # tag=v4.2.1 - name: Download Artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # tag=v4.1.8 diff --git a/.github/workflows/ci-publish.yaml b/.github/workflows/ci-publish.yaml index fb4976852d..1346c5315b 100644 --- a/.github/workflows/ci-publish.yaml +++ b/.github/workflows/ci-publish.yaml @@ -23,7 +23,7 @@ jobs: exit 1 fi - name: Checkout Repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # tag=v4.2.1 - name: Parse Version from POM id: parse @@ -52,7 +52,7 @@ jobs: - call-build steps: - name: Checkout Repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # tag=v4.2.1 - name: Download Artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # tag=v4.1.8 diff --git a/.github/workflows/ci-release.yaml b/.github/workflows/ci-release.yaml index 80e97fb888..6744bc7c18 100644 --- a/.github/workflows/ci-release.yaml +++ b/.github/workflows/ci-release.yaml @@ -20,7 +20,7 @@ jobs: release-branch: ${{ steps.variables.outputs.release-branch }} steps: - name: Checkout Repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # tag=v4.2.1 - name: Setup Environment id: variables @@ -51,7 +51,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # tag=v4.2.1 - name: Set up JDK uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # tag=v4.4.0 @@ -118,7 +118,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # tag=v4.2.1 with: ref: ${{ needs.prepare-release.outputs.release-branch }} diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml index 7d61782238..34289b25e7 100644 --- a/.github/workflows/ci-test.yaml +++ b/.github/workflows/ci-test.yaml @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # tag=v4.2.1 - name: Set up JDK uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # tag=v4.4.0 diff --git a/.github/workflows/dependency-review.yaml b/.github/workflows/dependency-review.yaml index 8c0c9f7f4d..40adfd5ae6 100644 --- a/.github/workflows/dependency-review.yaml +++ b/.github/workflows/dependency-review.yaml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # tag=v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # tag=v4.2.1 - name: Dependency Review uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # tag=v4.3.4 From b238dea9fb6f522598defc6eb9a285caf2ae86c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:03:22 +0000 Subject: [PATCH 238/429] Bump actions/upload-artifact from 4.4.0 to 4.4.3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.0 to 4.4.3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/50769540e7f4bd5e21e526ee35c689e35e0d6874...b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- .github/workflows/ci-test.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 04aa07dcf1..129ebc804f 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -55,7 +55,7 @@ jobs: mvn -B --no-transfer-progress cyclonedx:makeBom -Dservices.bom.merge.skip=false org.codehaus.mojo:exec-maven-plugin:exec@merge-services-bom - name: Upload Artifacts - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # tag=v4.4.0 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # tag=v4.4.3 with: name: assembled-wars path: |- diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml index 7d61782238..34aab90de0 100644 --- a/.github/workflows/ci-test.yaml +++ b/.github/workflows/ci-test.yaml @@ -66,7 +66,7 @@ jobs: - name: Upload PR test coverage report if: ${{ github.event_name == 'pull_request' }} - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # tag=v4.4.0 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # tag=v4.4.3 with: name: pr-test-coverage-report path: |- From efef55c8564297d2099d47265e3d2fc4f823cf06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:03:30 +0000 Subject: [PATCH 239/429] Bump github/codeql-action from 3.26.11 to 3.26.12 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.11 to 3.26.12. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea...c36620d31ac7c881962c3d9dd939c40ec9434f2b) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 04aa07dcf1..14dd4bd410 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -149,6 +149,6 @@ jobs: - name: Upload Trivy Scan Results to GitHub Security Tab if: ${{ inputs.publish-container }} - uses: github/codeql-action/upload-sarif@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # tag=v3.26.11 + uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b # tag=v3.26.12 with: sarif_file: 'trivy-results.sarif' From 2be2a4e80698f4e75c2c905d94b9d54d59db35ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:03:36 +0000 Subject: [PATCH 240/429] Bump aquasecurity/trivy-action from 0.24.0 to 0.27.0 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.24.0 to 0.27.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8...5681af892cd0f4997658e2bacc62bd0a894cf564) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 04aa07dcf1..13be3ecede 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -135,7 +135,7 @@ jobs: - name: Run Trivy Vulnerability Scanner if: ${{ inputs.publish-container }} - uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # tag=0.24.0 + uses: aquasecurity/trivy-action@5681af892cd0f4997658e2bacc62bd0a894cf564 # tag=0.27.0 env: # https://github.com/aquasecurity/trivy-action/issues/389 TRIVY_DB_REPOSITORY: "public.ecr.aws/aquasecurity/trivy-db:2" From 6a3ffeb5bf6c81d683c1def0c1dd875c8b6aec55 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 08:37:12 +0000 Subject: [PATCH 241/429] Bump debian from `939e69e` to `fffe160` in /src/main/docker Bumps debian from `939e69e` to `fffe160`. --- updated-dependencies: - dependency-name: debian dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/main/docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile index 27b92d41d7..df48bf86d4 100644 --- a/src/main/docker/Dockerfile +++ b/src/main/docker/Dockerfile @@ -1,6 +1,6 @@ FROM eclipse-temurin:21.0.4_7-jre-jammy@sha256:870aae69d4521fdaf26e952f8026f75b37cb721e6302d4d4d7100f6b09823057 AS jre-build -FROM debian:stable-slim@sha256:939e69ef5aa4dc178893a718ea567f1ca390df60793fd08c0bc7008362f72a57 +FROM debian:stable-slim@sha256:fffe16098bcefa876d01862a61f8f30ef4292c9485940e905d41a15d8459828b # Arguments that can be passed at build time # Directory names must end with / to avoid errors when ADDing and COPYing From 491b854aa267f39997da9f91a4eadd579ba0b90b Mon Sep 17 00:00:00 2001 From: nscuro Date: Thu, 17 Oct 2024 15:50:56 +0200 Subject: [PATCH 242/429] Bump cyclonedx-core-java to 9.1.0 Fixes #4144 Signed-off-by: nscuro --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a0376427d6..9bc2dcd87f 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ 1.12.0 1.4.2 1.0.1 - 9.0.5 + 9.1.0 2.1.0 2.17.2 2.17.2 From 81e5975d93c1932e81a1e45b29252b33e5244b9b Mon Sep 17 00:00:00 2001 From: nscuro Date: Thu, 17 Oct 2024 16:18:07 +0200 Subject: [PATCH 243/429] Bump Jackson to 2.18.0 for cyclonedx-core-java 9.1.0 compatibility Signed-off-by: nscuro --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9bc2dcd87f..32dd55e523 100644 --- a/pom.xml +++ b/pom.xml @@ -100,8 +100,8 @@ 1.0.1 9.1.0 2.1.0 - 2.17.2 - 2.17.2 + 2.18.0 + 2.18.0 20240303 3.4.1 4.13.2 From 2fe140159ae2ebfdd771059195c98a92adc622cb Mon Sep 17 00:00:00 2001 From: Dani Hengeveld Date: Fri, 18 Oct 2024 11:54:42 +0200 Subject: [PATCH 244/429] Update Deploying Docker guide to Compose v2 - Added `ALPINE_OIDC_CLIENT_ID` environment variable to example. - Removed `docker-compose.yml` version top-level element (obsolote). Signed-off-by: Dani Hengeveld --- docs/_docs/getting-started/deploy-docker.md | 12 +++++++----- src/main/docker/docker-compose.yml | 2 -- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/_docs/getting-started/deploy-docker.md b/docs/_docs/getting-started/deploy-docker.md index 8667bcdb7e..78e994569d 100755 --- a/docs/_docs/getting-started/deploy-docker.md +++ b/docs/_docs/getting-started/deploy-docker.md @@ -18,7 +18,7 @@ other than a modern version of Docker. | 4.5GB RAM | 16GB RAM | | 2 CPU cores | 4 CPU cores | -> These requirements can be disabled by setting the 'system.requirement.check.enabled' property or the 'SYSTEM_REQUIREMENT_CHECK_ENABLED' environment variable to 'false'. +> These requirements can be disabled by setting the 'system.requirement.check.enabled' property or the 'SYSTEM_REQUIREMENT_CHECK_ENABLED' environment variable to 'false'. ### Container Requirements (Front End) @@ -29,12 +29,15 @@ other than a modern version of Docker. ### Quickstart (Docker Compose) +> The easiest way to use Docker Compose is by installing Docker Desktop, since Compose comes bundled as a plugin. +> See the official [Docker Compose installation guide](https://docs.docker.com/compose/install/) for alternative installation methods. + ```bash # Downloads the latest Docker Compose file curl -LO https://dependencytrack.org/docker-compose.yml # Starts the stack using Docker Compose -docker-compose up -d +docker compose up -d ``` ### Quickstart (Docker Swarm) @@ -69,11 +72,9 @@ docker run -d -m 8192m -p 8080:8080 --name dependency-track -v dependency-track: The preferred method for production environments is to use docker-compose.yml with a corresponding database container (Postgres, MySQL, or Microsoft SQL). The following is an example YAML file that -can be used with `docker-compose` or `docker stack deploy`. +can be used with `docker compose` or `docker stack deploy`. ```yaml -version: '3.7' - ##################################################### # This Docker Compose file contains two services # Dependency-Track API Server @@ -123,6 +124,7 @@ services: # # Optional OpenID Connect (OIDC) Properties # - ALPINE_OIDC_ENABLED=true + # - ALPINE_OIDC_CLIENT_ID= # - ALPINE_OIDC_ISSUER=https://auth.example.com/auth/realms/example # - ALPINE_OIDC_USERNAME_CLAIM=preferred_username # - ALPINE_OIDC_TEAMS_CLAIM=groups diff --git a/src/main/docker/docker-compose.yml b/src/main/docker/docker-compose.yml index b1d298f266..2e81967b1f 100644 --- a/src/main/docker/docker-compose.yml +++ b/src/main/docker/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.7' - ##################################################### # This Docker Compose file contains two services # Dependency-Track API Server From b488ceaa2dfe974e3d7c92cf19f9e74746997ca1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 08:17:07 +0000 Subject: [PATCH 245/429] Bump aquasecurity/trivy-action from 0.27.0 to 0.28.0 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.27.0 to 0.28.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/5681af892cd0f4997658e2bacc62bd0a894cf564...915b19bbe73b92a6cf82a1bc12b087c9a19a5fe2) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 89fa98c6ba..bb3c8c288e 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -135,7 +135,7 @@ jobs: - name: Run Trivy Vulnerability Scanner if: ${{ inputs.publish-container }} - uses: aquasecurity/trivy-action@5681af892cd0f4997658e2bacc62bd0a894cf564 # tag=0.27.0 + uses: aquasecurity/trivy-action@915b19bbe73b92a6cf82a1bc12b087c9a19a5fe2 # tag=0.28.0 env: # https://github.com/aquasecurity/trivy-action/issues/389 TRIVY_DB_REPOSITORY: "public.ecr.aws/aquasecurity/trivy-db:2" From aa3390df136c5b08069b1098dcb14d6aba46918c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 08:17:23 +0000 Subject: [PATCH 246/429] Bump github/codeql-action from 3.26.12 to 3.26.13 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.12 to 3.26.13. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/c36620d31ac7c881962c3d9dd939c40ec9434f2b...f779452ac5af1c261dce0346a8f964149f49322b) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/_meta-build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_meta-build.yaml b/.github/workflows/_meta-build.yaml index 89fa98c6ba..fcef2abdb5 100644 --- a/.github/workflows/_meta-build.yaml +++ b/.github/workflows/_meta-build.yaml @@ -149,6 +149,6 @@ jobs: - name: Upload Trivy Scan Results to GitHub Security Tab if: ${{ inputs.publish-container }} - uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b # tag=v3.26.12 + uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # tag=v3.26.13 with: sarif_file: 'trivy-results.sarif' From bbcbc6c877d507914484f7ff9357a8dc239b0cbf Mon Sep 17 00:00:00 2001 From: SudoHenk <12190988+SudoHenk@users.noreply.github.com> Date: Mon, 21 Oct 2024 16:46:55 +0200 Subject: [PATCH 247/429] Added Dutch tax office as adopter Signed-off-by: SudoHenk <12190988+SudoHenk@users.noreply.github.com> --- ADOPTERS.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index cdfd7be351..7a728eeed9 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -1,6 +1,6 @@ # Adopters - + This is a list of organizations that have spoken publicly about their adoption or @@ -21,6 +21,14 @@ pre-production (in alphabetical order): * [Apex Fintech Solutions](https://apexfintechsolutions.com/) has integrated OWASP Dependency-Track into their CI/CD pipeline as part of the DevSecOps program. This integration allows for the upload of SBOMs (Software Bill of Materials) to the platform for comprehensive component analysis and a detailed understanding of the software inventory used in software applications. By analyzing the components in our monorepo, we enhance our vulnerability management program and gain valuable insights into transitive dependencies, which traditional SCA (Software Composition Analysis) tools often overlook. +* [Dutch Tax Office - Belastingdienst](https://www.belastingdienst.nl/) has integrated OWASP Dependency-Track into their +development processes as part of the DevSecOps program. We integrate Dependency-Track +with various platforms and programming languages to gain vulnerability insights in +our internally developed software. + +We want to thank all contributors of Dependency-Track creating a resilient and +extensible SCA tool. Especially the API is a huge asset to integrate +within the current organization processes. If you have adopted OWASP Depenency Track and would like to be included in this list, feel free to submit a PR updating this file or From ca87f0ede8c1fdc7c2d6186765eb0570d6e492e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 08:57:53 +0000 Subject: [PATCH 248/429] Bump org.codehaus.mojo:exec-maven-plugin from 3.4.1 to 3.5.0 Bumps [org.codehaus.mojo:exec-maven-plugin](https://github.com/mojohaus/exec-maven-plugin) from 3.4.1 to 3.5.0. - [Release notes](https://github.com/mojohaus/exec-maven-plugin/releases) - [Commits](https://github.com/mojohaus/exec-maven-plugin/compare/3.4.1...3.5.0) --- updated-dependencies: - dependency-name: org.codehaus.mojo:exec-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 32dd55e523..dabee12823 100644 --- a/pom.xml +++ b/pom.xml @@ -679,7 +679,7 @@ org.codehaus.mojo exec-maven-plugin - 3.4.1 + 3.5.0 merge-services-bom From 06d60e84a4423ffb0fcdfe85bf75cce20d6f1c2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Oct 2024 08:51:49 +0000 Subject: [PATCH 249/429] Bump org.testcontainers:testcontainers from 1.20.2 to 1.20.3 Bumps [org.testcontainers:testcontainers](https://github.com/testcontainers/testcontainers-java) from 1.20.2 to 1.20.3. - [Release notes](https://github.com/testcontainers/testcontainers-java/releases) - [Changelog](https://github.com/testcontainers/testcontainers-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/testcontainers/testcontainers-java/compare/1.20.2...1.20.3) --- updated-dependencies: - dependency-name: org.testcontainers:testcontainers dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dabee12823..d9b3d64122 100644 --- a/pom.xml +++ b/pom.xml @@ -115,7 +115,7 @@ 2.2.0 2.1.22 1.19.0 - 1.20.2 + 1.20.3 2.35.2 7.0.0 1.1.1 From ef3db4225837de1198ff400a68b859968b924063 Mon Sep 17 00:00:00 2001 From: nscuro Date: Wed, 23 Oct 2024 15:36:50 +0200 Subject: [PATCH 250/429] Add World Kinect to `ADOPTERS.md` Closes #3810 Signed-off-by: nscuro --- ADOPTERS.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index 7a728eeed9..379fda653e 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -6,9 +6,8 @@ This is a list of organizations that have spoken publicly about their adoption or production users that have added themselves (in alphabetical order): -* [Coming Soon] - - +* [World Kinect Corporation](https://world-kinect.com/) uses Dependency-Track to continuously identify software supply chain risks and to enforce policy compliance across the portfolio. + World Kinect's usage of Dependency-Track was [showcased in the community meeting of May 2024](https://www.youtube.com/watch?v=MS2DlMdUI7Q&t=1320s). This is a list of adopters in early stages of production or pre-production (in alphabetical order): @@ -24,10 +23,8 @@ pre-production (in alphabetical order): * [Dutch Tax Office - Belastingdienst](https://www.belastingdienst.nl/) has integrated OWASP Dependency-Track into their development processes as part of the DevSecOps program. We integrate Dependency-Track with various platforms and programming languages to gain vulnerability insights in -our internally developed software. - -We want to thank all contributors of Dependency-Track creating a resilient and -extensible SCA tool. Especially the API is a huge asset to integrate +our internally developed software. We want to thank all contributors of Dependency-Track creating a resilient and +extensible SCA tool. Especially the API is a huge asset to integrate within the current organization processes. If you have adopted OWASP Depenency Track and would like to be included in this list, From 57796648ff6c71ccd874bccf0b062db6bfcec520 Mon Sep 17 00:00:00 2001 From: nscuro Date: Wed, 23 Oct 2024 22:37:35 +0200 Subject: [PATCH 251/429] Add Rohde & Schwarz to `ADOPTERS.md` Closes #4295 Signed-off-by: nscuro --- ADOPTERS.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ADOPTERS.md b/ADOPTERS.md index 379fda653e..67d247a4a0 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -6,6 +6,11 @@ This is a list of organizations that have spoken publicly about their adoption or production users that have added themselves (in alphabetical order): +* [Rohde & Schwarz](https://www.rohde-schwarz.com/): At Rohde & Schwarz, we are deeply committed to ensuring the +cybersecurity of our products, systems, and solutions. As part of our comprehensive security strategy, +we utilize a diverse set of tools to safeguard our technology. We value Dependency-Track for its scalability, +adherence to open standards, and active community. Additionally, we actively contribute to the development of +Dependency-Track by adding features, improving its usability for large organizations, and strengthening its security posture. * [World Kinect Corporation](https://world-kinect.com/) uses Dependency-Track to continuously identify software supply chain risks and to enforce policy compliance across the portfolio. World Kinect's usage of Dependency-Track was [showcased in the community meeting of May 2024](https://www.youtube.com/watch?v=MS2DlMdUI7Q&t=1320s). From 4e48c57b82d7ca71ddbc9d7a7265788ab32bb848 Mon Sep 17 00:00:00 2001 From: nscuro Date: Wed, 23 Oct 2024 22:57:00 +0200 Subject: [PATCH 252/429] Format `ADOPTERS.md` as table Also list the contact(s) who requested or contributed their organization's entry. Signed-off-by: nscuro --- ADOPTERS.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index 67d247a4a0..5b94b540d2 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -6,32 +6,32 @@ This is a list of organizations that have spoken publicly about their adoption or production users that have added themselves (in alphabetical order): -* [Rohde & Schwarz](https://www.rohde-schwarz.com/): At Rohde & Schwarz, we are deeply committed to ensuring the -cybersecurity of our products, systems, and solutions. As part of our comprehensive security strategy, -we utilize a diverse set of tools to safeguard our technology. We value Dependency-Track for its scalability, -adherence to open standards, and active community. Additionally, we actively contribute to the development of -Dependency-Track by adding features, improving its usability for large organizations, and strengthening its security posture. -* [World Kinect Corporation](https://world-kinect.com/) uses Dependency-Track to continuously identify software supply chain risks and to enforce policy compliance across the portfolio. - World Kinect's usage of Dependency-Track was [showcased in the community meeting of May 2024](https://www.youtube.com/watch?v=MS2DlMdUI7Q&t=1320s). +| Organization | Contact | Description | +|:----------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Rohde & Schwarz] | [@lukas-braune] | At Rohde & Schwarz, we are deeply committed to ensuring the cybersecurity of our products, systems, and solutions. As part of our comprehensive security strategy, we utilize a diverse set of tools to safeguard our technology. We value Dependency-Track for its scalability, adherence to open standards, and active community. Additionally, we actively contribute to the development of Dependency-Track by adding features, improving its usability for large organizations, and strengthening its security posture. | +| [World Kinect Corporation] | [@aravindparappil46], [@setchy] | World Kinect Corporation (NYSE: WKC) uses Dependency-Track to continuously identify software supply chain risks and to enforce policy compliance across the portfolio. Its usage of Dependency-Track was [showcased in the community meeting of May 2024](https://www.youtube.com/watch?v=MS2DlMdUI7Q&t=1320s). | This is a list of adopters in early stages of production or pre-production (in alphabetical order): -* [Air France-KLM](https://www.airfranceklm.com/) has always been highly vigilant and profoundly committed to the realm of IT security. We use a variety of tools to ensure our systems' safety, one of which is the OWASP Dependency Track. This tool forms a crucial part of our vulnerability detection systems, scanning the Software Bill of Materials (SBOM) for each application and sending it to our in-house DT instance. With over 10,000 projects undergoing daily scans, our security measures are both comprehensive and rigorous. - - The Dependency Track API is not only highly configurable but also user-friendly, boasting a visually appealing user interface. The project is in a constant state of evolution, adapting and improving to meet the ever-changing landscape of IT security. The community of DT contributors is always ready to lend a hand when issues arise, making it not just an effective tool, but also a pleasure to work with as a developer. - - We extend our gratitude to the team behind the OWASP Dependency Track for their excellent work. We look forward to welcoming you aboard our flights soon! - -* [Apex Fintech Solutions](https://apexfintechsolutions.com/) has integrated OWASP Dependency-Track into their CI/CD pipeline as part of the DevSecOps program. This integration allows for the upload of SBOMs (Software Bill of Materials) to the platform for comprehensive component analysis and a detailed understanding of the software inventory used in software applications. By analyzing the components in our monorepo, we enhance our vulnerability management program and gain valuable insights into transitive dependencies, which traditional SCA (Software Composition Analysis) tools often overlook. - -* [Dutch Tax Office - Belastingdienst](https://www.belastingdienst.nl/) has integrated OWASP Dependency-Track into their -development processes as part of the DevSecOps program. We integrate Dependency-Track -with various platforms and programming languages to gain vulnerability insights in -our internally developed software. We want to thank all contributors of Dependency-Track creating a resilient and -extensible SCA tool. Especially the API is a huge asset to integrate -within the current organization processes. +| Organization | Contact | Description | +|:-------------------------------------|:---------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Air France-KLM] | [@nekhtan] | Air France-KLM has always been highly vigilant and profoundly committed to the realm of IT security. We use a variety of tools to ensure our systems' safety, one of which is the OWASP Dependency Track. This tool forms a crucial part of our vulnerability detection systems, scanning the Software Bill of Materials (SBOM) for each application and sending it to our in-house DT instance. With over 10,000 projects undergoing daily scans, our security measures are both comprehensive and rigorous. The Dependency Track API is not only highly configurable but also user-friendly, boasting a visually appealing user interface. The project is in a constant state of evolution, adapting and improving to meet the ever-changing landscape of IT security. The community of DT contributors is always ready to lend a hand when issues arise, making it not just an effective tool, but also a pleasure to work with as a developer. We extend our gratitude to the team behind the OWASP Dependency Track for their excellent work. We look forward to welcoming you aboard our flights soon! | +| [Apex Fintech Solutions] | [@spawar-apex] | Apex Fintech Solutions has integrated OWASP Dependency-Track into their CI/CD pipeline as part of the DevSecOps program. This integration allows for the upload of SBOMs (Software Bill of Materials) to the platform for comprehensive component analysis and a detailed understanding of the software inventory used in software applications. By analyzing the components in our monorepo, we enhance our vulnerability management program and gain valuable insights into transitive dependencies, which traditional SCA (Software Composition Analysis) tools often overlook. | +| [Dutch Tax Office - Belastingdienst] | [@SudoHenk] | Dutch Tax Office has integrated OWASP Dependency-Track into their development processes as part of the DevSecOps program. We integrate Dependency-Track with various platforms and programming languages to gain vulnerability insights in our internally developed software. We want to thank all contributors of Dependency-Track creating a resilient and extensible SCA tool. Especially the API is a huge asset to integrate within the current organization processes. | If you have adopted OWASP Depenency Track and would like to be included in this list, feel free to submit a PR updating this file or [open an issue](https://github.com/). + +[@SudoHenk]: https://github.com/SudoHenk +[@aravindparappil46]: https://github.com/aravindparappil46 +[@lukas-braune]: https://github.com/lukas-braune +[@nekhtan]: https://github.com/nekhtan +[@setchy]: https://github.com/setchy +[@spawar-apex]: https://github.com/spawar-apex +[Air France-KLM]: https://www.airfranceklm.com/ +[Apex Fintech Solutions]: https://apexfintechsolutions.com/ +[Dutch Tax Office - Belastingdienst]: https://www.belastingdienst.nl/ +[Rohde & Schwarz]: https://www.rohde-schwarz.com/ +[World Kinect Corporation]: https://world-kinect.com/ \ No newline at end of file From bdb8c8726816d8532b759bc2ce71e555220913f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 08:07:10 +0000 Subject: [PATCH 253/429] Bump lib.protobuf-java.version from 4.28.2 to 4.28.3 Bumps `lib.protobuf-java.version` from 4.28.2 to 4.28.3. Updates `com.google.protobuf:protobuf-java` from 4.28.2 to 4.28.3 - [Release notes](https://github.com/protocolbuffers/protobuf/releases) - [Changelog](https://github.com/protocolbuffers/protobuf/blob/main/protobuf_release.bzl) - [Commits](https://github.com/protocolbuffers/protobuf/commits) Updates `com.google.protobuf:protobuf-java-util` from 4.28.2 to 4.28.3 --- updated-dependencies: - dependency-name: com.google.protobuf:protobuf-java dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.google.protobuf:protobuf-java-util dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dabee12823..1bfe0361d9 100644 --- a/pom.xml +++ b/pom.xml @@ -111,7 +111,7 @@ 7.0.0 1.5.0 3.2.2 - 4.28.2 + 4.28.3 2.2.0 2.1.22 1.19.0 From 8fdab0edd6c5a39bcb386c948e0c55da032af0cd Mon Sep 17 00:00:00 2001 From: Vinod Anandan Date: Thu, 24 Oct 2024 16:54:11 +0100 Subject: [PATCH 254/429] Support AWS JDBC Driver Signed-off-by: Vinod Anandan --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index 620cfc97ab..3555889a54 100644 --- a/pom.xml +++ b/pom.xml @@ -90,6 +90,7 @@ 4.2.2 0.1.2 10.18.1 + 2.5.0 1.20.1 1.20.1 1.20.1 @@ -349,6 +350,11 @@ postgresql ${lib.jdbc-driver.postgresql.version} + + software.amazon.jdbc + aws-advanced-jdbc-wrapper + ${lib.aws-advanced-jdbc-wrapper.version} + com.google.cloud.sql mysql-socket-factory-connector-j-8 From 7a3baa366f05736878f093db1bad2396261619c0 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Mon, 29 Apr 2024 17:13:29 +0200 Subject: [PATCH 255/429] added javacron dependency Signed-off-by: Max Schiller --- pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pom.xml b/pom.xml index 3555889a54..6bcfaee7d1 100644 --- a/pom.xml +++ b/pom.xml @@ -125,6 +125,7 @@ 5.4 2.0.16 1.323 + 1.4.0 12.8.1.jre11 8.2.0 @@ -411,6 +412,12 @@ ${lib.org-kohsuke-github-api.version} + + com.asahaf.javacron + javacron + ${lib.com-asahaf-javacron.version} + + junit From aa1eae94bd45a7ff14f8114a9628f1ad974b7d4d Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Mon, 29 Apr 2024 17:23:59 +0200 Subject: [PATCH 256/429] added scheduled properties in NotificationRule, added configurable default cron interval Signed-off-by: Max Schiller --- .../model/ConfigPropertyConstants.java | 1 + .../model/NotificationRule.java | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java index db989d7489..64c2912231 100644 --- a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java +++ b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java @@ -98,6 +98,7 @@ public enum ConfigPropertyConstants { ACCESS_MANAGEMENT_ACL_ENABLED("access-management", "acl.enabled", "false", PropertyType.BOOLEAN, "Flag to enable/disable access control to projects in the portfolio", true), NOTIFICATION_TEMPLATE_BASE_DIR("notification", "template.baseDir", SystemUtils.getEnvironmentVariable("DEFAULT_TEMPLATES_OVERRIDE_BASE_DIRECTORY", System.getProperty("user.home")), PropertyType.STRING, "The base directory to use when searching for notification templates"), NOTIFICATION_TEMPLATE_DEFAULT_OVERRIDE_ENABLED("notification", "template.default.override.enabled", SystemUtils.getEnvironmentVariable("DEFAULT_TEMPLATES_OVERRIDE_ENABLED", "false"), PropertyType.BOOLEAN, "Flag to enable/disable override of default notification templates"), + NOTIFICATION_CRON_DEFAULT_INTERVAL("notification", "cron.default.interval", "0 12 * * *", PropertyType.STRING, "The default interval of scheduled notifications as cron expression (every day at 12pm)"), TASK_SCHEDULER_LDAP_SYNC_CADENCE("task-scheduler", "ldap.sync.cadence", "6", PropertyType.INTEGER, "Sync cadence (in hours) for LDAP"), TASK_SCHEDULER_GHSA_MIRROR_CADENCE("task-scheduler", "ghsa.mirror.cadence", "24", PropertyType.INTEGER, "Mirror cadence (in hours) for Github Security Advisories"), TASK_SCHEDULER_OSV_MIRROR_CADENCE("task-scheduler", "osv.mirror.cadence", "24", PropertyType.INTEGER, "Mirror cadence (in hours) for OSV database"), diff --git a/src/main/java/org/dependencytrack/model/NotificationRule.java b/src/main/java/org/dependencytrack/model/NotificationRule.java index 9fdad4c536..cae9b984af 100644 --- a/src/main/java/org/dependencytrack/model/NotificationRule.java +++ b/src/main/java/org/dependencytrack/model/NotificationRule.java @@ -22,6 +22,9 @@ import alpine.model.Team; import alpine.notification.NotificationLevel; import alpine.server.json.TrimmedStringDeserializer; + +import com.asahaf.javacron.InvalidExpressionException; +import com.asahaf.javacron.Schedule; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; @@ -45,6 +48,7 @@ import javax.jdo.annotations.PrimaryKey; import javax.jdo.annotations.Unique; import java.io.Serializable; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -154,6 +158,20 @@ public class NotificationRule implements Serializable { @NotNull private UUID uuid; + @Persistent(defaultFetchGroup = "true") + @Column(name = "CRON_CONFIG", allowsNull = "true") // new column, must allow nulls on existing databases + @JsonDeserialize(using = TrimmedStringDeserializer.class) + // @Pattern(regexp = RegexSequence.Definition.CRON, message = "The message may only contain characters valid in cron strings") + private String cronConfig; + + @Persistent(defaultFetchGroup = "true") + @Column(name = "LAST_EXECUTION_TIME", allowsNull = "true") // new column, must allow nulls on existing databases + private ZonedDateTime lastExecutionTime; + + @Persistent + @Column(name = "PUBLISH_ONLY_WITH_UPDATES", allowsNull = "true") // new column, must allow nulls on existing databases + private boolean publishOnlyWithUpdates; + public long getId() { return id; } @@ -296,4 +314,36 @@ public UUID getUuid() { public void setUuid(@NotNull UUID uuid) { this.uuid = uuid; } + + public Schedule getCronConfig() throws InvalidExpressionException { + var cronSchedule = Schedule.create(ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue()); + if (this.cronConfig != null) { + cronSchedule = Schedule.create(this.cronConfig); + } + return cronSchedule; + } + + public void setCronConfig(Schedule cronSchedule) { + if (cronSchedule == null) { + this.cronConfig = ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue(); + return; + } + this.cronConfig = cronSchedule.getExpression(); + } + + public ZonedDateTime getLastExecutionTime() { + return lastExecutionTime; + } + + public void setLastExecutionTime(ZonedDateTime lastExecutionTime) { + this.lastExecutionTime = lastExecutionTime; + } + + public boolean getPublishOnlyWithUpdates() { + return publishOnlyWithUpdates; + } + + public void setPublishOnlyWithUpdates(boolean publishOnlyWithUpdates) { + this.publishOnlyWithUpdates = publishOnlyWithUpdates; + } } From 17b2fdf455ca2e4fe78cfd9ddc857041adb53c67 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Mon, 29 Apr 2024 18:00:17 +0200 Subject: [PATCH 257/429] changed type of cron configuration to string Signed-off-by: Max Schiller --- .../dependencytrack/model/NotificationRule.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/NotificationRule.java b/src/main/java/org/dependencytrack/model/NotificationRule.java index cae9b984af..02e26dc75f 100644 --- a/src/main/java/org/dependencytrack/model/NotificationRule.java +++ b/src/main/java/org/dependencytrack/model/NotificationRule.java @@ -23,8 +23,6 @@ import alpine.notification.NotificationLevel; import alpine.server.json.TrimmedStringDeserializer; -import com.asahaf.javacron.InvalidExpressionException; -import com.asahaf.javacron.Schedule; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; @@ -315,20 +313,20 @@ public void setUuid(@NotNull UUID uuid) { this.uuid = uuid; } - public Schedule getCronConfig() throws InvalidExpressionException { - var cronSchedule = Schedule.create(ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue()); + public String getCronConfig() { + var cronConfig = ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue(); if (this.cronConfig != null) { - cronSchedule = Schedule.create(this.cronConfig); + cronConfig = this.cronConfig; } - return cronSchedule; + return cronConfig; } - public void setCronConfig(Schedule cronSchedule) { - if (cronSchedule == null) { + public void setCronConfig(String cronConfig) { + if (cronConfig == null) { this.cronConfig = ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue(); return; } - this.cronConfig = cronSchedule.getExpression(); + this.cronConfig = cronConfig; } public ZonedDateTime getLastExecutionTime() { From 5dc97086d2bef04e767b3a4efbf3fbe9d0c5c525 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Mon, 29 Apr 2024 18:00:46 +0200 Subject: [PATCH 258/429] added fallback for last execution time if not set Signed-off-by: Max Schiller --- src/main/java/org/dependencytrack/model/NotificationRule.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/dependencytrack/model/NotificationRule.java b/src/main/java/org/dependencytrack/model/NotificationRule.java index 02e26dc75f..71254c34e4 100644 --- a/src/main/java/org/dependencytrack/model/NotificationRule.java +++ b/src/main/java/org/dependencytrack/model/NotificationRule.java @@ -330,6 +330,9 @@ public void setCronConfig(String cronConfig) { } public ZonedDateTime getLastExecutionTime() { + if (lastExecutionTime == null) { + return ZonedDateTime.now(); + } return lastExecutionTime; } From b5a6e46496cf23eee1d3f91931db4d96cccd640b Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Thu, 2 May 2024 17:12:40 +0200 Subject: [PATCH 259/429] moved scheduled properties from NotificationRule to new class Signed-off-by: Max Schiller --- .../model/NotificationRule.java | 50 ----------- .../model/ScheduledNotificationRule.java | 88 +++++++++++++++++++ 2 files changed, 88 insertions(+), 50 deletions(-) create mode 100644 src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java diff --git a/src/main/java/org/dependencytrack/model/NotificationRule.java b/src/main/java/org/dependencytrack/model/NotificationRule.java index 71254c34e4..8b82d7efb0 100644 --- a/src/main/java/org/dependencytrack/model/NotificationRule.java +++ b/src/main/java/org/dependencytrack/model/NotificationRule.java @@ -46,7 +46,6 @@ import javax.jdo.annotations.PrimaryKey; import javax.jdo.annotations.Unique; import java.io.Serializable; -import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -156,20 +155,6 @@ public class NotificationRule implements Serializable { @NotNull private UUID uuid; - @Persistent(defaultFetchGroup = "true") - @Column(name = "CRON_CONFIG", allowsNull = "true") // new column, must allow nulls on existing databases - @JsonDeserialize(using = TrimmedStringDeserializer.class) - // @Pattern(regexp = RegexSequence.Definition.CRON, message = "The message may only contain characters valid in cron strings") - private String cronConfig; - - @Persistent(defaultFetchGroup = "true") - @Column(name = "LAST_EXECUTION_TIME", allowsNull = "true") // new column, must allow nulls on existing databases - private ZonedDateTime lastExecutionTime; - - @Persistent - @Column(name = "PUBLISH_ONLY_WITH_UPDATES", allowsNull = "true") // new column, must allow nulls on existing databases - private boolean publishOnlyWithUpdates; - public long getId() { return id; } @@ -312,39 +297,4 @@ public UUID getUuid() { public void setUuid(@NotNull UUID uuid) { this.uuid = uuid; } - - public String getCronConfig() { - var cronConfig = ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue(); - if (this.cronConfig != null) { - cronConfig = this.cronConfig; - } - return cronConfig; - } - - public void setCronConfig(String cronConfig) { - if (cronConfig == null) { - this.cronConfig = ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue(); - return; - } - this.cronConfig = cronConfig; - } - - public ZonedDateTime getLastExecutionTime() { - if (lastExecutionTime == null) { - return ZonedDateTime.now(); - } - return lastExecutionTime; - } - - public void setLastExecutionTime(ZonedDateTime lastExecutionTime) { - this.lastExecutionTime = lastExecutionTime; - } - - public boolean getPublishOnlyWithUpdates() { - return publishOnlyWithUpdates; - } - - public void setPublishOnlyWithUpdates(boolean publishOnlyWithUpdates) { - this.publishOnlyWithUpdates = publishOnlyWithUpdates; - } } diff --git a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java new file mode 100644 index 0000000000..a826d62e46 --- /dev/null +++ b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java @@ -0,0 +1,88 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.model; + +import alpine.server.json.TrimmedStringDeserializer; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import javax.jdo.annotations.Column; +import javax.jdo.annotations.PersistenceCapable; +import javax.jdo.annotations.Persistent; +import java.time.ZonedDateTime; + +/** + * Defines a Model class for scheduled notification configurations. + * + * @author Max Schiller + */ +@PersistenceCapable +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class ScheduledNotificationRule extends NotificationRule { + @Persistent(defaultFetchGroup = "true") + @Column(name = "CRON_CONFIG", allowsNull = "true") // new column, must allow nulls on existing databases + @JsonDeserialize(using = TrimmedStringDeserializer.class) + // @Pattern(regexp = RegexSequence.Definition.CRON, message = "The message may only contain characters valid in cron strings") + private String cronConfig; + + @Persistent(defaultFetchGroup = "true") + @Column(name = "LAST_EXECUTION_TIME", allowsNull = "true") // new column, must allow nulls on existing databases + private ZonedDateTime lastExecutionTime; + + @Persistent + @Column(name = "PUBLISH_ONLY_WITH_UPDATES", allowsNull = "true") // new column, must allow nulls on existing databases + private boolean publishOnlyWithUpdates; + + public String getCronConfig() { + var cronConfig = ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue(); + if (this.cronConfig != null) { + cronConfig = this.cronConfig; + } + return cronConfig; + } + + public void setCronConfig(String cronConfig) { + if (cronConfig == null) { + this.cronConfig = ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue(); + return; + } + this.cronConfig = cronConfig; + } + + public ZonedDateTime getLastExecutionTime() { + if (lastExecutionTime == null) { + return ZonedDateTime.now(); + } + return lastExecutionTime; + } + + public void setLastExecutionTime(ZonedDateTime lastExecutionTime) { + this.lastExecutionTime = lastExecutionTime; + } + + public boolean getPublishOnlyWithUpdates() { + return publishOnlyWithUpdates; + } + + public void setPublishOnlyWithUpdates(boolean publishOnlyWithUpdates) { + this.publishOnlyWithUpdates = publishOnlyWithUpdates; + } +} \ No newline at end of file From 91f711118af3180065955990eec64d25ccbcd332 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Thu, 2 May 2024 17:12:59 +0200 Subject: [PATCH 260/429] added persistence entry for ScheduledNotificationRule Signed-off-by: Max Schiller --- src/main/resources/META-INF/persistence.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml index 54ea30d611..26da474e71 100644 --- a/src/main/resources/META-INF/persistence.xml +++ b/src/main/resources/META-INF/persistence.xml @@ -34,6 +34,7 @@ org.dependencytrack.model.LicenseGroup org.dependencytrack.model.NotificationPublisher org.dependencytrack.model.NotificationRule + org.dependencytrack.model.ScheduledNotificationRule org.dependencytrack.model.Policy org.dependencytrack.model.PolicyCondition org.dependencytrack.model.PolicyViolation From d14772dea6476dd55becfacb06588629782b87c1 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Thu, 2 May 2024 17:40:25 +0200 Subject: [PATCH 261/429] added scheduled crud methods to query managers Signed-off-by: Max Schiller --- .../persistence/NotificationQueryManager.java | 65 +++++++++++++++++++ .../persistence/QueryManager.java | 13 ++++ 2 files changed, 78 insertions(+) diff --git a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java index 3f4b6e71ad..b5cf44be28 100644 --- a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java @@ -22,10 +22,13 @@ import alpine.notification.NotificationLevel; import alpine.persistence.PaginatedResult; import alpine.resources.AlpineRequest; + +import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.NotificationPublisher; import org.dependencytrack.model.NotificationRule; import org.dependencytrack.model.Project; import org.dependencytrack.model.Tag; +import org.dependencytrack.model.ScheduledNotificationRule; import org.dependencytrack.notification.NotificationScope; import org.dependencytrack.notification.publisher.Publisher; @@ -33,6 +36,8 @@ import javax.jdo.Query; import java.util.ArrayList; import java.util.Collection; + +import java.time.ZonedDateTime; import java.util.List; import static org.dependencytrack.util.PersistenceUtil.assertPersistent; @@ -80,6 +85,29 @@ public NotificationRule createNotificationRule(String name, NotificationScope sc }); } + /** + * Creates a new ScheduledNotificationRule. + * @param name the name of the rule + * @param scope the scope + * @param level the level + * @param publisher the publisher + * @return a new ScheduledNotificationRule + */ + public ScheduledNotificationRule createScheduledNotificationRule(String name, NotificationScope scope, NotificationLevel level, NotificationPublisher publisher) { + final ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setName(name); + rule.setScope(scope); + rule.setNotificationLevel(level); + rule.setPublisher(publisher); + rule.setEnabled(true); + rule.setNotifyChildren(true); + rule.setLogSuccessfulPublish(false); + rule.setCronConfig(ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue()); + rule.setLastExecutionTime(ZonedDateTime.now()); + rule.setPublishOnlyWithUpdates(false); + return persist(rule); + } + /** * Updated an existing NotificationRule. * @param transientRule the rule to update @@ -99,6 +127,26 @@ public NotificationRule updateNotificationRule(NotificationRule transientRule) { return persist(rule); }); } + + /** + * Updated an existing ScheduledNotificationRule. + * @param transientRule the rule to update + * @return a ScheduledNotificationRule + */ + public ScheduledNotificationRule updateScheduledNotificationRule(ScheduledNotificationRule transientRule) { + final ScheduledNotificationRule rule = getObjectByUuid(ScheduledNotificationRule.class, transientRule.getUuid()); + rule.setName(transientRule.getName()); + rule.setEnabled(transientRule.isEnabled()); + rule.setNotifyChildren(transientRule.isNotifyChildren()); + rule.setLogSuccessfulPublish(transientRule.isLogSuccessfulPublish()); + rule.setNotificationLevel(transientRule.getNotificationLevel()); + rule.setPublisherConfig(transientRule.getPublisherConfig()); + rule.setNotifyOn(transientRule.getNotifyOn()); + rule.setCronConfig(transientRule.getCronConfig()); + rule.setLastExecutionTime(transientRule.getLastExecutionTime()); + rule.setPublishOnlyWithUpdates(transientRule.getPublishOnlyWithUpdates()); + return persist(rule); + } /** * Returns a paginated list of all notification rules. @@ -116,6 +164,23 @@ public PaginatedResult getNotificationRules() { } return execute(query); } + + /** + * Returns a paginated list of all scheduled notification rules. + * @return a paginated list of ScheduledNotificationRules + */ + public PaginatedResult getScheduledNotificationRules() { + final Query query = pm.newQuery(ScheduledNotificationRule.class); + if (orderBy == null) { + query.setOrdering("name asc"); + } + if (filter != null) { + query.setFilter("name.toLowerCase().matches(:name) || publisher.name.toLowerCase().matches(:name)"); + final String filterString = ".*" + filter.toLowerCase() + ".*"; + return execute(query, filterString); + } + return execute(query); + } /** * Retrieves all NotificationPublishers. diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index 01c73ebcf2..0da2d16412 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -67,6 +67,7 @@ import org.dependencytrack.model.Repository; import org.dependencytrack.model.RepositoryMetaComponent; import org.dependencytrack.model.RepositoryType; +import org.dependencytrack.model.ScheduledNotificationRule; import org.dependencytrack.model.ServiceComponent; import org.dependencytrack.model.Tag; import org.dependencytrack.model.Vex; @@ -1304,6 +1305,18 @@ public void removeTeamFromNotificationRules(final Team team) { getNotificationQueryManager().removeTeamFromNotificationRules(team); } + public ScheduledNotificationRule createScheduledNotificationRule(String name, NotificationScope scope, NotificationLevel level, NotificationPublisher publisher) { + return getNotificationQueryManager().createScheduledNotificationRule(name, scope, level, publisher); + } + + public ScheduledNotificationRule updateScheduledNotificationRule(ScheduledNotificationRule transientRule) { + return getNotificationQueryManager().updateScheduledNotificationRule(transientRule); + } + + public PaginatedResult getScheduledNotificationRules() { + return getNotificationQueryManager().getScheduledNotificationRules(); + } + /** * Determines if a config property is enabled or not. * @param configPropertyConstants the property to query From 575d8fb9b68b3342497ea5164395a98b3dd8a807 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Thu, 2 May 2024 18:09:54 +0200 Subject: [PATCH 262/429] added api for scheduled notification rules Signed-off-by: Max Schiller --- .../v1/ScheduledNotificationRuleResource.java | 351 ++++++++++++++++++ 1 file changed, 351 insertions(+) create mode 100644 src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java diff --git a/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java new file mode 100644 index 0000000000..76daf4a399 --- /dev/null +++ b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java @@ -0,0 +1,351 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.resources.v1; + +import alpine.common.logging.Logger; +import alpine.model.Team; +import alpine.persistence.PaginatedResult; +import alpine.server.auth.PermissionRequired; +import alpine.server.resources.AlpineResource; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.Authorization; +import io.swagger.annotations.ResponseHeader; +import org.apache.commons.lang3.StringUtils; +import org.dependencytrack.auth.Permissions; +import org.dependencytrack.model.NotificationPublisher; +import org.dependencytrack.model.Project; +import org.dependencytrack.model.ScheduledNotificationRule; +import org.dependencytrack.model.validation.ValidUuid; +import org.dependencytrack.notification.NotificationScope; +import org.dependencytrack.notification.publisher.SendMailPublisher; +import org.dependencytrack.persistence.QueryManager; +import org.dependencytrack.resources.v1.openapi.PaginatedApi; + +import javax.validation.Validator; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.List; + +/** + * JAX-RS resources for processing scheduled notification rules. + * + * @author Max Schiller + */ +@Path("/v1/schedulednotification/rule") +@Api(value = "schedulednotification", authorizations = @Authorization(value = "X-Api-Key")) +public class ScheduledNotificationRuleResource extends AlpineResource { + + private static final Logger LOGGER = Logger.getLogger(ScheduledNotificationRuleResource.class); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Returns a list of all scheduled notification rules", + response = ScheduledNotificationRule.class, + responseContainer = "List", + responseHeaders = @ResponseHeader(name = TOTAL_COUNT_HEADER, response = Long.class, description = "The total number of scheduled notification rules"), + notes = "

          Requires permission SYSTEM_CONFIGURATION

          " + ) + @PaginatedApi + @ApiResponses(value = { + @ApiResponse(code = 401, message = "Unauthorized") + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response getAllScheduledNotificationRules() { + try (QueryManager qm = new QueryManager(getAlpineRequest())) { + final PaginatedResult result = qm.getScheduledNotificationRules(); + return Response.ok(result.getObjects()).header(TOTAL_COUNT_HEADER, result.getTotal()).build(); + } + } + + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Creates a new scheduled notification rule", + response = ScheduledNotificationRule.class, + code = 201, + notes = "

          Requires permission SYSTEM_CONFIGURATION

          " + ) + @ApiResponses(value = { + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 404, message = "The UUID of the notification publisher could not be found") + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response createScheduledNotificationRule(ScheduledNotificationRule jsonRule) { + final Validator validator = super.getValidator(); + failOnValidationError( + validator.validateProperty(jsonRule, "name") + ); + + try (QueryManager qm = new QueryManager()) { + NotificationPublisher publisher = null; + if (jsonRule.getPublisher() != null) { + publisher =qm.getObjectByUuid(NotificationPublisher.class, jsonRule.getPublisher().getUuid()); + } + if (publisher == null) { + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the notification publisher could not be found.").build(); + } + final ScheduledNotificationRule rule = qm.createScheduledNotificationRule( + StringUtils.trimToNull(jsonRule.getName()), + jsonRule.getScope(), + jsonRule.getNotificationLevel(), + publisher + ); + return Response.status(Response.Status.CREATED).entity(rule).build(); + } + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Updates a scheduled notification rule", + response = ScheduledNotificationRule.class, + notes = "

          Requires permission SYSTEM_CONFIGURATION

          " + ) + @ApiResponses(value = { + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 404, message = "The UUID of the scheduled notification rule could not be found") + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response updateScheduledNotificationRule(ScheduledNotificationRule jsonRule) { + final Validator validator = super.getValidator(); + failOnValidationError( + validator.validateProperty(jsonRule, "name"), + validator.validateProperty(jsonRule, "publisherConfig") + ); + + try (QueryManager qm = new QueryManager()) { + ScheduledNotificationRule rule = qm.getObjectByUuid(ScheduledNotificationRule.class, jsonRule.getUuid()); + if (rule != null) { + jsonRule.setName(StringUtils.trimToNull(jsonRule.getName())); + rule = qm.updateScheduledNotificationRule(jsonRule); + return Response.ok(rule).build(); + } else { + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the scheduled notification rule could not be found.").build(); + } + } + } + + @DELETE + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Deletes a scheduled notification rule", + code = 204, + notes = "

          Requires permission SYSTEM_CONFIGURATION

          " + ) + @ApiResponses(value = { + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 404, message = "The UUID of the scheduled notification rule could not be found") + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response deleteScheduledNotificationRule(ScheduledNotificationRule jsonRule) { + try (QueryManager qm = new QueryManager()) { + final ScheduledNotificationRule rule = qm.getObjectByUuid(ScheduledNotificationRule.class, jsonRule.getUuid()); + if (rule != null) { + qm.delete(rule); + return Response.status(Response.Status.NO_CONTENT).build(); + } else { + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the scheduled notification rule could not be found.").build(); + } + } + } + + @POST + @Path("/{ruleUuid}/project/{projectUuid}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Adds a project to a scheduled notification rule", + response = ScheduledNotificationRule.class, + notes = "

          Requires permission SYSTEM_CONFIGURATION

          " + ) + @ApiResponses(value = { + @ApiResponse(code = 304, message = "The rule already has the specified project assigned"), + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 404, message = "The scheduled notification rule or project could not be found") + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response addProjectToRule( + @ApiParam(value = "The UUID of the rule to add a project to", format = "uuid", required = true) + @PathParam("ruleUuid") @ValidUuid String ruleUuid, + @ApiParam(value = "The UUID of the project to add to the rule", format = "uuid", required = true) + @PathParam("projectUuid") @ValidUuid String projectUuid) { + try (QueryManager qm = new QueryManager()) { + final ScheduledNotificationRule rule = qm.getObjectByUuid(ScheduledNotificationRule.class, ruleUuid); + if (rule == null) { + return Response.status(Response.Status.NOT_FOUND).entity("The scheduled notification rule could not be found.").build(); + } + if (rule.getScope() != NotificationScope.PORTFOLIO) { + return Response.status(Response.Status.NOT_ACCEPTABLE).entity("Project limitations are only possible on scheduled notification rules with PORTFOLIO scope.").build(); + } + final Project project = qm.getObjectByUuid(Project.class, projectUuid); + if (project == null) { + return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); + } + final List projects = rule.getProjects(); + if (projects != null && !projects.contains(project)) { + rule.getProjects().add(project); + qm.persist(rule); + return Response.ok(rule).build(); + } + return Response.status(Response.Status.NOT_MODIFIED).build(); + } + } + + @DELETE + @Path("/{ruleUuid}/project/{projectUuid}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Removes a project from a scheduled notification rule", + response = ScheduledNotificationRule.class, + notes = "

          Requires permission SYSTEM_CONFIGURATION

          " + ) + @ApiResponses(value = { + @ApiResponse(code = 304, message = "The rule does not have the specified project assigned"), + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 404, message = "The scheduled notification rule or project could not be found") + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response removeProjectFromRule( + @ApiParam(value = "The UUID of the rule to remove the project from", format = "uuid", required = true) + @PathParam("ruleUuid") @ValidUuid String ruleUuid, + @ApiParam(value = "The UUID of the project to remove from the rule", format = "uuid", required = true) + @PathParam("projectUuid") @ValidUuid String projectUuid) { + try (QueryManager qm = new QueryManager()) { + final ScheduledNotificationRule rule = qm.getObjectByUuid(ScheduledNotificationRule.class, ruleUuid); + if (rule == null) { + return Response.status(Response.Status.NOT_FOUND).entity("The scheduled notification rule could not be found.").build(); + } + if (rule.getScope() != NotificationScope.PORTFOLIO) { + return Response.status(Response.Status.NOT_ACCEPTABLE).entity("Project limitations are only possible on scheduled notification rules with PORTFOLIO scope.").build(); + } + final Project project = qm.getObjectByUuid(Project.class, projectUuid); + if (project == null) { + return Response.status(Response.Status.NOT_FOUND).entity("The project could not be found.").build(); + } + final List projects = rule.getProjects(); + if (projects != null && projects.contains(project)) { + rule.getProjects().remove(project); + qm.persist(rule); + return Response.ok(rule).build(); + } + return Response.status(Response.Status.NOT_MODIFIED).build(); + } + } + + @POST + @Path("/{ruleUuid}/team/{teamUuid}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Adds a team to a scheduled scheduled notification rule", + response = ScheduledNotificationRule.class, + notes = "

          Requires permission SYSTEM_CONFIGURATION

          " + ) + @ApiResponses(value = { + @ApiResponse(code = 304, message = "The rule already has the specified team assigned"), + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 404, message = "The scheduled notification rule or team could not be found") + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response addTeamToRule( + @ApiParam(value = "The UUID of the rule to add a team to", format = "uuid", required = true) + @PathParam("ruleUuid") @ValidUuid String ruleUuid, + @ApiParam(value = "The UUID of the team to add to the rule", format = "uuid", required = true) + @PathParam("teamUuid") @ValidUuid String teamUuid) { + try (QueryManager qm = new QueryManager()) { + final ScheduledNotificationRule rule = qm.getObjectByUuid(ScheduledNotificationRule.class, ruleUuid); + if (rule == null) { + return Response.status(Response.Status.NOT_FOUND).entity("The scheduled notification rule could not be found.").build(); + } + if (!rule.getPublisher().getPublisherClass().equals(SendMailPublisher.class.getName())) { + return Response.status(Response.Status.NOT_ACCEPTABLE).entity("Team subscriptions are only possible on scheduled notification rules with EMAIL publisher.").build(); + } + final Team team = qm.getObjectByUuid(Team.class, teamUuid); + if (team == null) { + return Response.status(Response.Status.NOT_FOUND).entity("The team could not be found.").build(); + } + final List teams = rule.getTeams(); + if (teams != null && !teams.contains(team)) { + rule.getTeams().add(team); + qm.persist(rule); + return Response.ok(rule).build(); + } + return Response.status(Response.Status.NOT_MODIFIED).build(); + } + } + + @DELETE + @Path("/{ruleUuid}/team/{teamUuid}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Removes a team from a scheduled notification rule", + response = ScheduledNotificationRule.class, + notes = "

          Requires permission SYSTEM_CONFIGURATION

          " + ) + @ApiResponses(value = { + @ApiResponse(code = 304, message = "The rule does not have the specified team assigned"), + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 404, message = "The scheduled notification rule or team could not be found") + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response removeTeamFromRule( + @ApiParam(value = "The UUID of the rule to remove the project from", format = "uuid", required = true) + @PathParam("ruleUuid") @ValidUuid String ruleUuid, + @ApiParam(value = "The UUID of the project to remove from the rule", format = "uuid", required = true) + @PathParam("teamUuid") @ValidUuid String teamUuid) { + try (QueryManager qm = new QueryManager()) { + final ScheduledNotificationRule rule = qm.getObjectByUuid(ScheduledNotificationRule.class, ruleUuid); + if (rule == null) { + return Response.status(Response.Status.NOT_FOUND).entity("The scheduled notification rule could not be found.").build(); + } + if (!rule.getPublisher().getPublisherClass().equals(SendMailPublisher.class.getName())) { + return Response.status(Response.Status.NOT_ACCEPTABLE).entity("Team subscriptions are only possible on scheduled notification rules with EMAIL publisher.").build(); + } + final Team team = qm.getObjectByUuid(Team.class, teamUuid); + if (team == null) { + return Response.status(Response.Status.NOT_FOUND).entity("The team could not be found.").build(); + } + final List teams = rule.getTeams(); + if (teams != null && teams.contains(team)) { + rule.getTeams().remove(team); + qm.persist(rule); + return Response.ok(rule).build(); + } + return Response.status(Response.Status.NOT_MODIFIED).build(); + } + } +} From cfc52b25bca4a540555f4ad49440b524a99e697c Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Fri, 3 May 2024 10:37:47 +0200 Subject: [PATCH 263/429] added some minor validation in scheduled api Signed-off-by: Max Schiller --- .../resources/v1/ScheduledNotificationRuleResource.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java index 76daf4a399..f068ce7646 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java @@ -103,7 +103,9 @@ public Response getAllScheduledNotificationRules() { public Response createScheduledNotificationRule(ScheduledNotificationRule jsonRule) { final Validator validator = super.getValidator(); failOnValidationError( - validator.validateProperty(jsonRule, "name") + validator.validateProperty(jsonRule, "name"), + validator.validateProperty(jsonRule, "cronConfig"), + validator.validateProperty(jsonRule, "lastExecutionTime") ); try (QueryManager qm = new QueryManager()) { @@ -141,7 +143,9 @@ public Response updateScheduledNotificationRule(ScheduledNotificationRule jsonRu final Validator validator = super.getValidator(); failOnValidationError( validator.validateProperty(jsonRule, "name"), - validator.validateProperty(jsonRule, "publisherConfig") + validator.validateProperty(jsonRule, "publisherConfig"), + validator.validateProperty(jsonRule, "cronConfig"), + validator.validateProperty(jsonRule, "lastExecutionTime") ); try (QueryManager qm = new QueryManager()) { From 0ceac17b93807f918a80bef5b79263b5fa52997e Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Fri, 3 May 2024 12:43:11 +0200 Subject: [PATCH 264/429] fixed wrong database usage (data stored in notificationrule table), workaround for unknown possibility of JDO Inheritance setting Signed-off-by: Max Schiller --- .../model/ScheduledNotificationRule.java | 250 +++++++++++++++++- 1 file changed, 249 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java index a826d62e46..6c0b505a9f 100644 --- a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java +++ b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java @@ -18,15 +18,42 @@ */ package org.dependencytrack.model; +import alpine.common.validation.RegexSequence; +import alpine.model.Team; +import alpine.notification.NotificationLevel; import alpine.server.json.TrimmedStringDeserializer; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import javax.jdo.annotations.Column; +import javax.jdo.annotations.Element; +import javax.jdo.annotations.Extension; +import javax.jdo.annotations.IdGeneratorStrategy; +import javax.jdo.annotations.Join; +import javax.jdo.annotations.Order; import javax.jdo.annotations.PersistenceCapable; import javax.jdo.annotations.Persistent; +import javax.jdo.annotations.PrimaryKey; +import javax.jdo.annotations.Unique; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +import org.apache.commons.collections4.CollectionUtils; +import org.dependencytrack.notification.NotificationGroup; +import org.dependencytrack.notification.NotificationScope; + +import java.io.Serializable; import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.UUID; /** * Defines a Model class for scheduled notification configurations. @@ -36,7 +63,92 @@ @PersistenceCapable @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public class ScheduledNotificationRule extends NotificationRule { +public class ScheduledNotificationRule implements Serializable { + private static final long serialVersionUID = 2534439091019367263L; + + @PrimaryKey + @Persistent(valueStrategy = IdGeneratorStrategy.NATIVE) + @JsonIgnore + private long id; + + /** + * The String representation of the name of the notification. + */ + @Persistent + @Column(name = "NAME", allowsNull = "false") + @NotBlank + @Size(min = 1, max = 255) + @JsonDeserialize(using = TrimmedStringDeserializer.class) + @Pattern(regexp = RegexSequence.Definition.PRINTABLE_CHARS, message = "The name may only contain printable characters") + private String name; + + @Persistent + @Column(name = "ENABLED") + private boolean enabled; + + @Persistent + @Column(name = "NOTIFY_CHILDREN", allowsNull = "true") // New column, must allow nulls on existing data bases) + private boolean notifyChildren; + + /** + * In addition to warnings and errors, also emit a log message upon successful publishing. + *

          + * Intended to aid in debugging of missing notifications, or environments where notification + * delivery is critical and subject to auditing. + * + * @since 4.10.0 + */ + @Persistent + @Column(name = "LOG_SUCCESSFUL_PUBLISH", allowsNull = "true") + private boolean logSuccessfulPublish; + + @Persistent(defaultFetchGroup = "true") + @Column(name = "SCOPE", jdbcType = "VARCHAR", allowsNull = "false") + @NotNull + private NotificationScope scope; + + @Persistent(defaultFetchGroup = "true") + @Column(name = "NOTIFICATION_LEVEL", jdbcType = "VARCHAR") + private NotificationLevel notificationLevel; + + @Persistent(table = "SCHEDULED_NOTIFICATIONRULE_PROJECTS", defaultFetchGroup = "true") + @Join(column = "SCHEDULED_NOTIFICATIONRULE_ID") + @Element(column = "PROJECT_ID") + @Order(extensions = @Extension(vendorName = "datanucleus", key = "list-ordering", value = "name ASC, version ASC")) + private List projects; + + @Persistent(table = "SCHEDULED_NOTIFICATIONRULE_TEAMS", defaultFetchGroup = "true") + @Join(column = "SCHEDULED_NOTIFICATIONRULE_ID") + @Element(column = "TEAM_ID") + @Order(extensions = @Extension(vendorName = "datanucleus", key = "list-ordering", value = "name ASC")) + private List teams; + + @Persistent + @Column(name = "NOTIFY_ON", length = 1024) + private String notifyOn; + + @Persistent + @Column(name = "MESSAGE", length = 1024) + @Size(max = 1024) + @JsonDeserialize(using = TrimmedStringDeserializer.class) + @Pattern(regexp = RegexSequence.Definition.PRINTABLE_CHARS, message = "The message may only contain printable characters") + private String message; + + @Persistent(defaultFetchGroup = "true") + @Column(name = "PUBLISHER") + private NotificationPublisher publisher; + + @Persistent(defaultFetchGroup = "true") + @Column(name = "PUBLISHER_CONFIG", jdbcType = "CLOB") + @JsonDeserialize(using = TrimmedStringDeserializer.class) + private String publisherConfig; + + @Persistent(defaultFetchGroup = "true", customValueStrategy = "uuid") + @Unique(name = "SCHEDULED_NOTIFICATIONRULE_UUID_IDX") + @Column(name = "UUID", jdbcType = "VARCHAR", length = 36, allowsNull = "false") + @NotNull + private UUID uuid; + @Persistent(defaultFetchGroup = "true") @Column(name = "CRON_CONFIG", allowsNull = "true") // new column, must allow nulls on existing databases @JsonDeserialize(using = TrimmedStringDeserializer.class) @@ -51,6 +163,142 @@ public class ScheduledNotificationRule extends NotificationRule { @Column(name = "PUBLISH_ONLY_WITH_UPDATES", allowsNull = "true") // new column, must allow nulls on existing databases private boolean publishOnlyWithUpdates; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @NotNull + public String getName() { + return name; + } + + public void setName(@NotNull String name) { + this.name = name; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isNotifyChildren() { + return notifyChildren; + } + + public void setNotifyChildren(boolean notifyChildren) { + this.notifyChildren = notifyChildren; + } + + public boolean isLogSuccessfulPublish() { + return logSuccessfulPublish; + } + + public void setLogSuccessfulPublish(final boolean logSuccessfulPublish) { + this.logSuccessfulPublish = logSuccessfulPublish; + } + + @NotNull + public NotificationScope getScope() { + return scope; + } + + public void setScope(@NotNull NotificationScope scope) { + this.scope = scope; + } + + public NotificationLevel getNotificationLevel() { + return notificationLevel; + } + + public void setNotificationLevel(NotificationLevel notificationLevel) { + this.notificationLevel = notificationLevel; + } + + public List getProjects() { + return projects; + } + + public void setProjects(List projects) { + this.projects = projects; + } + + public List getTeams() { + return teams; + } + + public void setTeams(List teams) { + this.teams = teams; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Set getNotifyOn() { + Set result = new TreeSet<>(); + if (notifyOn != null) { + String[] groups = notifyOn.split(","); + for (String s: groups) { + result.add(NotificationGroup.valueOf(s.trim())); + } + } + return result; + } + + public void setNotifyOn(Set groups) { + if (CollectionUtils.isEmpty(groups)) { + this.notifyOn = null; + return; + } + StringBuilder sb = new StringBuilder(); + List list = new ArrayList<>(groups); + Collections.sort(list); + for (int i=0; i Date: Tue, 7 May 2024 11:57:01 +0200 Subject: [PATCH 265/429] Updated NotificationQueryManager to use UTC time for ScheduledNotificationRule Signed-off-by: Max Schiller --- .../dependencytrack/persistence/NotificationQueryManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java index b5cf44be28..8e891436a4 100644 --- a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java @@ -37,6 +37,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.List; @@ -103,7 +104,7 @@ public ScheduledNotificationRule createScheduledNotificationRule(String name, No rule.setNotifyChildren(true); rule.setLogSuccessfulPublish(false); rule.setCronConfig(ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue()); - rule.setLastExecutionTime(ZonedDateTime.now()); + rule.setLastExecutionTime(ZonedDateTime.now(ZoneOffset.UTC)); rule.setPublishOnlyWithUpdates(false); return persist(rule); } From f51db01f18b6810636014aea68312b883bfbd384 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Tue, 7 May 2024 11:58:14 +0200 Subject: [PATCH 266/429] Add new methods for retrieving new policy violations and vulnerabilities for scheduled notifications Signed-off-by: Max Schiller --- .../persistence/PolicyQueryManager.java | 37 ++++++++++++++++- .../persistence/QueryManager.java | 9 ++++ .../VulnerabilityQueryManager.java | 41 +++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index 3aa3b07190..8e21f4a3e5 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -23,6 +23,8 @@ import alpine.model.UserPrincipal; import alpine.persistence.PaginatedResult; import alpine.resources.AlpineRequest; + +import org.datanucleus.api.jdo.JDOQuery; import org.dependencytrack.model.Component; import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.License; @@ -36,14 +38,17 @@ import org.dependencytrack.model.ViolationAnalysisComment; import org.dependencytrack.model.ViolationAnalysisState; import org.dependencytrack.util.DateUtil; - import javax.jdo.PersistenceManager; import javax.jdo.Query; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -53,6 +58,7 @@ import static org.dependencytrack.util.PersistenceUtil.assertNonPersistentAll; import static org.dependencytrack.util.PersistenceUtil.assertPersistent; import static org.dependencytrack.util.PersistenceUtil.assertPersistentAll; +import java.util.Map; final class PolicyQueryManager extends QueryManager implements IQueryManager { @@ -392,6 +398,35 @@ public List getAllPolicyViolations(final Project project) { return (List)query.execute(project.getId()); } + /** + * Returns a List of all Policy objects since given datetime for given project ids. + * @param dateTime DateTime, since which the policy violations shall be fetched + * @param projectIds IDs of the projects, that shall be used for filtering + * @return a List of {@link PolicyViolation}s + */ + @SuppressWarnings("unchecked") + public Map> getNewPolicyViolationsForProjectsSince(ZonedDateTime dateTime, List projectIds){ + String queryString = "SELECT PROJECT_ID, ID " + + "FROM POLICYVIOLATION " + + "WHERE (TIMESTAMP BETWEEN ? AND ?) "; + if(projectIds != null && !projectIds.isEmpty()){ + queryString.concat("AND (PROJECT_ID IN ?) "); + } + queryString.concat("ORDER BY PROJECT_ID ASC"); + final Query query = pm.newQuery(JDOQuery.SQL_QUERY_LANGUAGE, queryString); + final List totalList = (List)query.execute(dateTime, ZonedDateTime.now(ZoneOffset.UTC), projectIds); + Map> projectPolicyViolations = new HashMap<>(); + for(Object[] obj : totalList){ + Project project = getObjectById(Project.class, obj[0]); + PolicyViolation policyViolation = getObjectById(PolicyViolation.class, obj[1]); + if(!projectPolicyViolations.containsKey(project)){ + projectPolicyViolations.put(project, new ArrayList<>()); + } + projectPolicyViolations.get(project).add(policyViolation); + } + return projectPolicyViolations; + } + /** * Returns a List of all Policy violations for a specific project. * @param project the project to retrieve violations for diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index 0da2d16412..5c365b4f01 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -90,6 +90,7 @@ import javax.jdo.PersistenceManager; import javax.jdo.Query; import java.security.Principal; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -735,6 +736,10 @@ public List getAllPolicyViolations(final Project project) { return getPolicyQueryManager().getAllPolicyViolations(project); } + public Map> getNewPolicyViolationsForProjectsSince(ZonedDateTime zonedDateTime, List projectIds){ + return getPolicyQueryManager().getNewPolicyViolationsForProjectsSince(zonedDateTime, projectIds); + } + public PaginatedResult getPolicyViolations(final Project project, boolean includeSuppressed) { return getPolicyQueryManager().getPolicyViolations(project, includeSuppressed); } @@ -832,6 +837,10 @@ public Vulnerability getVulnerabilityByVulnId(Vulnerability.Source source, Strin return getVulnerabilityQueryManager().getVulnerabilityByVulnId(source, vulnId, includeVulnerableSoftware); } + public Map> getNewVulnerabilitiesForProjectsSince(ZonedDateTime zonedDateTime, List projectIds){ + return getVulnerabilityQueryManager().getNewVulnerabilitiesForProjectsSince(zonedDateTime, projectIds); + } + public void addVulnerability(Vulnerability vulnerability, Component component, AnalyzerIdentity analyzerIdentity) { getVulnerabilityQueryManager().addVulnerability(vulnerability, component, analyzerIdentity); } diff --git a/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java b/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java index 4594e539d8..05867ea402 100644 --- a/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java @@ -22,6 +22,7 @@ import alpine.persistence.PaginatedResult; import alpine.resources.AlpineRequest; import org.apache.commons.lang3.StringUtils; +import org.datanucleus.api.jdo.JDOQuery; import org.dependencytrack.event.IndexEvent; import org.dependencytrack.model.AffectedVersionAttribution; import org.dependencytrack.model.Analysis; @@ -37,6 +38,9 @@ import javax.jdo.PersistenceManager; import javax.jdo.Query; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -180,6 +184,43 @@ public Vulnerability getVulnerabilityByVulnId(Vulnerability.Source source, Strin return getVulnerabilityByVulnId(source.name(), vulnId, includeVulnerableSoftware); } + /** + * Returns vulnerabilities for the specified npm module + * @param module the NPM module to query on + * @return a list of Vulnerability objects + */ + @Deprecated + @SuppressWarnings("unchecked") + //todo: determine if this is needed and delete + public List getVulnerabilitiesForNpmModule(String module) { + final Query query = pm.newQuery(Vulnerability.class, "source == :source && subtitle == :module"); + query.getFetchPlan().addGroup(Vulnerability.FetchGroup.COMPONENTS.name()); + return (List) query.execute(Vulnerability.Source.NPM.name(), module); + } + + @SuppressWarnings("unchecked") + public Map> getNewVulnerabilitiesForProjectsSince(ZonedDateTime lastExecution, List projectIds){ + String queryString = "PROJECT_ID, VULNERABILITY_ID " + + "FROM FINDINGATTRIBUTION " + + "WHERE ATTRIBUTED_ON BETWEEN ? AND ? "; + if(projectIds != null && !projectIds.isEmpty()){ + queryString.concat("AND PROJECT_ID IN ? "); + } + queryString.concat("ORDER BY PROJECT_ID ASC, VULNERABILITY_ID ASC"); + final Query query = pm.newQuery(JDOQuery.SQL_QUERY_LANGUAGE, queryString); + final List totalList = (List)query.execute(lastExecution, ZonedDateTime.now(ZoneOffset.UTC), projectIds); + Map> projectVulnerabilities = new HashMap<>(); + for(Object[] obj : totalList){ + Project project = getObjectById(Project.class, obj[0]); + Vulnerability vulnerability = getObjectById(Vulnerability.class, obj[1]); + if(!projectVulnerabilities.containsKey(project)){ + projectVulnerabilities.put(project, new ArrayList<>()); + } + projectVulnerabilities.get(project).add(vulnerability); + } + return projectVulnerabilities; + } + /** * Adds a vulnerability to a component. * @param vulnerability the vulnerability to add From 0438253dc90ad5b3008888dc7f863b3f3f67d97a Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Tue, 7 May 2024 13:09:52 +0200 Subject: [PATCH 267/429] Added basic Task for sending scheduled notifications (originates mainly of previous work from MGE, may be changed in future) Signed-off-by: Max Schiller --- .../tasks/SendScheduledNotificationTask.java | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java new file mode 100644 index 0000000000..8b2f537b26 --- /dev/null +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -0,0 +1,163 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.tasks; + +import java.io.StringWriter; +import java.io.Writer; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import javax.json.Json; +import javax.json.JsonObject; + +import org.dependencytrack.model.NotificationPublisher; +import org.dependencytrack.model.PolicyViolation; +import org.dependencytrack.model.Project; +import org.dependencytrack.model.ScheduledNotificationRule; +import org.dependencytrack.model.Vulnerability; +import org.dependencytrack.persistence.QueryManager; + +import alpine.common.logging.Logger; +import alpine.security.crypto.DataEncryption; +import alpine.server.mail.SendMail; +import alpine.server.mail.SendMailException; +import io.pebbletemplates.pebble.PebbleEngine; +import io.pebbletemplates.pebble.template.PebbleTemplate; + +import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_ENABLED; +import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_FROM_ADDR; +import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_PASSWORD; +import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_SERVER_HOSTNAME; +import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_SERVER_PORT; +import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_SSLTLS; +import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_TRUSTCERT; +import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_USERNAME; +import static org.dependencytrack.notification.publisher.Publisher.CONFIG_TEMPLATE_KEY; +import static org.dependencytrack.notification.publisher.Publisher.CONFIG_TEMPLATE_MIME_TYPE_KEY; + +public class SendScheduledNotificationTask implements Runnable { + private ScheduledNotificationRule scheduledNotificationRule; + private ScheduledExecutorService service; + private static final Logger LOGGER = Logger.getLogger(SendScheduledNotificationTask.class); + + public SendScheduledNotificationTask(ScheduledNotificationRule scheduledNotificationRule, ScheduledExecutorService service) { + this.scheduledNotificationRule = scheduledNotificationRule; + this.service = service; + } + + @Override + public void run() { + String content = ""; + final String mimeType; + final boolean smtpEnabled; + final String smtpFrom; + final String smtpHostname; + final int smtpPort; + final String smtpUser; + final String encryptedSmtpPassword; + final boolean smtpSslTls; + final boolean smtpTrustCert; + Map> newProjectVulnerabilities; + Map> newProjectPolicyViolations; + + try (QueryManager qm = new QueryManager()) { + scheduledNotificationRule = qm.getObjectByUuid(ScheduledNotificationRule.class, scheduledNotificationRule.getUuid()); + if (scheduledNotificationRule == null) { + LOGGER.info("shutdown ExecutorService for Scheduled notification " + scheduledNotificationRule.getUuid()); + service.shutdown(); + } else { + // if (scheduledNotificationRule.getLastExecutionTime().equals(scheduledNotificationRule.getCreated())) { + // LOGGER.info("schedulednotification just created. No Information to show"); + // } else { + final List projectIds = scheduledNotificationRule.getProjects().stream().map(proj -> proj.getId()).toList(); + newProjectVulnerabilities = qm.getNewVulnerabilitiesForProjectsSince(scheduledNotificationRule.getLastExecutionTime(), projectIds); + newProjectPolicyViolations = qm.getNewPolicyViolationsForProjectsSince(scheduledNotificationRule.getLastExecutionTime(), projectIds); + + NotificationPublisher notificationPublisher = qm.getNotificationPublisher("Email"); + + JsonObject notificationPublisherConfig = Json.createObjectBuilder() + .add(CONFIG_TEMPLATE_MIME_TYPE_KEY, notificationPublisher.getTemplateMimeType()) + .add(CONFIG_TEMPLATE_KEY, notificationPublisher.getTemplate()) + .build(); + + PebbleEngine pebbleEngine = new PebbleEngine.Builder().build(); + String literalTemplate = notificationPublisherConfig.getString(CONFIG_TEMPLATE_KEY); + final PebbleTemplate template = pebbleEngine.getLiteralTemplate(literalTemplate); + mimeType = notificationPublisherConfig.getString(CONFIG_TEMPLATE_MIME_TYPE_KEY); + + final Map context = new HashMap<>(); + context.put("length", newProjectVulnerabilities.size()); + context.put("vulnerabilities", newProjectVulnerabilities); + context.put("policyviolations", newProjectPolicyViolations); + final Writer writer = new StringWriter(); + template.evaluate(writer, context); + content = writer.toString(); + + smtpEnabled = qm.isEnabled(EMAIL_SMTP_ENABLED); + if (!smtpEnabled) { + System.out.println("SMTP is not enabled; Skipping notification "); + return; + } + smtpFrom = qm.getConfigProperty(EMAIL_SMTP_FROM_ADDR.getGroupName(),EMAIL_SMTP_FROM_ADDR.getPropertyName()).getPropertyValue(); + smtpHostname = qm.getConfigProperty(EMAIL_SMTP_SERVER_HOSTNAME.getGroupName(),EMAIL_SMTP_SERVER_HOSTNAME.getPropertyName()).getPropertyValue(); + smtpPort = Integer.parseInt(qm.getConfigProperty(EMAIL_SMTP_SERVER_PORT.getGroupName(),EMAIL_SMTP_SERVER_PORT.getPropertyName()).getPropertyValue()); + smtpUser = qm.getConfigProperty(EMAIL_SMTP_USERNAME.getGroupName(),EMAIL_SMTP_USERNAME.getPropertyName()).getPropertyValue(); + encryptedSmtpPassword = qm.getConfigProperty(EMAIL_SMTP_PASSWORD.getGroupName(),EMAIL_SMTP_PASSWORD.getPropertyName()).getPropertyValue(); + smtpSslTls = qm.isEnabled(EMAIL_SMTP_SSLTLS); + smtpTrustCert = qm.isEnabled(EMAIL_SMTP_TRUSTCERT); + final boolean smtpAuth = (smtpUser != null && encryptedSmtpPassword != null); + final String decryptedSmtpPassword; + try { + decryptedSmtpPassword = (encryptedSmtpPassword != null) ? DataEncryption.decryptAsString(encryptedSmtpPassword) : null; + } catch (Exception e) { + System.out.println("Failed to decrypt SMTP password"); + return; + } + // String[] destinations = scheduledNotificationRule.getDestinations().split(" "); + try { + final SendMail sendMail = new SendMail() + .from(smtpFrom) + // .to(destinations) + .subject("[Dependency-Track] " + "ScheduledNotification") + .body(content) + .bodyMimeType(mimeType) + .host(smtpHostname) + .port(smtpPort) + .username(smtpUser) + .password(decryptedSmtpPassword) + .smtpauth(smtpAuth) + .useStartTLS(smtpSslTls) + .trustCert(smtpTrustCert); + sendMail.send(); + } catch (SendMailException | RuntimeException e) { + LOGGER.debug("Failed to send notification email "); + LOGGER.debug(e.getMessage()); + } + // } + // qm.updateScheduledNotificationInfoNextExecution(scheduledNotificationRule); + } + + } catch (Exception e) { + LOGGER.debug(e.getMessage()); + } + + } + +} From 38cd05b08e79eadfaa41dbbc8539020ae52ca5f4 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 8 May 2024 13:20:13 +0200 Subject: [PATCH 268/429] added update method for last execution after scheduled task completion Signed-off-by: Max Schiller --- .../persistence/NotificationQueryManager.java | 6 ++++++ .../java/org/dependencytrack/persistence/QueryManager.java | 4 ++++ .../tasks/SendScheduledNotificationTask.java | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java index 8e891436a4..0505969f69 100644 --- a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java @@ -149,6 +149,12 @@ public ScheduledNotificationRule updateScheduledNotificationRule(ScheduledNotifi return persist(rule); } + public ScheduledNotificationRule updateScheduledNotificationRuleLastExecutionTimeToNowUtc(ScheduledNotificationRule transientRule) { + final ScheduledNotificationRule rule = getObjectByUuid(ScheduledNotificationRule.class, transientRule.getUuid()); + rule.setLastExecutionTime(ZonedDateTime.now(ZoneOffset.UTC)); + return persist(rule); + } + /** * Returns a paginated list of all notification rules. * @return a paginated list of NotificationRules diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index 5c365b4f01..c3f5d52f00 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -1322,6 +1322,10 @@ public ScheduledNotificationRule updateScheduledNotificationRule(ScheduledNotifi return getNotificationQueryManager().updateScheduledNotificationRule(transientRule); } + public ScheduledNotificationRule updateScheduledNotificationRuleToNowUtc(ScheduledNotificationRule transientRule) { + return getNotificationQueryManager().updateScheduledNotificationRuleToNowUtc(transientRule); + } + public PaginatedResult getScheduledNotificationRules() { return getNotificationQueryManager().getScheduledNotificationRules(); } diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index 8b2f537b26..1fd1441f7f 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -151,7 +151,7 @@ public void run() { LOGGER.debug(e.getMessage()); } // } - // qm.updateScheduledNotificationInfoNextExecution(scheduledNotificationRule); + qm.updateScheduledNotificationRuleToNowUtc(scheduledNotificationRule); } } catch (Exception e) { From 7974bbfad4d374ae542d2da83ba502a682027730 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Fri, 10 May 2024 13:13:36 +0200 Subject: [PATCH 269/429] fixed VulnerabilityQueryManager SQL query for new vulnerabilities Signed-off-by: Max Schiller --- .../dependencytrack/persistence/VulnerabilityQueryManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java b/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java index 05867ea402..4047fbc643 100644 --- a/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java @@ -200,7 +200,7 @@ public List getVulnerabilitiesForNpmModule(String module) { @SuppressWarnings("unchecked") public Map> getNewVulnerabilitiesForProjectsSince(ZonedDateTime lastExecution, List projectIds){ - String queryString = "PROJECT_ID, VULNERABILITY_ID " + + String queryString = "SELECT PROJECT_ID, VULNERABILITY_ID " + "FROM FINDINGATTRIBUTION " + "WHERE ATTRIBUTED_ON BETWEEN ? AND ? "; if(projectIds != null && !projectIds.isEmpty()){ From 35ed2235e57aa4b0bf3c52e7cfcf6c65caf1080c Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Mon, 13 May 2024 15:47:21 +0200 Subject: [PATCH 270/429] added basic support for scheduled publishing in notification publishers Signed-off-by: Max Schiller --- .../model/NotificationPublisher.java | 13 ++++++++++ .../DefaultNotificationPublishers.java | 15 ++++++++++- .../persistence/NotificationQueryManager.java | 25 ++++++++++-------- .../persistence/QueryManager.java | 8 +++++- .../v1/NotificationPublisherResource.java | 26 ++++++++++--------- .../util/NotificationUtil.java | 3 ++- 6 files changed, 64 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/NotificationPublisher.java b/src/main/java/org/dependencytrack/model/NotificationPublisher.java index d4016e3f39..689182cc5f 100644 --- a/src/main/java/org/dependencytrack/model/NotificationPublisher.java +++ b/src/main/java/org/dependencytrack/model/NotificationPublisher.java @@ -52,6 +52,7 @@ @Persistent(name = "template"), @Persistent(name = "templateMimeType"), @Persistent(name = "defaultPublisher"), + @Persistent(name = "publishScheduled"), @Persistent(name = "uuid"), }) }) @@ -108,6 +109,10 @@ public enum FetchGroup { @Column(name = "DEFAULT_PUBLISHER") private boolean defaultPublisher; + @Persistent(defaultFetchGroup = "true") + @Column(name = "PUBLISH_SCHEDULED") + private boolean publishScheduled; + @Persistent(defaultFetchGroup = "true", customValueStrategy = "uuid") @Unique(name = "NOTIFICATIONPUBLISHER_UUID_IDX") @Column(name = "UUID", jdbcType = "VARCHAR", length = 36, allowsNull = "false") @@ -173,6 +178,14 @@ public void setDefaultPublisher(boolean defaultPublisher) { this.defaultPublisher = defaultPublisher; } + public boolean isPublishScheduled(){ + return publishScheduled; + } + + public void setPublishScheduled(boolean publishScheduled){ + this.publishScheduled = publishScheduled; + } + @NotNull public UUID getUuid() { return uuid; diff --git a/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java b/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java index 3c5ee7c262..e602fe2b60 100644 --- a/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java +++ b/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java @@ -26,7 +26,9 @@ public enum DefaultNotificationPublishers { MS_TEAMS("Microsoft Teams", "Publishes notifications to a Microsoft Teams channel", MsTeamsPublisher.class, "/templates/notification/publisher/msteams.peb", MediaType.APPLICATION_JSON, true), MATTERMOST("Mattermost", "Publishes notifications to a Mattermost channel", MattermostPublisher.class, "/templates/notification/publisher/mattermost.peb", MediaType.APPLICATION_JSON, true), EMAIL("Email", "Sends notifications to an email address", SendMailPublisher.class, "/templates/notification/publisher/email.peb", "text/plain; charset=utf-8", true), + // SCHEDULED_EMAIL("Scheduled Email", "Sends summarized notifications to an email address in a defined schedule", SendMailPublisher.class, "/templates/notification/publisher/scheduled_email.peb", MediaType.TEXT_PLAIN, true, true), CONSOLE("Console", "Displays notifications on the system console", ConsolePublisher.class, "/templates/notification/publisher/console.peb", MediaType.TEXT_PLAIN, true), + // SCHEDULED_CONSOLE("Scheduled Console", "Displays summarized notifications on the system console in a defined schedule", ConsolePublisher.class, "/templates/notification/publisher/scheduled_console.peb", MediaType.TEXT_PLAIN, true, true), WEBHOOK("Outbound Webhook", "Publishes notifications to a configurable endpoint", WebhookPublisher.class, "/templates/notification/publisher/webhook.peb", MediaType.APPLICATION_JSON, true), CS_WEBEX("Cisco Webex", "Publishes notifications to a Cisco Webex Teams channel", CsWebexPublisher.class, "/templates/notification/publisher/cswebex.peb", MediaType.APPLICATION_JSON, true), JIRA("Jira", "Creates a Jira issue in a configurable Jira instance and queue", JiraPublisher.class, "/templates/notification/publisher/jira.peb", MediaType.APPLICATION_JSON, true); @@ -37,15 +39,22 @@ public enum DefaultNotificationPublishers { private final String templateFile; private final String templateMimeType; private final boolean defaultPublisher; + private final boolean publishScheduled; DefaultNotificationPublishers(final String name, final String description, final Class publisherClass, - final String templateFile, final String templateMimeType, final boolean defaultPublisher) { + final String templateFile, final String templateMimeType, final boolean defaultPublisher, final boolean publishScheduled) { this.name = name; this.description = description; this.publisherClass = publisherClass; this.templateFile = templateFile; this.templateMimeType = templateMimeType; this.defaultPublisher = defaultPublisher; + this.publishScheduled = publishScheduled; + } + + DefaultNotificationPublishers(final String name, final String description, final Class publisherClass, + final String templateFile, final String templateMimeType, final boolean defaultPublisher) { + this(name, description, publisherClass, templateFile, templateMimeType, defaultPublisher, false); } public String getPublisherName() { @@ -71,4 +80,8 @@ public String getTemplateMimeType() { public boolean isDefaultPublisher() { return defaultPublisher; } + + public boolean isPublishScheduled() { + return publishScheduled; + } } \ No newline at end of file diff --git a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java index 0505969f69..e652ddbda6 100644 --- a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java @@ -241,17 +241,20 @@ private NotificationPublisher getDefaultNotificationPublisher(final String clazz */ public NotificationPublisher createNotificationPublisher(final String name, final String description, final Class publisherClass, final String templateContent, - final String templateMimeType, final boolean defaultPublisher) { - return callInTransaction(() -> { - final NotificationPublisher publisher = new NotificationPublisher(); - publisher.setName(name); - publisher.setDescription(description); - publisher.setPublisherClass(publisherClass.getName()); - publisher.setTemplate(templateContent); - publisher.setTemplateMimeType(templateMimeType); - publisher.setDefaultPublisher(defaultPublisher); - return pm.makePersistent(publisher); - }); + final String templateMimeType, final boolean defaultPublisher, final boolean publishScheduled) { + pm.currentTransaction().begin(); + final NotificationPublisher publisher = new NotificationPublisher(); + publisher.setName(name); + publisher.setDescription(description); + publisher.setPublisherClass(publisherClass.getName()); + publisher.setTemplate(templateContent); + publisher.setTemplateMimeType(templateMimeType); + publisher.setDefaultPublisher(defaultPublisher); + publisher.setPublishScheduled(publishScheduled); + pm.makePersistent(publisher); + pm.currentTransaction().commit(); + pm.getFetchPlan().addGroup(NotificationPublisher.FetchGroup.ALL.name()); + return getObjectById(NotificationPublisher.class, publisher.getId()); } /** diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index c3f5d52f00..e9ef017afd 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -1295,7 +1295,13 @@ public NotificationPublisher getDefaultNotificationPublisher(final Class publisherClass, final String templateContent, final String templateMimeType, final boolean defaultPublisher) { - return getNotificationQueryManager().createNotificationPublisher(name, description, publisherClass, templateContent, templateMimeType, defaultPublisher); + return createNotificationPublisher(name, description, publisherClass, templateContent, templateMimeType, defaultPublisher, false); + } + + public NotificationPublisher createNotificationPublisher(final String name, final String description, + final Class publisherClass, final String templateContent, + final String templateMimeType, final boolean defaultPublisher, final boolean publishScheduled) { + return getNotificationQueryManager().createNotificationPublisher(name, description, publisherClass, templateContent, templateMimeType, defaultPublisher, publishScheduled); } public NotificationPublisher updateNotificationPublisher(NotificationPublisher transientPublisher) { diff --git a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java index 105c660c96..18c90128e1 100644 --- a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java @@ -145,18 +145,20 @@ public Response createNotificationPublisher(NotificationPublisher jsonNotificati return Response.status(Response.Status.BAD_REQUEST).entity("The creation of a new default publisher is forbidden").build(); } - final Class publisherClass = Class.forName(jsonNotificationPublisher.getPublisherClass()).asSubclass(Publisher.class); - final NotificationPublisher notificationPublisherCreated = qm.createNotificationPublisher( - jsonNotificationPublisher.getName(), - jsonNotificationPublisher.getDescription(), - publisherClass, - jsonNotificationPublisher.getTemplate(), - jsonNotificationPublisher.getTemplateMimeType(), - jsonNotificationPublisher.isDefaultPublisher() - ); - return Response.status(Response.Status.CREATED).entity(notificationPublisherCreated).build(); - } catch (ClassCastException e) { - return Response.status(Response.Status.BAD_REQUEST).entity("The class " + jsonNotificationPublisher.getPublisherClass() + " does not implement " + Publisher.class.getName()).build(); + Class publisherClass = Class.forName(jsonNotificationPublisher.getPublisherClass()); + + if (Publisher.class.isAssignableFrom(publisherClass)) { + Class castedPublisherClass = (Class) publisherClass; + NotificationPublisher notificationPublisherCreated = qm.createNotificationPublisher( + jsonNotificationPublisher.getName(), jsonNotificationPublisher.getDescription(), + castedPublisherClass, jsonNotificationPublisher.getTemplate(), jsonNotificationPublisher.getTemplateMimeType(), + jsonNotificationPublisher.isDefaultPublisher(), jsonNotificationPublisher.isPublishScheduled() + ); + return Response.status(Response.Status.CREATED).entity(notificationPublisherCreated).build(); + } else { + return Response.status(Response.Status.BAD_REQUEST).entity("The class "+jsonNotificationPublisher.getPublisherClass()+" does not implement "+Publisher.class.getName()).build(); + } + } catch (ClassNotFoundException e) { return Response.status(Response.Status.BAD_REQUEST).entity("The class " + jsonNotificationPublisher.getPublisherClass() + " cannot be found").build(); } diff --git a/src/main/java/org/dependencytrack/util/NotificationUtil.java b/src/main/java/org/dependencytrack/util/NotificationUtil.java index 32a74388bf..e00058c725 100644 --- a/src/main/java/org/dependencytrack/util/NotificationUtil.java +++ b/src/main/java/org/dependencytrack/util/NotificationUtil.java @@ -583,7 +583,7 @@ public static void loadDefaultNotificationPublishers(QueryManager qm) throws IOE qm.createNotificationPublisher( publisher.getPublisherName(), publisher.getPublisherDescription(), publisher.getPublisherClass(), templateContent, publisher.getTemplateMimeType(), - publisher.isDefaultPublisher() + publisher.isDefaultPublisher(), publisher.isPublishScheduled() ); } else { existingPublisher.setName(publisher.getPublisherName()); @@ -592,6 +592,7 @@ public static void loadDefaultNotificationPublishers(QueryManager qm) throws IOE existingPublisher.setTemplate(templateContent); existingPublisher.setTemplateMimeType(publisher.getTemplateMimeType()); existingPublisher.setDefaultPublisher(publisher.isDefaultPublisher()); + existingPublisher.setPublishScheduled(publisher.isPublishScheduled()); qm.updateNotificationPublisher(existingPublisher); } } From 19cb99fb7bafb209376f12be4fed349c21e87f8d Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Mon, 13 May 2024 18:49:40 +0200 Subject: [PATCH 271/429] Added API endpoints for filtering publishers Signed-off-by: Max Schiller --- .../dependencytrack/model/PublishTrigger.java | 31 ++++++++++++++ .../persistence/NotificationQueryManager.java | 25 ++++++++++- .../persistence/QueryManager.java | 5 +++ .../v1/NotificationPublisherResource.java | 41 +++++++++++++++++++ 4 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/dependencytrack/model/PublishTrigger.java diff --git a/src/main/java/org/dependencytrack/model/PublishTrigger.java b/src/main/java/org/dependencytrack/model/PublishTrigger.java new file mode 100644 index 0000000000..43c682e4f2 --- /dev/null +++ b/src/main/java/org/dependencytrack/model/PublishTrigger.java @@ -0,0 +1,31 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ + +package org.dependencytrack.model; + +/** + * Provides a list of available triggers for publishers to send notifications. + * + * @author Max Schiller + */ +public enum PublishTrigger { + ALL, + EVENT, + SCHEDULE, +} diff --git a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java index e652ddbda6..e7a981dc02 100644 --- a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java @@ -28,6 +28,7 @@ import org.dependencytrack.model.NotificationRule; import org.dependencytrack.model.Project; import org.dependencytrack.model.Tag; +import org.dependencytrack.model.PublishTrigger; import org.dependencytrack.model.ScheduledNotificationRule; import org.dependencytrack.notification.NotificationScope; import org.dependencytrack.notification.publisher.Publisher; @@ -194,12 +195,32 @@ public PaginatedResult getScheduledNotificationRules() { * This method if designed NOT to provide paginated results. * @return list of all NotificationPublisher objects */ - @SuppressWarnings("unchecked") public List getAllNotificationPublishers() { + return getAllNotificationPublishersOfType(PublishTrigger.ALL); + } + + /** + * Retrieves all NotificationPublishers matching the corresponding trigger type. + * This methoid is designed NOT to provide paginated results. + * @param trigger + * @return list of all matching NotificationPublisher objects + */ + public List getAllNotificationPublishersOfType(PublishTrigger trigger) { final Query query = pm.newQuery(NotificationPublisher.class); query.getFetchPlan().addGroup(NotificationPublisher.FetchGroup.ALL.name()); query.setOrdering("name asc"); - return (List)query.execute(); + switch (trigger) { + case SCHEDULE: + query.setFilter("publishScheduled == :publishScheduled"); + query.setParameters(true); + return List.copyOf(query.executeList()); + case EVENT: + query.setFilter("publishScheduled == :publishScheduled || publishScheduled == null"); + query.setParameters(false); + return List.copyOf(query.executeList()); + default: + return List.copyOf(query.executeList()); + } } /** diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index e9ef017afd..c2e6421dc4 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -64,6 +64,7 @@ import org.dependencytrack.model.Project; import org.dependencytrack.model.ProjectMetrics; import org.dependencytrack.model.ProjectProperty; +import org.dependencytrack.model.PublishTrigger; import org.dependencytrack.model.Repository; import org.dependencytrack.model.RepositoryMetaComponent; import org.dependencytrack.model.RepositoryType; @@ -1284,6 +1285,10 @@ public List getAllNotificationPublishers() { return getNotificationQueryManager().getAllNotificationPublishers(); } + public List getAllNotificationPublishersOfType(PublishTrigger trigger) { + return getNotificationQueryManager().getAllNotificationPublishersOfType(trigger); + } + public NotificationPublisher getNotificationPublisher(final String name) { return getNotificationQueryManager().getNotificationPublisher(name); } diff --git a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java index 18c90128e1..4d20f95c54 100644 --- a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java @@ -39,6 +39,7 @@ import org.dependencytrack.model.ConfigPropertyConstants; import org.dependencytrack.model.NotificationPublisher; import org.dependencytrack.model.NotificationRule; +import org.dependencytrack.model.PublishTrigger; import org.dependencytrack.model.validation.ValidUuid; import org.dependencytrack.notification.NotificationConstants; import org.dependencytrack.notification.NotificationGroup; @@ -107,6 +108,46 @@ public Response getAllNotificationPublishers() { } } + @GET + @Path("/event") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Returns a list of all event-driven notification publishers", + response = NotificationPublisher.class, + responseContainer = "List", + notes = "

          Requires permission SYSTEM_CONFIGURATION

          " + ) + @ApiResponses(value = { + @ApiResponse(code = 401, message = "Unauthorized") + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response getAllEventNotificationPublishers() { + try (QueryManager qm = new QueryManager()) { + final List publishers = qm.getAllNotificationPublishersOfType(PublishTrigger.EVENT); + return Response.ok(publishers).build(); + } + } + + @GET + @Path("/scheduled") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Returns a list of all scheduled notification publishers", + response = NotificationPublisher.class, + responseContainer = "List", + notes = "

          Requires permission SYSTEM_CONFIGURATION

          " + ) + @ApiResponses(value = { + @ApiResponse(code = 401, message = "Unauthorized") + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response getAllScheduledNotificationPublishers() { + try (QueryManager qm = new QueryManager()) { + final List publishers = qm.getAllNotificationPublishersOfType(PublishTrigger.SCHEDULE); + return Response.ok(publishers).build(); + } + } + @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) From 5498027b434bb785c89143cd927df4c88ad1f3c0 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Tue, 14 May 2024 09:48:49 +0200 Subject: [PATCH 272/429] Unique serialVersionUID for ScheduledNotificationRule instead of same as NotificationRule Signed-off-by: Max Schiller --- .../org/dependencytrack/model/ScheduledNotificationRule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java index 6c0b505a9f..7f9e9e9b4a 100644 --- a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java +++ b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java @@ -64,7 +64,7 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class ScheduledNotificationRule implements Serializable { - private static final long serialVersionUID = 2534439091019367263L; + private static final long serialVersionUID = 3390485832822256096L; @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.NATIVE) From e34cb0fe9ccc85f3890086faa23b901d5fb49ac3 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Tue, 14 May 2024 14:41:03 +0200 Subject: [PATCH 273/429] fixed setting last execution time on update Signed-off-by: Max Schiller --- .../dependencytrack/persistence/NotificationQueryManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java index e7a981dc02..c23a77371a 100644 --- a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java @@ -145,7 +145,6 @@ public ScheduledNotificationRule updateScheduledNotificationRule(ScheduledNotifi rule.setPublisherConfig(transientRule.getPublisherConfig()); rule.setNotifyOn(transientRule.getNotifyOn()); rule.setCronConfig(transientRule.getCronConfig()); - rule.setLastExecutionTime(transientRule.getLastExecutionTime()); rule.setPublishOnlyWithUpdates(transientRule.getPublishOnlyWithUpdates()); return persist(rule); } From 8da3ba9e955cafbc80393ca3df45761af2a24d46 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 15 May 2024 10:39:38 +0200 Subject: [PATCH 274/429] fixed wrong method usage for updating last execution time in QueryManager and Scheduled Task Signed-off-by: Max Schiller --- .../java/org/dependencytrack/persistence/QueryManager.java | 4 ++-- .../dependencytrack/tasks/SendScheduledNotificationTask.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index c2e6421dc4..bcc522761d 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -1333,8 +1333,8 @@ public ScheduledNotificationRule updateScheduledNotificationRule(ScheduledNotifi return getNotificationQueryManager().updateScheduledNotificationRule(transientRule); } - public ScheduledNotificationRule updateScheduledNotificationRuleToNowUtc(ScheduledNotificationRule transientRule) { - return getNotificationQueryManager().updateScheduledNotificationRuleToNowUtc(transientRule); + public ScheduledNotificationRule updateScheduledNotificationRuleLastExecutionTimeToNowUtc(ScheduledNotificationRule transientRule) { + return getNotificationQueryManager().updateScheduledNotificationRuleLastExecutionTimeToNowUtc(transientRule); } public PaginatedResult getScheduledNotificationRules() { diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index 1fd1441f7f..8d9a1ca135 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -151,7 +151,7 @@ public void run() { LOGGER.debug(e.getMessage()); } // } - qm.updateScheduledNotificationRuleToNowUtc(scheduledNotificationRule); + qm.updateScheduledNotificationRuleLastExecutionTimeToNowUtc(scheduledNotificationRule); } } catch (Exception e) { From bcef12195c011ca0483bc41481605d14dd340b0c Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 15 May 2024 11:13:48 +0200 Subject: [PATCH 275/429] fixed last execution to only update after successful publishing Signed-off-by: Max Schiller --- .../dependencytrack/tasks/SendScheduledNotificationTask.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index 8d9a1ca135..43aff51acf 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -146,12 +146,11 @@ public void run() { .useStartTLS(smtpSslTls) .trustCert(smtpTrustCert); sendMail.send(); + qm.updateScheduledNotificationRuleLastExecutionTimeToNowUtc(scheduledNotificationRule); } catch (SendMailException | RuntimeException e) { LOGGER.debug("Failed to send notification email "); LOGGER.debug(e.getMessage()); } - // } - qm.updateScheduledNotificationRuleLastExecutionTimeToNowUtc(scheduledNotificationRule); } } catch (Exception e) { From dc9833ea053b0c6abbbc1057217674eafc03e352 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Thu, 16 May 2024 17:14:26 +0200 Subject: [PATCH 276/429] code cleanup Signed-off-by: Max Schiller --- .../org/dependencytrack/model/ScheduledNotificationRule.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java index 7f9e9e9b4a..2421e86515 100644 --- a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java +++ b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java @@ -152,7 +152,6 @@ public class ScheduledNotificationRule implements Serializable { @Persistent(defaultFetchGroup = "true") @Column(name = "CRON_CONFIG", allowsNull = "true") // new column, must allow nulls on existing databases @JsonDeserialize(using = TrimmedStringDeserializer.class) - // @Pattern(regexp = RegexSequence.Definition.CRON, message = "The message may only contain characters valid in cron strings") private String cronConfig; @Persistent(defaultFetchGroup = "true") From 70a7f1d767c7cbb8aef93c547d28d5ca8babffef Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Thu, 16 May 2024 18:43:00 +0200 Subject: [PATCH 277/429] abstracted NotificationRule with interface for reusing existing PublishContext Signed-off-by: Max Schiller --- .../model/NotificationRule.java | 2 +- .../java/org/dependencytrack/model/Rule.java | 25 +++++++++++++++++++ .../model/ScheduledNotificationRule.java | 2 +- .../publisher/PublishContext.java | 3 ++- 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/dependencytrack/model/Rule.java diff --git a/src/main/java/org/dependencytrack/model/NotificationRule.java b/src/main/java/org/dependencytrack/model/NotificationRule.java index 8b82d7efb0..5cdf76bfac 100644 --- a/src/main/java/org/dependencytrack/model/NotificationRule.java +++ b/src/main/java/org/dependencytrack/model/NotificationRule.java @@ -62,7 +62,7 @@ @PersistenceCapable @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public class NotificationRule implements Serializable { +public class NotificationRule implements Rule, Serializable { private static final long serialVersionUID = 2534439091019367263L; diff --git a/src/main/java/org/dependencytrack/model/Rule.java b/src/main/java/org/dependencytrack/model/Rule.java new file mode 100644 index 0000000000..e8d29a61e8 --- /dev/null +++ b/src/main/java/org/dependencytrack/model/Rule.java @@ -0,0 +1,25 @@ +package org.dependencytrack.model; + +import java.util.List; +import java.util.Set; + +import org.dependencytrack.notification.NotificationGroup; +import org.dependencytrack.notification.NotificationScope; + +import alpine.model.Team; +import alpine.notification.NotificationLevel; + +public interface Rule { + public String getName(); + public boolean isEnabled(); + public boolean isNotifyChildren(); + public boolean isLogSuccessfulPublish(); + public NotificationScope getScope(); + public NotificationLevel getNotificationLevel(); + public NotificationPublisher getPublisher(); + public String getPublisherConfig(); + public Set getNotifyOn(); + public String getMessage(); + public List getProjects(); + public List getTeams(); +} diff --git a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java index 2421e86515..39bd037964 100644 --- a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java +++ b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java @@ -63,7 +63,7 @@ @PersistenceCapable @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public class ScheduledNotificationRule implements Serializable { +public class ScheduledNotificationRule implements Rule, Serializable { private static final long serialVersionUID = 3390485832822256096L; @PrimaryKey diff --git a/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java b/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java index e60a1e9287..6cb1e6dd32 100644 --- a/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java +++ b/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java @@ -21,6 +21,7 @@ import alpine.notification.Notification; import com.google.common.base.MoreObjects; import org.dependencytrack.model.NotificationRule; +import org.dependencytrack.model.Rule; import org.dependencytrack.notification.vo.AnalysisDecisionChange; import org.dependencytrack.notification.vo.BomConsumedOrProcessed; import org.dependencytrack.notification.vo.BomProcessingFailed; @@ -114,7 +115,7 @@ public static PublishContext from(final Notification notification) { * @param rule The applicable {@link NotificationRule} * @return This {@link PublishContext} */ - public PublishContext withRule(final NotificationRule rule) { + public PublishContext withRule(final Rule rule) { return new PublishContext(this.notificationGroup, this.notificationLevel, this.notificationScope, this.notificationTimestamp, this.notificationSubjects, rule.getName(), rule.getScope().name(), rule.getNotificationLevel().name(), rule.isLogSuccessfulPublish()); } From 3e5fb39e313ed1e2cf91658da64c7d20e2ccf451 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Thu, 16 May 2024 18:44:13 +0200 Subject: [PATCH 278/429] basic rebuild of scheduled publish task to match idea of multiple publishing per notification group Signed-off-by: Max Schiller --- .../tasks/SendScheduledNotificationTask.java | 204 ++++++++---------- 1 file changed, 90 insertions(+), 114 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index 43aff51acf..10edf1a87b 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -18,145 +18,121 @@ */ package org.dependencytrack.tasks; -import java.io.StringWriter; -import java.io.Writer; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ScheduledExecutorService; +import java.io.StringReader; +import java.lang.reflect.InvocationTargetException; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + import javax.json.Json; import javax.json.JsonObject; +import javax.json.JsonReader; +import org.dependencytrack.exception.PublisherException; import org.dependencytrack.model.NotificationPublisher; -import org.dependencytrack.model.PolicyViolation; import org.dependencytrack.model.Project; +import org.dependencytrack.model.Rule; import org.dependencytrack.model.ScheduledNotificationRule; -import org.dependencytrack.model.Vulnerability; +import org.dependencytrack.notification.NotificationGroup; +import org.dependencytrack.notification.publisher.PublishContext; +import org.dependencytrack.notification.publisher.Publisher; +import org.dependencytrack.notification.publisher.SendMailPublisher; +import org.dependencytrack.notification.vo.NewVulnerabilityIdentified; import org.dependencytrack.persistence.QueryManager; import alpine.common.logging.Logger; -import alpine.security.crypto.DataEncryption; -import alpine.server.mail.SendMail; -import alpine.server.mail.SendMailException; -import io.pebbletemplates.pebble.PebbleEngine; -import io.pebbletemplates.pebble.template.PebbleTemplate; - -import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_ENABLED; -import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_FROM_ADDR; -import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_PASSWORD; -import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_SERVER_HOSTNAME; -import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_SERVER_PORT; -import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_SSLTLS; -import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_TRUSTCERT; -import static org.dependencytrack.model.ConfigPropertyConstants.EMAIL_SMTP_USERNAME; +import alpine.notification.Notification; import static org.dependencytrack.notification.publisher.Publisher.CONFIG_TEMPLATE_KEY; import static org.dependencytrack.notification.publisher.Publisher.CONFIG_TEMPLATE_MIME_TYPE_KEY; public class SendScheduledNotificationTask implements Runnable { - private ScheduledNotificationRule scheduledNotificationRule; - private ScheduledExecutorService service; + private UUID scheduledNotificationRuleUuid; private static final Logger LOGGER = Logger.getLogger(SendScheduledNotificationTask.class); - public SendScheduledNotificationTask(ScheduledNotificationRule scheduledNotificationRule, ScheduledExecutorService service) { - this.scheduledNotificationRule = scheduledNotificationRule; - this.service = service; + public SendScheduledNotificationTask(UUID scheduledNotificationRuleUuid) { + this.scheduledNotificationRuleUuid = scheduledNotificationRuleUuid; } @Override public void run() { - String content = ""; - final String mimeType; - final boolean smtpEnabled; - final String smtpFrom; - final String smtpHostname; - final int smtpPort; - final String smtpUser; - final String encryptedSmtpPassword; - final boolean smtpSslTls; - final boolean smtpTrustCert; - Map> newProjectVulnerabilities; - Map> newProjectPolicyViolations; - - try (QueryManager qm = new QueryManager()) { - scheduledNotificationRule = qm.getObjectByUuid(ScheduledNotificationRule.class, scheduledNotificationRule.getUuid()); - if (scheduledNotificationRule == null) { - LOGGER.info("shutdown ExecutorService for Scheduled notification " + scheduledNotificationRule.getUuid()); - service.shutdown(); - } else { - // if (scheduledNotificationRule.getLastExecutionTime().equals(scheduledNotificationRule.getCreated())) { - // LOGGER.info("schedulednotification just created. No Information to show"); - // } else { - final List projectIds = scheduledNotificationRule.getProjects().stream().map(proj -> proj.getId()).toList(); - newProjectVulnerabilities = qm.getNewVulnerabilitiesForProjectsSince(scheduledNotificationRule.getLastExecutionTime(), projectIds); - newProjectPolicyViolations = qm.getNewPolicyViolationsForProjectsSince(scheduledNotificationRule.getLastExecutionTime(), projectIds); - - NotificationPublisher notificationPublisher = qm.getNotificationPublisher("Email"); - - JsonObject notificationPublisherConfig = Json.createObjectBuilder() - .add(CONFIG_TEMPLATE_MIME_TYPE_KEY, notificationPublisher.getTemplateMimeType()) - .add(CONFIG_TEMPLATE_KEY, notificationPublisher.getTemplate()) - .build(); - - PebbleEngine pebbleEngine = new PebbleEngine.Builder().build(); - String literalTemplate = notificationPublisherConfig.getString(CONFIG_TEMPLATE_KEY); - final PebbleTemplate template = pebbleEngine.getLiteralTemplate(literalTemplate); - mimeType = notificationPublisherConfig.getString(CONFIG_TEMPLATE_MIME_TYPE_KEY); - - final Map context = new HashMap<>(); - context.put("length", newProjectVulnerabilities.size()); - context.put("vulnerabilities", newProjectVulnerabilities); - context.put("policyviolations", newProjectPolicyViolations); - final Writer writer = new StringWriter(); - template.evaluate(writer, context); - content = writer.toString(); - - smtpEnabled = qm.isEnabled(EMAIL_SMTP_ENABLED); - if (!smtpEnabled) { - System.out.println("SMTP is not enabled; Skipping notification "); - return; + try (var qm = new QueryManager()) { + var rule = qm.getObjectByUuid(ScheduledNotificationRule.class, scheduledNotificationRuleUuid); + for (NotificationGroup group : rule.getNotifyOn()) { + final Notification notificationProxy = new Notification() + .scope(rule.getScope()) + .group(group) + .title(rule.getName()) + .level(rule.getNotificationLevel()) + .content("") // TODO: evaluate use and creation of content here + .subject(null); // TODO: generate helper class here + + final PublishContext ctx = PublishContext.from(notificationProxy); + final PublishContext ruleCtx =ctx.withRule(rule); + + // Not all publishers need configuration (i.e. ConsolePublisher) + JsonObject config = Json.createObjectBuilder().build(); + if (rule.getPublisherConfig() != null) { + try (StringReader stringReader = new StringReader(rule.getPublisherConfig()); + final JsonReader jsonReader = Json.createReader(stringReader)) { + config = jsonReader.readObject(); + } catch (Exception e) { + LOGGER.error("An error occurred while preparing the configuration for the notification publisher (%s)".formatted(ruleCtx), e); + } } - smtpFrom = qm.getConfigProperty(EMAIL_SMTP_FROM_ADDR.getGroupName(),EMAIL_SMTP_FROM_ADDR.getPropertyName()).getPropertyValue(); - smtpHostname = qm.getConfigProperty(EMAIL_SMTP_SERVER_HOSTNAME.getGroupName(),EMAIL_SMTP_SERVER_HOSTNAME.getPropertyName()).getPropertyValue(); - smtpPort = Integer.parseInt(qm.getConfigProperty(EMAIL_SMTP_SERVER_PORT.getGroupName(),EMAIL_SMTP_SERVER_PORT.getPropertyName()).getPropertyValue()); - smtpUser = qm.getConfigProperty(EMAIL_SMTP_USERNAME.getGroupName(),EMAIL_SMTP_USERNAME.getPropertyName()).getPropertyValue(); - encryptedSmtpPassword = qm.getConfigProperty(EMAIL_SMTP_PASSWORD.getGroupName(),EMAIL_SMTP_PASSWORD.getPropertyName()).getPropertyValue(); - smtpSslTls = qm.isEnabled(EMAIL_SMTP_SSLTLS); - smtpTrustCert = qm.isEnabled(EMAIL_SMTP_TRUSTCERT); - final boolean smtpAuth = (smtpUser != null && encryptedSmtpPassword != null); - final String decryptedSmtpPassword; try { - decryptedSmtpPassword = (encryptedSmtpPassword != null) ? DataEncryption.decryptAsString(encryptedSmtpPassword) : null; - } catch (Exception e) { - System.out.println("Failed to decrypt SMTP password"); - return; - } - // String[] destinations = scheduledNotificationRule.getDestinations().split(" "); - try { - final SendMail sendMail = new SendMail() - .from(smtpFrom) - // .to(destinations) - .subject("[Dependency-Track] " + "ScheduledNotification") - .body(content) - .bodyMimeType(mimeType) - .host(smtpHostname) - .port(smtpPort) - .username(smtpUser) - .password(decryptedSmtpPassword) - .smtpauth(smtpAuth) - .useStartTLS(smtpSslTls) - .trustCert(smtpTrustCert); - sendMail.send(); - qm.updateScheduledNotificationRuleLastExecutionTimeToNowUtc(scheduledNotificationRule); - } catch (SendMailException | RuntimeException e) { - LOGGER.debug("Failed to send notification email "); - LOGGER.debug(e.getMessage()); + NotificationPublisher notificationPublisher = rule.getPublisher(); + final Class publisherClass = Class.forName(notificationPublisher.getPublisherClass()); + if (Publisher.class.isAssignableFrom(publisherClass)) { + final Publisher publisher = (Publisher) publisherClass.getDeclaredConstructor().newInstance(); + JsonObject notificationPublisherConfig = Json.createObjectBuilder() + .add(CONFIG_TEMPLATE_MIME_TYPE_KEY, notificationPublisher.getTemplateMimeType()) + .add(CONFIG_TEMPLATE_KEY, notificationPublisher.getTemplate()) + .addAll(Json.createObjectBuilder(config)) + .build(); + if (publisherClass != SendMailPublisher.class || rule.getTeams().isEmpty() || rule.getTeams() == null) { + publisher.inform(ruleCtx, restrictNotificationToRuleProjects(notificationProxy, rule), notificationPublisherConfig); + } else { + ((SendMailPublisher) publisher).inform(ruleCtx, restrictNotificationToRuleProjects(notificationProxy, rule), notificationPublisherConfig, rule.getTeams()); + } + } else { + LOGGER.error("The defined notification publisher is not assignable from " + Publisher.class.getCanonicalName() + " (%s)".formatted(ruleCtx)); + } + } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException + | InvocationTargetException | IllegalAccessException e) { + LOGGER.error( + "An error occurred while instantiating a notification publisher (%s)".formatted(ruleCtx), + e); + } catch (PublisherException publisherException) { + LOGGER.error("An error occurred during the publication of the notification (%s)".formatted(ruleCtx), + publisherException); } } - - } catch (Exception e) { - LOGGER.debug(e.getMessage()); } + } + private Notification restrictNotificationToRuleProjects(final Notification initialNotification, final Rule rule) { + Notification restrictedNotification = initialNotification; + if (canRestrictNotificationToRuleProjects(initialNotification, rule)) { + Set ruleProjectsUuids = rule.getProjects().stream().map(Project::getUuid).map(UUID::toString).collect(Collectors.toSet()); + restrictedNotification = new Notification(); + restrictedNotification.setGroup(initialNotification.getGroup()); + restrictedNotification.setLevel(initialNotification.getLevel()); + restrictedNotification.scope(initialNotification.getScope()); + restrictedNotification.setContent(initialNotification.getContent()); + restrictedNotification.setTitle(initialNotification.getTitle()); + restrictedNotification.setTimestamp(initialNotification.getTimestamp()); + if (initialNotification.getSubject() instanceof final NewVulnerabilityIdentified subject) { + Set restrictedProjects = subject.getAffectedProjects().stream().filter(project -> ruleProjectsUuids.contains(project.getUuid().toString())).collect(Collectors.toSet()); + NewVulnerabilityIdentified restrictedSubject = new NewVulnerabilityIdentified(subject.getVulnerability(), subject.getComponent(), restrictedProjects, null); + restrictedNotification.setSubject(restrictedSubject); + } + } + return restrictedNotification; } + private boolean canRestrictNotificationToRuleProjects(final Notification initialNotification, final Rule rule) { + return initialNotification.getSubject() instanceof NewVulnerabilityIdentified + && rule.getProjects() != null + && !rule.getProjects().isEmpty(); + } } From 43fb487110ce5d69e4027c0fa915da188bb6382e Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Fri, 17 May 2024 12:22:22 +0200 Subject: [PATCH 279/429] null checks in query managers for new events since last scheduled execution Signed-off-by: Max Schiller --- .../org/dependencytrack/persistence/PolicyQueryManager.java | 3 +++ .../dependencytrack/persistence/VulnerabilityQueryManager.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index 8e21f4a3e5..22ed0edb76 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -419,6 +419,9 @@ public Map> getNewPolicyViolationsForProjectsSinc for(Object[] obj : totalList){ Project project = getObjectById(Project.class, obj[0]); PolicyViolation policyViolation = getObjectById(PolicyViolation.class, obj[1]); + if(project == null || policyViolation == null){ + continue; + } if(!projectPolicyViolations.containsKey(project)){ projectPolicyViolations.put(project, new ArrayList<>()); } diff --git a/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java b/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java index 4047fbc643..5bd0bd7f74 100644 --- a/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java @@ -213,6 +213,9 @@ public Map> getNewVulnerabilitiesForProjectsSince(Z for(Object[] obj : totalList){ Project project = getObjectById(Project.class, obj[0]); Vulnerability vulnerability = getObjectById(Vulnerability.class, obj[1]); + if(project == null || vulnerability == null){ + continue; + } if(!projectVulnerabilities.containsKey(project)){ projectVulnerabilities.put(project, new ArrayList<>()); } From 49367454a430721abeec23c833aa5e5ed4f18a6e Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Fri, 17 May 2024 12:23:16 +0200 Subject: [PATCH 280/429] generation of basic notification content in task Signed-off-by: Max Schiller --- .../tasks/SendScheduledNotificationTask.java | 68 +++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index 10edf1a87b..a067e5a0e7 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -20,6 +20,9 @@ import java.io.StringReader; import java.lang.reflect.InvocationTargetException; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -30,9 +33,11 @@ import org.dependencytrack.exception.PublisherException; import org.dependencytrack.model.NotificationPublisher; +import org.dependencytrack.model.PolicyViolation; import org.dependencytrack.model.Project; import org.dependencytrack.model.Rule; import org.dependencytrack.model.ScheduledNotificationRule; +import org.dependencytrack.model.Vulnerability; import org.dependencytrack.notification.NotificationGroup; import org.dependencytrack.notification.publisher.PublishContext; import org.dependencytrack.notification.publisher.Publisher; @@ -58,13 +63,40 @@ public void run() { try (var qm = new QueryManager()) { var rule = qm.getObjectByUuid(ScheduledNotificationRule.class, scheduledNotificationRuleUuid); for (NotificationGroup group : rule.getNotifyOn()) { + final List projectIds = rule.getProjects().stream().map(proj -> proj.getId()).toList(); final Notification notificationProxy = new Notification() .scope(rule.getScope()) .group(group) - .title(rule.getName()) - .level(rule.getNotificationLevel()) - .content("") // TODO: evaluate use and creation of content here - .subject(null); // TODO: generate helper class here + .title(generateNotificationTitle(rule, group)) + .level(rule.getNotificationLevel()); + + switch (group) { + case NEW_VULNERABILITY: + var newProjectVulnerabilities = qm.getNewVulnerabilitiesForProjectsSince(rule.getLastExecutionTime(), projectIds); + if(newProjectVulnerabilities.isEmpty() && rule.getPublishOnlyWithUpdates()) + continue; + notificationProxy + .content(generateVulnerabilityNotificationContent(rule, + newProjectVulnerabilities.values().stream().flatMap(List::stream).toList(), + newProjectVulnerabilities.keySet().stream().toList(), + rule.getLastExecutionTime())) + .subject(null); // TODO: generate helper class here + break; + case POLICY_VIOLATION: + var newProjectPolicyViolations = qm.getNewPolicyViolationsForProjectsSince(rule.getLastExecutionTime(), projectIds); + if(newProjectPolicyViolations.isEmpty() && rule.getPublishOnlyWithUpdates()) + continue; + notificationProxy + .content(generatePolicyNotificationContent(rule, + newProjectPolicyViolations.values().stream().flatMap(List::stream).toList(), + newProjectPolicyViolations.keySet().stream().toList(), + rule.getLastExecutionTime())) + .subject(null); // TODO: generate helper class here + break; + default: + LOGGER.error(group.name() + " is not a supported notification group for scheduled publishing"); + continue; + } final PublishContext ctx = PublishContext.from(notificationProxy); final PublishContext ruleCtx =ctx.withRule(rule); @@ -135,4 +167,32 @@ private boolean canRestrictNotificationToRuleProjects(final Notification initial && rule.getProjects() != null && !rule.getProjects().isEmpty(); } + + private String generateNotificationTitle(final Rule rule, final NotificationGroup group) { + return "Scheduled Notification: " + group.name(); + } + + private String generateVulnerabilityNotificationContent(final Rule rule, final List vulnerabilities, final List projects, final ZonedDateTime lastExecutionTime) { + final String content; + + if (vulnerabilities.isEmpty()) { + content = "No new vulnerabilities found."; + } else { + content = "In total, " + vulnerabilities.size() + " new vulnerabilities in " + projects.size() + " projects were found since " + lastExecutionTime.toLocalDateTime().truncatedTo(ChronoUnit.SECONDS) + "."; + } + + return content; + } + + private String generatePolicyNotificationContent(final Rule rule, final List policyViolations, final List projects, final ZonedDateTime lastExecutionTime) { + final String content; + + if (policyViolations.isEmpty()) { + content = "No new policy violations found."; + } else { + content = "In total, " + policyViolations.size() + " new policy violations in " + projects.size() + " projects were found since " + lastExecutionTime.toLocalDateTime().truncatedTo(ChronoUnit.SECONDS) + "."; + } + + return content; + } } From a996fe6ca7e062575cc2c47502edabe5a83a4a3e Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Fri, 24 May 2024 09:20:21 +0200 Subject: [PATCH 281/429] fixed missing header part in Rule Signed-off-by: Max Schiller --- .../java/org/dependencytrack/model/Rule.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/org/dependencytrack/model/Rule.java b/src/main/java/org/dependencytrack/model/Rule.java index e8d29a61e8..e3ac5e3f81 100644 --- a/src/main/java/org/dependencytrack/model/Rule.java +++ b/src/main/java/org/dependencytrack/model/Rule.java @@ -1,3 +1,21 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ package org.dependencytrack.model; import java.util.List; From bea408bea20d7c7736fbf8e46805815d6433bbf5 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Fri, 24 May 2024 09:22:21 +0200 Subject: [PATCH 282/429] fixed query in policy and vulnerability querymanagers when project limitation is missing Signed-off-by: Max Schiller --- .../persistence/PolicyQueryManager.java | 11 +++++++---- .../persistence/VulnerabilityQueryManager.java | 7 +++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index 22ed0edb76..ba39c942ba 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -407,14 +407,17 @@ public List getAllPolicyViolations(final Project project) { @SuppressWarnings("unchecked") public Map> getNewPolicyViolationsForProjectsSince(ZonedDateTime dateTime, List projectIds){ String queryString = "SELECT PROJECT_ID, ID " + - "FROM POLICYVIOLATION " + - "WHERE (TIMESTAMP BETWEEN ? AND ?) "; - if(projectIds != null && !projectIds.isEmpty()){ + "FROM POLICYVIOLATION " + + "WHERE (TIMESTAMP BETWEEN ? AND ?) "; + boolean hasProjectLimitation = projectIds != null && !projectIds.isEmpty(); + if(hasProjectLimitation){ queryString.concat("AND (PROJECT_ID IN ?) "); } queryString.concat("ORDER BY PROJECT_ID ASC"); final Query query = pm.newQuery(JDOQuery.SQL_QUERY_LANGUAGE, queryString); - final List totalList = (List)query.execute(dateTime, ZonedDateTime.now(ZoneOffset.UTC), projectIds); + final List totalList = hasProjectLimitation + ? (List) query.execute(dateTime, ZonedDateTime.now(ZoneOffset.UTC), projectIds) + : (List) query.execute(dateTime, ZonedDateTime.now(ZoneOffset.UTC)); Map> projectPolicyViolations = new HashMap<>(); for(Object[] obj : totalList){ Project project = getObjectById(Project.class, obj[0]); diff --git a/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java b/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java index 5bd0bd7f74..3e62cf5680 100644 --- a/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java @@ -203,12 +203,15 @@ public Map> getNewVulnerabilitiesForProjectsSince(Z String queryString = "SELECT PROJECT_ID, VULNERABILITY_ID " + "FROM FINDINGATTRIBUTION " + "WHERE ATTRIBUTED_ON BETWEEN ? AND ? "; - if(projectIds != null && !projectIds.isEmpty()){ + boolean hasProjectLimitation = projectIds != null && !projectIds.isEmpty(); + if(hasProjectLimitation){ queryString.concat("AND PROJECT_ID IN ? "); } queryString.concat("ORDER BY PROJECT_ID ASC, VULNERABILITY_ID ASC"); final Query query = pm.newQuery(JDOQuery.SQL_QUERY_LANGUAGE, queryString); - final List totalList = (List)query.execute(lastExecution, ZonedDateTime.now(ZoneOffset.UTC), projectIds); + final List totalList = hasProjectLimitation + ? (List) query.execute(lastExecution, ZonedDateTime.now(ZoneOffset.UTC), projectIds) + : (List) query.execute(lastExecution, ZonedDateTime.now(ZoneOffset.UTC)); Map> projectVulnerabilities = new HashMap<>(); for(Object[] obj : totalList){ Project project = getObjectById(Project.class, obj[0]); From 56043242f3c7c328d579b921a9aae9c4ceeb125f Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Fri, 24 May 2024 09:39:38 +0200 Subject: [PATCH 283/429] changed retrieval of default publishers from db to support multiple default publishers with same publisher class Signed-off-by: Max Schiller --- .../persistence/NotificationQueryManager.java | 13 +++++++------ .../dependencytrack/persistence/QueryManager.java | 5 +++-- .../resources/v1/NotificationPublisherResource.java | 8 ++++---- .../org/dependencytrack/util/NotificationUtil.java | 2 +- .../v1/NotificationPublisherResourceTest.java | 10 +++++----- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java index c23a77371a..f5187d0be2 100644 --- a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java @@ -31,6 +31,7 @@ import org.dependencytrack.model.PublishTrigger; import org.dependencytrack.model.ScheduledNotificationRule; import org.dependencytrack.notification.NotificationScope; +import org.dependencytrack.notification.publisher.DefaultNotificationPublishers; import org.dependencytrack.notification.publisher.Publisher; import javax.jdo.PersistenceManager; @@ -238,8 +239,8 @@ public NotificationPublisher getNotificationPublisher(final String name) { * @param clazz The Class of the NotificationPublisher * @return a NotificationPublisher */ - public NotificationPublisher getDefaultNotificationPublisher(final Class clazz) { - return getDefaultNotificationPublisher(clazz.getCanonicalName()); + public NotificationPublisher getDefaultNotificationPublisher(final DefaultNotificationPublishers defaultPublisher) { + return getDefaultNotificationPublisher(defaultPublisher.getPublisherName(), defaultPublisher.getPublisherClass().getCanonicalName()); } /** @@ -247,11 +248,11 @@ public NotificationPublisher getDefaultNotificationPublisher(final Class query = pm.newQuery(NotificationPublisher.class, "publisherClass == :publisherClass && defaultPublisher == true"); + private NotificationPublisher getDefaultNotificationPublisher(final String publisherName, final String clazz) { + final Query query = pm.newQuery(NotificationPublisher.class, "name == :name && publisherClass == :publisherClass && defaultPublisher == true"); query.getFetchPlan().addGroup(NotificationPublisher.FetchGroup.ALL.name()); query.setRange(0, 1); - return singleResult(query.execute(clazz)); + return singleResult(query.execute(publisherName, clazz)); } /** @@ -286,7 +287,7 @@ public NotificationPublisher updateNotificationPublisher(NotificationPublisher t if (transientPublisher.getId() > 0) { publisher = getObjectById(NotificationPublisher.class, transientPublisher.getId()); } else if (transientPublisher.isDefaultPublisher()) { - publisher = getDefaultNotificationPublisher(transientPublisher.getPublisherClass()); + publisher = getDefaultNotificationPublisher(transientPublisher.getName(), transientPublisher.getPublisherClass()); } if (publisher != null) { publisher.setName(transientPublisher.getName()); diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index bcc522761d..170999c83d 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -81,6 +81,7 @@ import org.dependencytrack.model.VulnerabilityMetrics; import org.dependencytrack.model.VulnerableSoftware; import org.dependencytrack.notification.NotificationScope; +import org.dependencytrack.notification.publisher.DefaultNotificationPublishers; import org.dependencytrack.notification.publisher.Publisher; import org.dependencytrack.resources.v1.vo.AffectedProject; import org.dependencytrack.resources.v1.vo.DependencyGraphResponse; @@ -1293,8 +1294,8 @@ public NotificationPublisher getNotificationPublisher(final String name) { return getNotificationQueryManager().getNotificationPublisher(name); } - public NotificationPublisher getDefaultNotificationPublisher(final Class clazz) { - return getNotificationQueryManager().getDefaultNotificationPublisher(clazz); + public NotificationPublisher getDefaultNotificationPublisher(final DefaultNotificationPublishers defaultPublisher) { + return getNotificationQueryManager().getDefaultNotificationPublisher(defaultPublisher); } public NotificationPublisher createNotificationPublisher(final String name, final String description, diff --git a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java index 4d20f95c54..356fab64ad 100644 --- a/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/NotificationPublisherResource.java @@ -44,9 +44,9 @@ import org.dependencytrack.notification.NotificationConstants; import org.dependencytrack.notification.NotificationGroup; import org.dependencytrack.notification.NotificationScope; +import org.dependencytrack.notification.publisher.DefaultNotificationPublishers; import org.dependencytrack.notification.publisher.PublishContext; import org.dependencytrack.notification.publisher.Publisher; -import org.dependencytrack.notification.publisher.SendMailPublisher; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.util.NotificationUtil; @@ -347,9 +347,9 @@ public Response restoreDefaultTemplates() { @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) public Response testSmtpPublisherConfig(@FormParam("destination") String destination) { try(QueryManager qm = new QueryManager()) { - Class defaultEmailPublisherClass = SendMailPublisher.class; - NotificationPublisher emailNotificationPublisher = qm.getDefaultNotificationPublisher(defaultEmailPublisherClass); - final Publisher emailPublisher = defaultEmailPublisherClass.getDeclaredConstructor().newInstance(); + DefaultNotificationPublishers defaultEmailPublisher = DefaultNotificationPublishers.EMAIL; + NotificationPublisher emailNotificationPublisher = qm.getDefaultNotificationPublisher(defaultEmailPublisher); + final Publisher emailPublisher = (Publisher) defaultEmailPublisher.getPublisherClass().getDeclaredConstructor().newInstance(); final JsonObject config = Json.createObjectBuilder() .add(Publisher.CONFIG_DESTINATION, destination) .add(Publisher.CONFIG_TEMPLATE_KEY, emailNotificationPublisher.getTemplate()) diff --git a/src/main/java/org/dependencytrack/util/NotificationUtil.java b/src/main/java/org/dependencytrack/util/NotificationUtil.java index e00058c725..0c14108522 100644 --- a/src/main/java/org/dependencytrack/util/NotificationUtil.java +++ b/src/main/java/org/dependencytrack/util/NotificationUtil.java @@ -578,7 +578,7 @@ public static void loadDefaultNotificationPublishers(QueryManager qm) throws IOE } } final String templateContent = FileUtils.readFileToString(templateFile, UTF_8); - final NotificationPublisher existingPublisher = qm.getDefaultNotificationPublisher(publisher.getPublisherClass()); + final NotificationPublisher existingPublisher = qm.getDefaultNotificationPublisher(publisher); if (existingPublisher == null) { qm.createNotificationPublisher( publisher.getPublisherName(), publisher.getPublisherDescription(), diff --git a/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java index 677303f372..add6158cb8 100644 --- a/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/NotificationPublisherResourceTest.java @@ -219,7 +219,7 @@ public void updateUnknownNotificationPublisherTest() { @Test public void updateExistingDefaultNotificationPublisherTest() { - NotificationPublisher notificationPublisher = qm.getDefaultNotificationPublisher(SendMailPublisher.class); + NotificationPublisher notificationPublisher = qm.getDefaultNotificationPublisher(DefaultNotificationPublishers.EMAIL); notificationPublisher.setName(notificationPublisher.getName() + " Updated"); Response response = jersey.target(V1_NOTIFICATION_PUBLISHER).request() .header(X_API_KEY, apiKey) @@ -324,8 +324,8 @@ public void deleteUnknownNotificationPublisherTest() { @Test public void deleteDefaultNotificationPublisherTest() { - NotificationPublisher notificationPublisher = qm.getDefaultNotificationPublisher(SendMailPublisher.class); - Response response = jersey.target(V1_NOTIFICATION_PUBLISHER + "/" + notificationPublisher.getUuid()).request() + NotificationPublisher notificationPublisher = qm.getDefaultNotificationPublisher(DefaultNotificationPublishers.EMAIL); + Response response = target(V1_NOTIFICATION_PUBLISHER + "/" + notificationPublisher.getUuid()).request() .header(X_API_KEY, apiKey) .delete(); Assert.assertEquals(400, response.getStatus(), 0); @@ -370,7 +370,7 @@ public void testNotificationRuleTest() { @Test public void restoreDefaultTemplatesTest() { - NotificationPublisher slackPublisher = qm.getDefaultNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherClass()); + NotificationPublisher slackPublisher = qm.getDefaultNotificationPublisher(DefaultNotificationPublishers.SLACK); slackPublisher.setName(slackPublisher.getName()+" Updated"); qm.persist(slackPublisher); qm.detach(NotificationPublisher.class, slackPublisher.getId()); @@ -387,7 +387,7 @@ public void restoreDefaultTemplatesTest() { qm.getPersistenceManager().refreshAll(); Assert.assertEquals(200, response.getStatus(), 0); Assert.assertFalse(qm.isEnabled(ConfigPropertyConstants.NOTIFICATION_TEMPLATE_DEFAULT_OVERRIDE_ENABLED)); - slackPublisher = qm.getDefaultNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherClass()); + slackPublisher = qm.getDefaultNotificationPublisher(DefaultNotificationPublishers.SLACK); Assert.assertEquals(DefaultNotificationPublishers.SLACK.getPublisherName(), slackPublisher.getName()); } } From 604669a5740be3a3ae826ec9a09cb550a4f067d3 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Fri, 24 May 2024 12:12:20 +0200 Subject: [PATCH 284/429] fixed missing detach for scheduled notification items Signed-off-by: Max Schiller --- .../org/dependencytrack/persistence/PolicyQueryManager.java | 3 ++- .../dependencytrack/persistence/VulnerabilityQueryManager.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java index ba39c942ba..05569e8177 100644 --- a/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/PolicyQueryManager.java @@ -425,10 +425,11 @@ public Map> getNewPolicyViolationsForProjectsSinc if(project == null || policyViolation == null){ continue; } + var detachedPolicyViolation = pm.detachCopy(policyViolation); if(!projectPolicyViolations.containsKey(project)){ projectPolicyViolations.put(project, new ArrayList<>()); } - projectPolicyViolations.get(project).add(policyViolation); + projectPolicyViolations.get(project).add(detachedPolicyViolation); } return projectPolicyViolations; } diff --git a/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java b/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java index 3e62cf5680..47c6cb6b4c 100644 --- a/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/VulnerabilityQueryManager.java @@ -219,10 +219,11 @@ public Map> getNewVulnerabilitiesForProjectsSince(Z if(project == null || vulnerability == null){ continue; } + var detachedVulnerability = pm.detachCopy(vulnerability); if(!projectVulnerabilities.containsKey(project)){ projectVulnerabilities.put(project, new ArrayList<>()); } - projectVulnerabilities.get(project).add(vulnerability); + projectVulnerabilities.get(project).add(detachedVulnerability); } return projectVulnerabilities; } From a8c209f63810babf0c1002b250827340a1d5e0be Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Mon, 27 May 2024 15:59:33 +0200 Subject: [PATCH 285/429] added scheduled default publisher with testing email template, support for first iteration of new vulnerabilities and policy violation summary Signed-off-by: Max Schiller --- .../DefaultNotificationPublishers.java | 4 +- .../publisher/PublishContext.java | 7 ++ .../notification/publisher/Publisher.java | 8 ++ ...ScheduledNewVulnerabilitiesIdentified.java | 63 +++++++++++++ .../ScheduledPolicyViolationsIdentified.java | 45 +++++++++ .../util/NotificationUtil.java | 91 +++++++++++++++++++ .../publisher/scheduled_email.peb | 44 +++++++++ 7 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentified.java create mode 100644 src/main/java/org/dependencytrack/notification/vo/ScheduledPolicyViolationsIdentified.java create mode 100644 src/main/resources/templates/notification/publisher/scheduled_email.peb diff --git a/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java b/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java index e602fe2b60..8e22a3141b 100644 --- a/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java +++ b/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java @@ -25,8 +25,8 @@ public enum DefaultNotificationPublishers { SLACK("Slack", "Publishes notifications to a Slack channel", SlackPublisher.class, "/templates/notification/publisher/slack.peb", MediaType.APPLICATION_JSON, true), MS_TEAMS("Microsoft Teams", "Publishes notifications to a Microsoft Teams channel", MsTeamsPublisher.class, "/templates/notification/publisher/msteams.peb", MediaType.APPLICATION_JSON, true), MATTERMOST("Mattermost", "Publishes notifications to a Mattermost channel", MattermostPublisher.class, "/templates/notification/publisher/mattermost.peb", MediaType.APPLICATION_JSON, true), - EMAIL("Email", "Sends notifications to an email address", SendMailPublisher.class, "/templates/notification/publisher/email.peb", "text/plain; charset=utf-8", true), - // SCHEDULED_EMAIL("Scheduled Email", "Sends summarized notifications to an email address in a defined schedule", SendMailPublisher.class, "/templates/notification/publisher/scheduled_email.peb", MediaType.TEXT_PLAIN, true, true), + EMAIL("Email", "Sends notifications to an email address", SendMailPublisher.class, "/templates/notification/publisher/email.peb", MediaType.TEXT_PLAIN, true), + SCHEDULED_EMAIL("Scheduled Email", "Sends summarized notifications to an email address in a defined schedule", SendMailPublisher.class, "/templates/notification/publisher/scheduled_email.peb", MediaType.TEXT_PLAIN, true, true), CONSOLE("Console", "Displays notifications on the system console", ConsolePublisher.class, "/templates/notification/publisher/console.peb", MediaType.TEXT_PLAIN, true), // SCHEDULED_CONSOLE("Scheduled Console", "Displays summarized notifications on the system console in a defined schedule", ConsolePublisher.class, "/templates/notification/publisher/scheduled_console.peb", MediaType.TEXT_PLAIN, true, true), WEBHOOK("Outbound Webhook", "Publishes notifications to a configurable endpoint", WebhookPublisher.class, "/templates/notification/publisher/webhook.peb", MediaType.APPLICATION_JSON, true), diff --git a/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java b/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java index 6cb1e6dd32..208b9ff5ad 100644 --- a/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java +++ b/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java @@ -28,6 +28,8 @@ import org.dependencytrack.notification.vo.NewVulnerabilityIdentified; import org.dependencytrack.notification.vo.NewVulnerableDependency; import org.dependencytrack.notification.vo.PolicyViolationIdentified; +import org.dependencytrack.notification.vo.ScheduledNewVulnerabilitiesIdentified; +import org.dependencytrack.notification.vo.ScheduledPolicyViolationsIdentified; import org.dependencytrack.notification.vo.VexConsumedOrProcessed; import org.dependencytrack.notification.vo.ViolationAnalysisDecisionChange; @@ -102,6 +104,11 @@ public static PublishContext from(final Notification notification) { notificationSubjects.put(SUBJECT_VULNERABILITY, Vulnerability.convert(subject.getVulnerability())); } else if (notification.getSubject() instanceof final VexConsumedOrProcessed subject) { notificationSubjects.put(SUBJECT_PROJECT, Project.convert(subject.getProject())); + } else if (notification.getSubject() instanceof final ScheduledNewVulnerabilitiesIdentified subject) { + notificationSubjects.put(SUBJECT_PROJECTS, subject.getNewProjectVulnerabilities().keySet().stream().map(Project::convert).toList()); + notificationSubjects.put(SUBJECT_VULNERABILITIES, subject.getNewVulnerabilitiesTotal().stream().map(Vulnerability::convert).toList()); + } else if (notification.getSubject() instanceof final ScheduledPolicyViolationsIdentified subject) { + notificationSubjects.put(SUBJECT_PROJECTS, subject.getNewProjectPolicyViolations().keySet().stream().map(Project::convert).toList()); } return new PublishContext(notification.getGroup(), Optional.ofNullable(notification.getLevel()).map(Enum::name).orElse(null), diff --git a/src/main/java/org/dependencytrack/notification/publisher/Publisher.java b/src/main/java/org/dependencytrack/notification/publisher/Publisher.java index 50a319d92f..c69eade286 100644 --- a/src/main/java/org/dependencytrack/notification/publisher/Publisher.java +++ b/src/main/java/org/dependencytrack/notification/publisher/Publisher.java @@ -34,6 +34,8 @@ import org.dependencytrack.notification.vo.NewVulnerabilityIdentified; import org.dependencytrack.notification.vo.NewVulnerableDependency; import org.dependencytrack.notification.vo.PolicyViolationIdentified; +import org.dependencytrack.notification.vo.ScheduledNewVulnerabilitiesIdentified; +import org.dependencytrack.notification.vo.ScheduledPolicyViolationsIdentified; import org.dependencytrack.notification.vo.VexConsumedOrProcessed; import org.dependencytrack.notification.vo.ViolationAnalysisDecisionChange; import org.dependencytrack.persistence.QueryManager; @@ -130,6 +132,12 @@ default String prepareTemplate(final Notification notification, final PebbleTemp } else if (notification.getSubject() instanceof final PolicyViolationIdentified subject) { context.put("subject", subject); context.put("subjectJson", NotificationUtil.toJson(subject)); + } else if (notification.getSubject() instanceof final ScheduledNewVulnerabilitiesIdentified subject) { + context.put("subject", subject); + context.put("subjectJson", NotificationUtil.toJson(subject)); + } else if (notification.getSubject() instanceof final ScheduledPolicyViolationsIdentified subject) { + context.put("subject", subject); + context.put("subjectJson", NotificationUtil.toJson(subject)); } } else if (NotificationScope.SYSTEM.name().equals(notification.getScope())) { if (notification.getSubject() instanceof final UserPrincipal subject) { diff --git a/src/main/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentified.java b/src/main/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentified.java new file mode 100644 index 0000000000..6a25b94a2e --- /dev/null +++ b/src/main/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentified.java @@ -0,0 +1,63 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.notification.vo; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.dependencytrack.model.Project; +import org.dependencytrack.model.Severity; +import org.dependencytrack.model.Vulnerability; + +public class ScheduledNewVulnerabilitiesIdentified { + private final Map> newProjectVulnerabilities; + private final Map>> newProjectVulnerabilitiesBySeverity; + private final List newVulnerabilitiesTotal; + private final Map> newVulnerabilitiesTotalBySeverity; + + public ScheduledNewVulnerabilitiesIdentified(Map> newProjectVulnerabilities) { + this.newProjectVulnerabilities = newProjectVulnerabilities; + this.newProjectVulnerabilitiesBySeverity = newProjectVulnerabilities.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().stream() + .collect(Collectors.groupingBy(Vulnerability::getSeverity, () -> new EnumMap<>(Severity.class), Collectors.toList())))); + this.newVulnerabilitiesTotal = newProjectVulnerabilities.values().stream() + .flatMap(List::stream) + .collect(Collectors.toList()); + this.newVulnerabilitiesTotalBySeverity = newVulnerabilitiesTotal.stream() + .collect(Collectors.groupingBy(Vulnerability::getSeverity, () -> new EnumMap<>(Severity.class), Collectors.toList())); + } + + public Map> getNewProjectVulnerabilities() { + return newProjectVulnerabilities; + } + + public Map>> getNewProjectVulnerabilitiesBySeverity() { + return newProjectVulnerabilitiesBySeverity; + } + + public List getNewVulnerabilitiesTotal() { + return newVulnerabilitiesTotal; + } + + public Map> getNewVulnerabilitiesTotalBySeverity() { + return newVulnerabilitiesTotalBySeverity; + } +} diff --git a/src/main/java/org/dependencytrack/notification/vo/ScheduledPolicyViolationsIdentified.java b/src/main/java/org/dependencytrack/notification/vo/ScheduledPolicyViolationsIdentified.java new file mode 100644 index 0000000000..e046415ffa --- /dev/null +++ b/src/main/java/org/dependencytrack/notification/vo/ScheduledPolicyViolationsIdentified.java @@ -0,0 +1,45 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.notification.vo; + +import java.util.List; +import java.util.Map; + +import org.dependencytrack.model.PolicyViolation; +import org.dependencytrack.model.Project; + +public class ScheduledPolicyViolationsIdentified { + private final Map> newProjectPolicyViolations; + private final List newPolicyViolationsTotal; + + public ScheduledPolicyViolationsIdentified(Map> newProjectPolicyViolations) { + this.newProjectPolicyViolations = newProjectPolicyViolations; + this.newPolicyViolationsTotal = newProjectPolicyViolations.values().stream() + .flatMap(List::stream) + .collect(java.util.stream.Collectors.toList()); + } + + public Map> getNewProjectPolicyViolations() { + return newProjectPolicyViolations; + } + + public List getNewPolicyViolationsTotal() { + return newPolicyViolationsTotal; + } +} diff --git a/src/main/java/org/dependencytrack/util/NotificationUtil.java b/src/main/java/org/dependencytrack/util/NotificationUtil.java index 0c14108522..ba1b9d9ef3 100644 --- a/src/main/java/org/dependencytrack/util/NotificationUtil.java +++ b/src/main/java/org/dependencytrack/util/NotificationUtil.java @@ -54,6 +54,8 @@ import org.dependencytrack.notification.vo.NewVulnerabilityIdentified; import org.dependencytrack.notification.vo.NewVulnerableDependency; import org.dependencytrack.notification.vo.PolicyViolationIdentified; +import org.dependencytrack.notification.vo.ScheduledNewVulnerabilitiesIdentified; +import org.dependencytrack.notification.vo.ScheduledPolicyViolationsIdentified; import org.dependencytrack.notification.vo.VexConsumedOrProcessed; import org.dependencytrack.notification.vo.ViolationAnalysisDecisionChange; import org.dependencytrack.parser.common.resolver.CweResolver; @@ -564,6 +566,95 @@ public static JsonObject toJson(final Policy policy) { return builder.build(); } + public static JsonObject toJson(final ScheduledNewVulnerabilitiesIdentified vo) { + final JsonObjectBuilder builder = Json.createObjectBuilder(); + + if (vo.getNewProjectVulnerabilities() != null && vo.getNewProjectVulnerabilities().size() > 0) { + final JsonArrayBuilder projectsBuilder = Json.createArrayBuilder(); + for (final Map.Entry> entry : vo.getNewProjectVulnerabilities().entrySet()) { + final JsonObjectBuilder projectBuilder = Json.createObjectBuilder(); + projectBuilder.add("project", toJson(entry.getKey())); + final JsonArrayBuilder vulnsBuilder = Json.createArrayBuilder(); + for (final Vulnerability vulnerability : entry.getValue()) { + vulnsBuilder.add(toJson(vulnerability)); + } + projectBuilder.add("vulnerabilities", vulnsBuilder.build()); + projectsBuilder.add(projectBuilder.build()); + } + builder.add("newProjectVulnerabilities", projectsBuilder.build()); + } + if(vo.getNewProjectVulnerabilitiesBySeverity() != null && vo.getNewProjectVulnerabilitiesBySeverity().size() > 0) { + final JsonArrayBuilder projectsBuilder = Json.createArrayBuilder(); + for (final Map.Entry>> entry : vo.getNewProjectVulnerabilitiesBySeverity().entrySet()) { + final JsonObjectBuilder projectBuilder = Json.createObjectBuilder(); + projectBuilder.add("project", toJson(entry.getKey())); + final JsonArrayBuilder vulnsBySeverityBuilder = Json.createArrayBuilder(); + for (final Map.Entry> vulnEntry : entry.getValue().entrySet()) { + final JsonObjectBuilder severityBuilder = Json.createObjectBuilder(); + severityBuilder.add("severity", vulnEntry.getKey().name()); + final JsonArrayBuilder vulnsBuilder = Json.createArrayBuilder(); + for (final Vulnerability vulnerability : vulnEntry.getValue()) { + vulnsBuilder.add(toJson(vulnerability)); + } + severityBuilder.add("vulnerabilities", vulnsBuilder.build()); + vulnsBySeverityBuilder.add(severityBuilder.build()); + } + projectBuilder.add("vulnerabilitiesBySeverity", vulnsBySeverityBuilder.build()); + projectsBuilder.add(projectBuilder.build()); + } + builder.add("newProjectVulnerabilitiesBySeverity", projectsBuilder.build()); + } + if (vo.getNewVulnerabilitiesTotal() != null && vo.getNewVulnerabilitiesTotal().size() > 0) { + final JsonArrayBuilder vulnsBuilder = Json.createArrayBuilder(); + for (final Vulnerability vulnerability : vo.getNewVulnerabilitiesTotal()) { + vulnsBuilder.add(toJson(vulnerability)); + } + builder.add("newVulnerabilitiesTotal", vulnsBuilder.build()); + } + if(vo.getNewVulnerabilitiesTotalBySeverity() != null && vo.getNewVulnerabilitiesTotalBySeverity().size() > 0) { + final JsonArrayBuilder vulnsBySeverityBuilder = Json.createArrayBuilder(); + for (final Map.Entry> vulnEntry : vo.getNewVulnerabilitiesTotalBySeverity().entrySet()) { + final JsonObjectBuilder severityBuilder = Json.createObjectBuilder(); + severityBuilder.add("severity", vulnEntry.getKey().name()); + final JsonArrayBuilder vulnsBuilder = Json.createArrayBuilder(); + for (final Vulnerability vulnerability : vulnEntry.getValue()) { + vulnsBuilder.add(toJson(vulnerability)); + } + severityBuilder.add("vulnerabilities", vulnsBuilder.build()); + vulnsBySeverityBuilder.add(severityBuilder.build()); + } + builder.add("newVulnerabilitiesTotalBySeverity", vulnsBySeverityBuilder.build()); + } + + return builder.build(); + } + + public static JsonObject toJson(ScheduledPolicyViolationsIdentified vo) { + final JsonObjectBuilder builder = Json.createObjectBuilder(); + if (vo.getNewProjectPolicyViolations() != null && vo.getNewProjectPolicyViolations().size() > 0) { + final JsonArrayBuilder projectsBuilder = Json.createArrayBuilder(); + for (final Map.Entry> entry : vo.getNewProjectPolicyViolations().entrySet()) { + final JsonObjectBuilder projectBuilder = Json.createObjectBuilder(); + projectBuilder.add("project", toJson(entry.getKey())); + final JsonArrayBuilder violationsBuilder = Json.createArrayBuilder(); + for (final PolicyViolation policyViolation : entry.getValue()) { + violationsBuilder.add(toJson(policyViolation)); + } + projectBuilder.add("policyViolations", violationsBuilder.build()); + projectsBuilder.add(projectBuilder.build()); + } + builder.add("newProjectPolicyViolations", projectsBuilder.build()); + } + if (vo.getNewPolicyViolationsTotal() != null && vo.getNewPolicyViolationsTotal().size() > 0) { + final JsonArrayBuilder violationsBuilder = Json.createArrayBuilder(); + for (final PolicyViolation policyViolation : vo.getNewPolicyViolationsTotal()) { + violationsBuilder.add(toJson(policyViolation)); + } + builder.add("newPolicyViolationsTotal", violationsBuilder.build()); + } + return builder.build(); + } + public static void loadDefaultNotificationPublishers(QueryManager qm) throws IOException { for (final DefaultNotificationPublishers publisher : DefaultNotificationPublishers.values()) { File templateFile = new File(URLDecoder.decode(NotificationUtil.class.getResource(publisher.getPublisherTemplateFile()).getFile(), UTF_8.name())); diff --git a/src/main/resources/templates/notification/publisher/scheduled_email.peb b/src/main/resources/templates/notification/publisher/scheduled_email.peb new file mode 100644 index 0000000000..b5c3df931a --- /dev/null +++ b/src/main/resources/templates/notification/publisher/scheduled_email.peb @@ -0,0 +1,44 @@ +{{ notification.title }} + +{{ notification.content }} + +=================================================================================================== + +{% if notification.group == "NEW_VULNERABILITY" %} +{% for entry in subject.newProjectVulnerabilitiesBySeverity %} +Project-Name: {{ entry.key.name }} +Project-URL: {{ baseUrl }}/projects/{{ entry.key.uuid }} +{% for severityEntry in entry.value %} + - {{ severityEntry.key }}: {{ severityEntry.value|length }} new vulnerabilities +{% endfor %} +{% endfor %} + +--------------------------------------------------------------------------------------------------- + +Details to all {{ subject.newVulnerabilitiesTotal|length }} new vulnerabilities: +{% for entry in subject.newProjectVulnerabilities %} +------------------------------------------------- +Project-Name: {{ entry.key.name }} +{% for vuln in entry.value %} + +ID: {{ vuln.vulnId }} +Severity: {{ vuln.severity }} +Description: {{ vuln.description }} +Source: {{ vuln.source }} +{% endfor %} +{% endfor %} +{% elseif notification.group == "POLICY_VIOLATION" %} + +New Policy Violations: {{ subject.newPolicyViolationsTotal|length }} +PolicyViolations: +{% for projectEntry in subject.newProjectPolicyViolations %} +{{ projectEntry.key.name }} ({{ projectEntry.key.uuid }}) +{% for policyEntry in projectEntry.value %} +- [{{ policyEntry.type }}] {{ policyEntry.timestamp }} {{ policyEntry.policyCondition }} +{% endfor %} +{% endfor %} +{% endif %} + +=================================================================================================== + +{{ timestamp }} \ No newline at end of file From a83f23e23fef0eff6a63dc18ac3ed6c96a11aadd Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Tue, 28 May 2024 14:59:16 +0200 Subject: [PATCH 286/429] modified scheduled task to deliver test data with new subject classes Signed-off-by: Max Schiller --- .../tasks/SendScheduledNotificationTask.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index a067e5a0e7..c2c42a4afb 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -20,6 +20,7 @@ import java.io.StringReader; import java.lang.reflect.InvocationTargetException; +import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.util.List; @@ -43,6 +44,8 @@ import org.dependencytrack.notification.publisher.Publisher; import org.dependencytrack.notification.publisher.SendMailPublisher; import org.dependencytrack.notification.vo.NewVulnerabilityIdentified; +import org.dependencytrack.notification.vo.ScheduledNewVulnerabilitiesIdentified; +import org.dependencytrack.notification.vo.ScheduledPolicyViolationsIdentified; import org.dependencytrack.persistence.QueryManager; import alpine.common.logging.Logger; @@ -62,8 +65,9 @@ public SendScheduledNotificationTask(UUID scheduledNotificationRuleUuid) { public void run() { try (var qm = new QueryManager()) { var rule = qm.getObjectByUuid(ScheduledNotificationRule.class, scheduledNotificationRuleUuid); + final List projectIds = rule.getProjects().stream().map(proj -> proj.getId()).toList(); + for (NotificationGroup group : rule.getNotifyOn()) { - final List projectIds = rule.getProjects().stream().map(proj -> proj.getId()).toList(); final Notification notificationProxy = new Notification() .scope(rule.getScope()) .group(group) @@ -75,23 +79,25 @@ public void run() { var newProjectVulnerabilities = qm.getNewVulnerabilitiesForProjectsSince(rule.getLastExecutionTime(), projectIds); if(newProjectVulnerabilities.isEmpty() && rule.getPublishOnlyWithUpdates()) continue; + ScheduledNewVulnerabilitiesIdentified vulnSubject = new ScheduledNewVulnerabilitiesIdentified(newProjectVulnerabilities); notificationProxy .content(generateVulnerabilityNotificationContent(rule, - newProjectVulnerabilities.values().stream().flatMap(List::stream).toList(), + vulnSubject.getNewVulnerabilitiesTotal(), newProjectVulnerabilities.keySet().stream().toList(), rule.getLastExecutionTime())) - .subject(null); // TODO: generate helper class here + .subject(vulnSubject); break; case POLICY_VIOLATION: - var newProjectPolicyViolations = qm.getNewPolicyViolationsForProjectsSince(rule.getLastExecutionTime(), projectIds); + var newProjectPolicyViolations = qm.getNewPolicyViolationsForProjectsSince(ZonedDateTime.of(2023, 05, 20, 0, 0, 0, 0, ZoneId.systemDefault())/* rule.getLastExecutionTime() */, projectIds); if(newProjectPolicyViolations.isEmpty() && rule.getPublishOnlyWithUpdates()) continue; + ScheduledPolicyViolationsIdentified policySubject = new ScheduledPolicyViolationsIdentified(newProjectPolicyViolations); notificationProxy .content(generatePolicyNotificationContent(rule, - newProjectPolicyViolations.values().stream().flatMap(List::stream).toList(), + policySubject.getNewPolicyViolationsTotal(), newProjectPolicyViolations.keySet().stream().toList(), rule.getLastExecutionTime())) - .subject(null); // TODO: generate helper class here + .subject(policySubject); break; default: LOGGER.error(group.name() + " is not a supported notification group for scheduled publishing"); From 6c70bd38a9efa4e197a8db261b46fcecf4c04767 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Tue, 28 May 2024 15:01:21 +0200 Subject: [PATCH 287/429] added cron task management on CRUD operations with automatic re-scheduling after completion Signed-off-by: Max Schiller --- .../v1/ScheduledNotificationRuleResource.java | 62 +++++++++++++++++++ .../tasks/ActionOnDoneFutureTask.java | 44 +++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 src/main/java/org/dependencytrack/tasks/ActionOnDoneFutureTask.java diff --git a/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java index f068ce7646..cd71a442ee 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java @@ -40,6 +40,11 @@ import org.dependencytrack.notification.publisher.SendMailPublisher; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.resources.v1.openapi.PaginatedApi; +import org.dependencytrack.tasks.ActionOnDoneFutureTask; +import org.dependencytrack.tasks.SendScheduledNotificationTask; + +import com.asahaf.javacron.InvalidExpressionException; +import com.asahaf.javacron.Schedule; import javax.validation.Validator; import javax.ws.rs.Consumes; @@ -52,7 +57,17 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; import java.util.List; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; /** * JAX-RS resources for processing scheduled notification rules. @@ -64,6 +79,7 @@ public class ScheduledNotificationRuleResource extends AlpineResource { private static final Logger LOGGER = Logger.getLogger(ScheduledNotificationRuleResource.class); + private static final HashMap> SCHEDULED_NOTIFY_TASKS = new HashMap>(); @GET @Produces(MediaType.APPLICATION_JSON) @@ -122,10 +138,42 @@ public Response createScheduledNotificationRule(ScheduledNotificationRule jsonRu jsonRule.getNotificationLevel(), publisher ); + + if(rule.isEnabled()) { + Schedule schedule; + try { + schedule = Schedule.create(jsonRule.getCronConfig()); + scheduleNextRuleTask(rule.getUuid(), schedule); + } catch (InvalidExpressionException e) { + return Response.status(Response.Status.BAD_REQUEST).entity("Invalid cron expression").build(); + } + } + return Response.status(Response.Status.CREATED).entity(rule).build(); } } + private void scheduleNextRuleTask(UUID ruleUuid, Schedule schedule) { + var scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); + var futureTask = new ActionOnDoneFutureTask(new SendScheduledNotificationTask(ruleUuid), () -> scheduleNextRuleTask(ruleUuid, schedule)); + + var future = scheduledExecutor.schedule( + futureTask, + schedule.nextDuration(TimeUnit.MILLISECONDS), + TimeUnit.MILLISECONDS); + SCHEDULED_NOTIFY_TASKS.put(ruleUuid, future); + + LOGGER.info(">>>>>>>>>> Scheduled notification task for rule " + ruleUuid + " @ " + LocalDateTime.ofInstant(Instant.ofEpochMilli(schedule.nextDuration(TimeUnit.MILLISECONDS)), ZoneId.systemDefault()).truncatedTo(ChronoUnit.SECONDS) + " >>>>>>>>>>"); + } + + private void cancelActiveRuleTask(UUID ruleUuid) { + if (SCHEDULED_NOTIFY_TASKS.containsKey(ruleUuid)) { + SCHEDULED_NOTIFY_TASKS.get(ruleUuid).cancel(true); + SCHEDULED_NOTIFY_TASKS.remove(ruleUuid); + LOGGER.info("<<<<<<<<<< Canceled scheduled notification task for rule " + ruleUuid + " <<<<<<<<<<<<"); + } + } + @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @@ -153,6 +201,17 @@ public Response updateScheduledNotificationRule(ScheduledNotificationRule jsonRu if (rule != null) { jsonRule.setName(StringUtils.trimToNull(jsonRule.getName())); rule = qm.updateScheduledNotificationRule(jsonRule); + + try { + cancelActiveRuleTask(jsonRule.getUuid()); + if (rule.isEnabled()) { + var schedule = Schedule.create(jsonRule.getCronConfig()); + scheduleNextRuleTask(jsonRule.getUuid(), schedule); + } + } catch (InvalidExpressionException e) { + return Response.status(Response.Status.BAD_REQUEST).entity("Invalid cron expression").build(); + } + return Response.ok(rule).build(); } else { return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the scheduled notification rule could not be found.").build(); @@ -178,6 +237,9 @@ public Response deleteScheduledNotificationRule(ScheduledNotificationRule jsonRu final ScheduledNotificationRule rule = qm.getObjectByUuid(ScheduledNotificationRule.class, jsonRule.getUuid()); if (rule != null) { qm.delete(rule); + + cancelActiveRuleTask(jsonRule.getUuid()); + return Response.status(Response.Status.NO_CONTENT).build(); } else { return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the scheduled notification rule could not be found.").build(); diff --git a/src/main/java/org/dependencytrack/tasks/ActionOnDoneFutureTask.java b/src/main/java/org/dependencytrack/tasks/ActionOnDoneFutureTask.java new file mode 100644 index 0000000000..f926fa11b9 --- /dev/null +++ b/src/main/java/org/dependencytrack/tasks/ActionOnDoneFutureTask.java @@ -0,0 +1,44 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.tasks; + +import java.util.concurrent.FutureTask; + +import alpine.common.logging.Logger; + +public class ActionOnDoneFutureTask extends FutureTask { + private static final Logger LOGGER = Logger.getLogger(ActionOnDoneFutureTask.class); + private final Runnable action; + + public ActionOnDoneFutureTask(Runnable runnable, Runnable actionOnDone) { + super(runnable, null); + this.action = actionOnDone; + } + + @Override + protected void done() { + super.done(); + try { + this.action.run(); + } catch (Exception e) { + // just catch and log, do not interfere with completion + LOGGER.warn(e.toString()); + } + } +} From 2263fd82eeb9c4b3c4fb2e2b2538a657e92366ce Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Tue, 28 May 2024 15:08:19 +0200 Subject: [PATCH 288/429] removed test date in scheduled task Signed-off-by: Max Schiller --- .../dependencytrack/tasks/SendScheduledNotificationTask.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index c2c42a4afb..86b0ec9a8e 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -20,7 +20,6 @@ import java.io.StringReader; import java.lang.reflect.InvocationTargetException; -import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.util.List; @@ -88,7 +87,7 @@ public void run() { .subject(vulnSubject); break; case POLICY_VIOLATION: - var newProjectPolicyViolations = qm.getNewPolicyViolationsForProjectsSince(ZonedDateTime.of(2023, 05, 20, 0, 0, 0, 0, ZoneId.systemDefault())/* rule.getLastExecutionTime() */, projectIds); + var newProjectPolicyViolations = qm.getNewPolicyViolationsForProjectsSince(rule.getLastExecutionTime(), projectIds); if(newProjectPolicyViolations.isEmpty() && rule.getPublishOnlyWithUpdates()) continue; ScheduledPolicyViolationsIdentified policySubject = new ScheduledPolicyViolationsIdentified(newProjectPolicyViolations); From aa009b1a642d44e17e830804a2843986df00760d Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Tue, 28 May 2024 15:08:56 +0200 Subject: [PATCH 289/429] fixed missing update of last execution time after successful publish Signed-off-by: Max Schiller --- .../dependencytrack/tasks/SendScheduledNotificationTask.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index 86b0ec9a8e..f6584fe53d 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -131,6 +131,9 @@ public void run() { } else { ((SendMailPublisher) publisher).inform(ruleCtx, restrictNotificationToRuleProjects(notificationProxy, rule), notificationPublisherConfig, rule.getTeams()); } + + // update last execution time after successful publication to avoid duplicate notifications in the next run + qm.updateScheduledNotificationRuleLastExecutionTimeToNowUtc(rule); } else { LOGGER.error("The defined notification publisher is not assignable from " + Publisher.class.getCanonicalName() + " (%s)".formatted(ruleCtx)); } From ac504eb1824ad252338c7aa01656baba7cbfb5e1 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 29 May 2024 10:31:43 +0200 Subject: [PATCH 290/429] initialize scheduled notification tasks at startup Signed-off-by: Max Schiller --- .../ScheduledNotificationTaskInitializer.java | 58 +++++++++++++++ .../ScheduledNotificationTaskManager.java | 71 +++++++++++++++++++ .../v1/ScheduledNotificationRuleResource.java | 47 +++--------- src/main/webapp/WEB-INF/web.xml | 3 + 4 files changed, 140 insertions(+), 39 deletions(-) create mode 100644 src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskInitializer.java create mode 100644 src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskManager.java diff --git a/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskInitializer.java b/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskInitializer.java new file mode 100644 index 0000000000..99f18dbb2b --- /dev/null +++ b/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskInitializer.java @@ -0,0 +1,58 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.notification; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.dependencytrack.model.ScheduledNotificationRule; +import org.dependencytrack.persistence.QueryManager; + +import com.asahaf.javacron.InvalidExpressionException; +import com.asahaf.javacron.Schedule; + +import alpine.common.logging.Logger; + +public class ScheduledNotificationTaskInitializer implements ServletContextListener { + private static final Logger LOGGER = Logger.getLogger(ScheduledNotificationTaskInitializer.class); + + @Override + public void contextInitialized(ServletContextEvent sce) { + try (var qm = new QueryManager()) { + var paginatedScheduledRules = qm.getScheduledNotificationRules(); + var scheduledRulesList = paginatedScheduledRules.getList(ScheduledNotificationRule.class); + for (var scheduledRule : scheduledRulesList) { + try { + if (scheduledRule.isEnabled()) { + ScheduledNotificationTaskManager.scheduleNextRuleTask( + scheduledRule.getUuid(), + Schedule.create(scheduledRule.getCronConfig())); + } + } catch (InvalidExpressionException e) { + LOGGER.error("Invalid cron expression in rule " + scheduledRule.getUuid() + ", no cron task could be created!"); + } + } + } + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + ScheduledNotificationTaskManager.cancelAllActiveRuleTasks(); + } +} diff --git a/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskManager.java b/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskManager.java new file mode 100644 index 0000000000..6da003aca4 --- /dev/null +++ b/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskManager.java @@ -0,0 +1,71 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.notification; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.dependencytrack.tasks.ActionOnDoneFutureTask; +import org.dependencytrack.tasks.SendScheduledNotificationTask; + +import com.asahaf.javacron.Schedule; + +import alpine.common.logging.Logger; + +public final class ScheduledNotificationTaskManager { + private static final Logger LOGGER = Logger.getLogger(ScheduledNotificationTaskManager.class); + private static final HashMap> SCHEDULED_NOTIFY_TASKS = new HashMap>(); + + public static void scheduleNextRuleTask(UUID ruleUuid, Schedule schedule) { + var scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); + var futureTask = new ActionOnDoneFutureTask(new SendScheduledNotificationTask(ruleUuid), () -> scheduleNextRuleTask(ruleUuid, schedule)); + + var future = scheduledExecutor.schedule( + futureTask, + schedule.nextDuration(TimeUnit.MILLISECONDS), + TimeUnit.MILLISECONDS); + SCHEDULED_NOTIFY_TASKS.put(ruleUuid, future); + + LOGGER.info(">>>>>>>>>> Scheduled notification task for rule " + ruleUuid + " @ " + LocalDateTime + .ofInstant(Instant.ofEpochMilli(schedule.nextDuration(TimeUnit.MILLISECONDS)), ZoneId.systemDefault()) + .truncatedTo(ChronoUnit.SECONDS) + " >>>>>>>>>>"); + } + + public static void cancelActiveRuleTask(UUID ruleUuid) { + if (SCHEDULED_NOTIFY_TASKS.containsKey(ruleUuid)) { + SCHEDULED_NOTIFY_TASKS.get(ruleUuid).cancel(true); + SCHEDULED_NOTIFY_TASKS.remove(ruleUuid); + LOGGER.info("<<<<<<<<<< Canceled scheduled notification task for rule " + ruleUuid + " <<<<<<<<<<<<"); + } + } + + public static void cancelAllActiveRuleTasks(){ + for (var future : SCHEDULED_NOTIFY_TASKS.values()) { + future.cancel(true); + } + SCHEDULED_NOTIFY_TASKS.clear(); + } +} diff --git a/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java index cd71a442ee..46b53b67df 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java @@ -37,12 +37,10 @@ import org.dependencytrack.model.ScheduledNotificationRule; import org.dependencytrack.model.validation.ValidUuid; import org.dependencytrack.notification.NotificationScope; +import org.dependencytrack.notification.ScheduledNotificationTaskManager; import org.dependencytrack.notification.publisher.SendMailPublisher; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.resources.v1.openapi.PaginatedApi; -import org.dependencytrack.tasks.ActionOnDoneFutureTask; -import org.dependencytrack.tasks.SendScheduledNotificationTask; - import com.asahaf.javacron.InvalidExpressionException; import com.asahaf.javacron.Schedule; @@ -58,16 +56,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.temporal.ChronoUnit; -import java.util.HashMap; import java.util.List; -import java.util.UUID; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; /** * JAX-RS resources for processing scheduled notification rules. @@ -79,7 +68,6 @@ public class ScheduledNotificationRuleResource extends AlpineResource { private static final Logger LOGGER = Logger.getLogger(ScheduledNotificationRuleResource.class); - private static final HashMap> SCHEDULED_NOTIFY_TASKS = new HashMap>(); @GET @Produces(MediaType.APPLICATION_JSON) @@ -143,8 +131,9 @@ public Response createScheduledNotificationRule(ScheduledNotificationRule jsonRu Schedule schedule; try { schedule = Schedule.create(jsonRule.getCronConfig()); - scheduleNextRuleTask(rule.getUuid(), schedule); - } catch (InvalidExpressionException e) { + ScheduledNotificationTaskManager.scheduleNextRuleTask(rule.getUuid(), schedule); + } catch (InvalidExpressionException e) { + LOGGER.error("Cron expression is invalid: " + jsonRule.getCronConfig()); return Response.status(Response.Status.BAD_REQUEST).entity("Invalid cron expression").build(); } } @@ -153,27 +142,6 @@ public Response createScheduledNotificationRule(ScheduledNotificationRule jsonRu } } - private void scheduleNextRuleTask(UUID ruleUuid, Schedule schedule) { - var scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); - var futureTask = new ActionOnDoneFutureTask(new SendScheduledNotificationTask(ruleUuid), () -> scheduleNextRuleTask(ruleUuid, schedule)); - - var future = scheduledExecutor.schedule( - futureTask, - schedule.nextDuration(TimeUnit.MILLISECONDS), - TimeUnit.MILLISECONDS); - SCHEDULED_NOTIFY_TASKS.put(ruleUuid, future); - - LOGGER.info(">>>>>>>>>> Scheduled notification task for rule " + ruleUuid + " @ " + LocalDateTime.ofInstant(Instant.ofEpochMilli(schedule.nextDuration(TimeUnit.MILLISECONDS)), ZoneId.systemDefault()).truncatedTo(ChronoUnit.SECONDS) + " >>>>>>>>>>"); - } - - private void cancelActiveRuleTask(UUID ruleUuid) { - if (SCHEDULED_NOTIFY_TASKS.containsKey(ruleUuid)) { - SCHEDULED_NOTIFY_TASKS.get(ruleUuid).cancel(true); - SCHEDULED_NOTIFY_TASKS.remove(ruleUuid); - LOGGER.info("<<<<<<<<<< Canceled scheduled notification task for rule " + ruleUuid + " <<<<<<<<<<<<"); - } - } - @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @@ -203,12 +171,13 @@ public Response updateScheduledNotificationRule(ScheduledNotificationRule jsonRu rule = qm.updateScheduledNotificationRule(jsonRule); try { - cancelActiveRuleTask(jsonRule.getUuid()); + ScheduledNotificationTaskManager.cancelActiveRuleTask(jsonRule.getUuid()); if (rule.isEnabled()) { var schedule = Schedule.create(jsonRule.getCronConfig()); - scheduleNextRuleTask(jsonRule.getUuid(), schedule); + ScheduledNotificationTaskManager.scheduleNextRuleTask(jsonRule.getUuid(), schedule); } } catch (InvalidExpressionException e) { + LOGGER.error("Cron expression is invalid: " + jsonRule.getCronConfig()); return Response.status(Response.Status.BAD_REQUEST).entity("Invalid cron expression").build(); } @@ -238,7 +207,7 @@ public Response deleteScheduledNotificationRule(ScheduledNotificationRule jsonRu if (rule != null) { qm.delete(rule); - cancelActiveRuleTask(jsonRule.getUuid()); + ScheduledNotificationTaskManager.cancelActiveRuleTask(jsonRule.getUuid()); return Response.status(Response.Status.NO_CONTENT).build(); } else { diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 339838a5f3..ad86cd00d5 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -57,6 +57,9 @@ org.dependencytrack.persistence.H2WebConsoleInitializer + + org.dependencytrack.notification.ScheduledNotificationTaskInitializer + WhitelistUrlFilter From eb6e24b02e3b18dd528c08950c6b21de147b55d4 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 29 May 2024 12:19:48 +0200 Subject: [PATCH 291/429] added option to run scheduled notification rule manually instant Signed-off-by: Max Schiller --- .../ScheduledNotificationTaskManager.java | 28 +++++------ .../v1/ScheduledNotificationRuleResource.java | 48 +++++++++++++++++++ 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskManager.java b/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskManager.java index 6da003aca4..c70f281ae2 100644 --- a/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskManager.java +++ b/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskManager.java @@ -18,10 +18,6 @@ */ package org.dependencytrack.notification; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.temporal.ChronoUnit; import java.util.HashMap; import java.util.UUID; import java.util.concurrent.Executors; @@ -33,32 +29,36 @@ import com.asahaf.javacron.Schedule; -import alpine.common.logging.Logger; - public final class ScheduledNotificationTaskManager { - private static final Logger LOGGER = Logger.getLogger(ScheduledNotificationTaskManager.class); private static final HashMap> SCHEDULED_NOTIFY_TASKS = new HashMap>(); + public static void scheduleNextRuleTask(UUID ruleUuid, Schedule schedule, long customDelay, TimeUnit delayUnit) { + scheduleNextRuleTask(ruleUuid, schedule, customDelay, delayUnit, () -> scheduleNextRuleTask(ruleUuid, schedule)); + } + public static void scheduleNextRuleTask(UUID ruleUuid, Schedule schedule) { + scheduleNextRuleTask(ruleUuid, schedule, schedule.nextDuration(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS); + } + + public static void scheduleNextRuleTaskOnce(UUID ruleUuid, long customDelay, TimeUnit delayUnit){ + scheduleNextRuleTask(ruleUuid, null, customDelay, delayUnit, () -> cancelActiveRuleTask(ruleUuid)); + } + + private static void scheduleNextRuleTask(UUID ruleUuid, Schedule schedule, long customDelay, TimeUnit delayUnit, Runnable actionAfterTaskCompletion){ var scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); - var futureTask = new ActionOnDoneFutureTask(new SendScheduledNotificationTask(ruleUuid), () -> scheduleNextRuleTask(ruleUuid, schedule)); + var futureTask = new ActionOnDoneFutureTask(new SendScheduledNotificationTask(ruleUuid), actionAfterTaskCompletion); var future = scheduledExecutor.schedule( futureTask, - schedule.nextDuration(TimeUnit.MILLISECONDS), + customDelay, TimeUnit.MILLISECONDS); SCHEDULED_NOTIFY_TASKS.put(ruleUuid, future); - - LOGGER.info(">>>>>>>>>> Scheduled notification task for rule " + ruleUuid + " @ " + LocalDateTime - .ofInstant(Instant.ofEpochMilli(schedule.nextDuration(TimeUnit.MILLISECONDS)), ZoneId.systemDefault()) - .truncatedTo(ChronoUnit.SECONDS) + " >>>>>>>>>>"); } public static void cancelActiveRuleTask(UUID ruleUuid) { if (SCHEDULED_NOTIFY_TASKS.containsKey(ruleUuid)) { SCHEDULED_NOTIFY_TASKS.get(ruleUuid).cancel(true); SCHEDULED_NOTIFY_TASKS.remove(ruleUuid); - LOGGER.info("<<<<<<<<<< Canceled scheduled notification task for rule " + ruleUuid + " <<<<<<<<<<<<"); } } diff --git a/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java index 46b53b67df..60e345ac7b 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java @@ -57,6 +57,7 @@ import javax.ws.rs.core.Response; import java.util.List; +import java.util.concurrent.TimeUnit; /** * JAX-RS resources for processing scheduled notification rules. @@ -342,6 +343,53 @@ public Response addTeamToRule( } } + @POST + @Path("/execute") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Executes a scheduled notification rule instantly ignoring the cron expression", + response = ScheduledNotificationRule.class, + notes = "

          Requires permission SYSTEM_CONFIGURATION

          " + ) + @ApiResponses(value = { + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 404, message = "The UUID of the scheduled notification rule could not be found") + }) + @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) + public Response executeScheduledNotificationRuleNow(ScheduledNotificationRule jsonRule) { + final Validator validator = super.getValidator(); + failOnValidationError( + validator.validateProperty(jsonRule, "name"), + validator.validateProperty(jsonRule, "publisherConfig"), + validator.validateProperty(jsonRule, "cronConfig"), + validator.validateProperty(jsonRule, "lastExecutionTime") + ); + + try (QueryManager qm = new QueryManager()) { + ScheduledNotificationRule rule = qm.getObjectByUuid(ScheduledNotificationRule.class, jsonRule.getUuid()); + if (rule != null) { + try { + ScheduledNotificationTaskManager.cancelActiveRuleTask(jsonRule.getUuid()); + if (rule.isEnabled()) { + // schedule must be passed too, to schedule the next execution according to cron expression again + var schedule = Schedule.create(jsonRule.getCronConfig()); + ScheduledNotificationTaskManager.scheduleNextRuleTask(jsonRule.getUuid(), schedule, 0, TimeUnit.MILLISECONDS); + } else { + ScheduledNotificationTaskManager.scheduleNextRuleTaskOnce(jsonRule.getUuid(), 0, TimeUnit.MILLISECONDS); + } + } catch (InvalidExpressionException e) { + LOGGER.error("Cron expression is invalid: " + jsonRule.getCronConfig()); + return Response.status(Response.Status.BAD_REQUEST).entity("Invalid cron expression").build(); + } + + return Response.ok(rule).build(); + } else { + return Response.status(Response.Status.NOT_FOUND).entity("The UUID of the scheduled notification rule could not be found.").build(); + } + } + } + @DELETE @Path("/{ruleUuid}/team/{teamUuid}") @Consumes(MediaType.APPLICATION_JSON) From 0174bf7857327c37b4dff85d1eb269331edda82e Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 29 May 2024 14:06:51 +0200 Subject: [PATCH 292/429] support to read default cron expression from environment variables Signed-off-by: Max Schiller --- .../org/dependencytrack/model/ConfigPropertyConstants.java | 2 +- .../org/dependencytrack/model/ScheduledNotificationRule.java | 4 ++-- .../dependencytrack/persistence/NotificationQueryManager.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java index 64c2912231..5482da61cb 100644 --- a/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java +++ b/src/main/java/org/dependencytrack/model/ConfigPropertyConstants.java @@ -98,7 +98,7 @@ public enum ConfigPropertyConstants { ACCESS_MANAGEMENT_ACL_ENABLED("access-management", "acl.enabled", "false", PropertyType.BOOLEAN, "Flag to enable/disable access control to projects in the portfolio", true), NOTIFICATION_TEMPLATE_BASE_DIR("notification", "template.baseDir", SystemUtils.getEnvironmentVariable("DEFAULT_TEMPLATES_OVERRIDE_BASE_DIRECTORY", System.getProperty("user.home")), PropertyType.STRING, "The base directory to use when searching for notification templates"), NOTIFICATION_TEMPLATE_DEFAULT_OVERRIDE_ENABLED("notification", "template.default.override.enabled", SystemUtils.getEnvironmentVariable("DEFAULT_TEMPLATES_OVERRIDE_ENABLED", "false"), PropertyType.BOOLEAN, "Flag to enable/disable override of default notification templates"), - NOTIFICATION_CRON_DEFAULT_INTERVAL("notification", "cron.default.interval", "0 12 * * *", PropertyType.STRING, "The default interval of scheduled notifications as cron expression (every day at 12pm)"), + NOTIFICATION_CRON_DEFAULT_EXPRESSION("notification", "cron.default.expression", SystemUtils.getEnvironmentVariable("DEFAULT_SCHEDULED_CRON_EXPRESSION", "0 12 * * *"), PropertyType.STRING, "The default interval of scheduled notifications as cron expression"), TASK_SCHEDULER_LDAP_SYNC_CADENCE("task-scheduler", "ldap.sync.cadence", "6", PropertyType.INTEGER, "Sync cadence (in hours) for LDAP"), TASK_SCHEDULER_GHSA_MIRROR_CADENCE("task-scheduler", "ghsa.mirror.cadence", "24", PropertyType.INTEGER, "Mirror cadence (in hours) for Github Security Advisories"), TASK_SCHEDULER_OSV_MIRROR_CADENCE("task-scheduler", "osv.mirror.cadence", "24", PropertyType.INTEGER, "Mirror cadence (in hours) for OSV database"), diff --git a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java index 39bd037964..ceec5483af 100644 --- a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java +++ b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java @@ -299,7 +299,7 @@ public void setUuid(@NotNull UUID uuid) { } public String getCronConfig() { - var cronConfig = ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue(); + var cronConfig = ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_EXPRESSION.getDefaultPropertyValue(); if (this.cronConfig != null) { cronConfig = this.cronConfig; } @@ -308,7 +308,7 @@ public String getCronConfig() { public void setCronConfig(String cronConfig) { if (cronConfig == null) { - this.cronConfig = ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue(); + this.cronConfig = ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_EXPRESSION.getDefaultPropertyValue(); return; } this.cronConfig = cronConfig; diff --git a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java index f5187d0be2..25797837ee 100644 --- a/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/NotificationQueryManager.java @@ -105,7 +105,7 @@ public ScheduledNotificationRule createScheduledNotificationRule(String name, No rule.setEnabled(true); rule.setNotifyChildren(true); rule.setLogSuccessfulPublish(false); - rule.setCronConfig(ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_INTERVAL.getDefaultPropertyValue()); + rule.setCronConfig(ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_EXPRESSION.getDefaultPropertyValue()); rule.setLastExecutionTime(ZonedDateTime.now(ZoneOffset.UTC)); rule.setPublishOnlyWithUpdates(false); return persist(rule); From 5e21db0d32250aa8e371bf8f670cdcae7b1342df Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 29 May 2024 14:30:45 +0200 Subject: [PATCH 293/429] update last execution time of rule without publishing, if no errors occured during task execution Signed-off-by: Max Schiller --- .../tasks/SendScheduledNotificationTask.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index f6584fe53d..e3bd21396a 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -65,6 +65,8 @@ public void run() { try (var qm = new QueryManager()) { var rule = qm.getObjectByUuid(ScheduledNotificationRule.class, scheduledNotificationRuleUuid); final List projectIds = rule.getProjects().stream().map(proj -> proj.getId()).toList(); + Boolean errorsDuringExecution = false; + Boolean atLeastOneSuccessfulPublish = false; for (NotificationGroup group : rule.getNotifyOn()) { final Notification notificationProxy = new Notification() @@ -100,6 +102,7 @@ public void run() { break; default: LOGGER.error(group.name() + " is not a supported notification group for scheduled publishing"); + errorsDuringExecution |= true; continue; } @@ -114,6 +117,7 @@ public void run() { config = jsonReader.readObject(); } catch (Exception e) { LOGGER.error("An error occurred while preparing the configuration for the notification publisher (%s)".formatted(ruleCtx), e); + errorsDuringExecution |= true; } } try { @@ -131,22 +135,27 @@ public void run() { } else { ((SendMailPublisher) publisher).inform(ruleCtx, restrictNotificationToRuleProjects(notificationProxy, rule), notificationPublisherConfig, rule.getTeams()); } - - // update last execution time after successful publication to avoid duplicate notifications in the next run - qm.updateScheduledNotificationRuleLastExecutionTimeToNowUtc(rule); + atLeastOneSuccessfulPublish |= true; } else { LOGGER.error("The defined notification publisher is not assignable from " + Publisher.class.getCanonicalName() + " (%s)".formatted(ruleCtx)); } } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException e) { - LOGGER.error( - "An error occurred while instantiating a notification publisher (%s)".formatted(ruleCtx), - e); + LOGGER.error("An error occurred while instantiating a notification publisher (%s)".formatted(ruleCtx), e); + errorsDuringExecution |= true; } catch (PublisherException publisherException) { - LOGGER.error("An error occurred during the publication of the notification (%s)".formatted(ruleCtx), - publisherException); + LOGGER.error("An error occurred during the publication of the notification (%s)".formatted(ruleCtx), publisherException); + errorsDuringExecution |= true; } } + if (!errorsDuringExecution || atLeastOneSuccessfulPublish) { + /* + * Update last execution time after successful operation (even without + * publishing) to avoid duplicate notifications in the next run and signalize + * user indirectly, that operation has ended without failure + */ + qm.updateScheduledNotificationRuleLastExecutionTimeToNowUtc(rule); + } } } From 83f765c04d4cf479b86e90809fdb576d75597a5d Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 29 May 2024 14:31:07 +0200 Subject: [PATCH 294/429] added informational logging Signed-off-by: Max Schiller --- .../ScheduledNotificationTaskInitializer.java | 2 ++ .../tasks/SendScheduledNotificationTask.java | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskInitializer.java b/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskInitializer.java index 99f18dbb2b..ada3ccb2f7 100644 --- a/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskInitializer.java +++ b/src/main/java/org/dependencytrack/notification/ScheduledNotificationTaskInitializer.java @@ -34,6 +34,7 @@ public class ScheduledNotificationTaskInitializer implements ServletContextListe @Override public void contextInitialized(ServletContextEvent sce) { + LOGGER.info("Initializing scheduled notification task service"); try (var qm = new QueryManager()) { var paginatedScheduledRules = qm.getScheduledNotificationRules(); var scheduledRulesList = paginatedScheduledRules.getList(ScheduledNotificationRule.class); @@ -53,6 +54,7 @@ public void contextInitialized(ServletContextEvent sce) { @Override public void contextDestroyed(ServletContextEvent sce) { + LOGGER.info("Shutting down scheduled notification task service"); ScheduledNotificationTaskManager.cancelAllActiveRuleTasks(); } } diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index e3bd21396a..361b3c934e 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -67,6 +67,8 @@ public void run() { final List projectIds = rule.getProjects().stream().map(proj -> proj.getId()).toList(); Boolean errorsDuringExecution = false; Boolean atLeastOneSuccessfulPublish = false; + + LOGGER.info("Processing notification publishing for scheduled notification rule " + rule.getUuid()); for (NotificationGroup group : rule.getNotifyOn()) { final Notification notificationProxy = new Notification() @@ -101,7 +103,7 @@ public void run() { .subject(policySubject); break; default: - LOGGER.error(group.name() + " is not a supported notification group for scheduled publishing"); + LOGGER.warn(group.name() + " is not a supported notification group for scheduled publishing"); errorsDuringExecution |= true; continue; } @@ -138,6 +140,7 @@ public void run() { atLeastOneSuccessfulPublish |= true; } else { LOGGER.error("The defined notification publisher is not assignable from " + Publisher.class.getCanonicalName() + " (%s)".formatted(ruleCtx)); + errorsDuringExecution |= true; } } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException e) { @@ -155,6 +158,10 @@ public void run() { * user indirectly, that operation has ended without failure */ qm.updateScheduledNotificationRuleLastExecutionTimeToNowUtc(rule); + LOGGER.info("Successfuly processed notification publishing for scheduled notification rule " + scheduledNotificationRuleUuid); + } + else { + LOGGER.error("Errors occured while processing notification publishing for scheduled notification rule " + scheduledNotificationRuleUuid); } } } From e2b6f7e0381e44501656d3c858146566012fd588 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 29 May 2024 15:41:17 +0200 Subject: [PATCH 295/429] removed author tags Signed-off-by: Max Schiller --- src/main/java/org/dependencytrack/model/PublishTrigger.java | 2 -- .../org/dependencytrack/model/ScheduledNotificationRule.java | 2 -- .../resources/v1/ScheduledNotificationRuleResource.java | 2 -- 3 files changed, 6 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/PublishTrigger.java b/src/main/java/org/dependencytrack/model/PublishTrigger.java index 43c682e4f2..9e402e63ae 100644 --- a/src/main/java/org/dependencytrack/model/PublishTrigger.java +++ b/src/main/java/org/dependencytrack/model/PublishTrigger.java @@ -21,8 +21,6 @@ /** * Provides a list of available triggers for publishers to send notifications. - * - * @author Max Schiller */ public enum PublishTrigger { ALL, diff --git a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java index ceec5483af..b16ca821d8 100644 --- a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java +++ b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java @@ -57,8 +57,6 @@ /** * Defines a Model class for scheduled notification configurations. - * - * @author Max Schiller */ @PersistenceCapable @JsonInclude(JsonInclude.Include.NON_NULL) diff --git a/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java index 60e345ac7b..21a00dd1c2 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java @@ -61,8 +61,6 @@ /** * JAX-RS resources for processing scheduled notification rules. - * - * @author Max Schiller */ @Path("/v1/schedulednotification/rule") @Api(value = "schedulednotification", authorizations = @Authorization(value = "X-Api-Key")) From 88b94d2f01ecff6a5d8840281187bad44a6a3677 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 29 May 2024 15:41:40 +0200 Subject: [PATCH 296/429] removed unnecessary code in publisher task Signed-off-by: Max Schiller --- .../tasks/SendScheduledNotificationTask.java | 34 ++----------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index 361b3c934e..455de666a0 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -23,10 +23,7 @@ import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.util.List; -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; - import javax.json.Json; import javax.json.JsonObject; import javax.json.JsonReader; @@ -42,7 +39,6 @@ import org.dependencytrack.notification.publisher.PublishContext; import org.dependencytrack.notification.publisher.Publisher; import org.dependencytrack.notification.publisher.SendMailPublisher; -import org.dependencytrack.notification.vo.NewVulnerabilityIdentified; import org.dependencytrack.notification.vo.ScheduledNewVulnerabilitiesIdentified; import org.dependencytrack.notification.vo.ScheduledPolicyViolationsIdentified; import org.dependencytrack.persistence.QueryManager; @@ -133,9 +129,9 @@ public void run() { .addAll(Json.createObjectBuilder(config)) .build(); if (publisherClass != SendMailPublisher.class || rule.getTeams().isEmpty() || rule.getTeams() == null) { - publisher.inform(ruleCtx, restrictNotificationToRuleProjects(notificationProxy, rule), notificationPublisherConfig); + publisher.inform(ruleCtx, notificationProxy, notificationPublisherConfig); } else { - ((SendMailPublisher) publisher).inform(ruleCtx, restrictNotificationToRuleProjects(notificationProxy, rule), notificationPublisherConfig, rule.getTeams()); + ((SendMailPublisher) publisher).inform(ruleCtx, notificationProxy, notificationPublisherConfig, rule.getTeams()); } atLeastOneSuccessfulPublish |= true; } else { @@ -166,32 +162,6 @@ public void run() { } } - private Notification restrictNotificationToRuleProjects(final Notification initialNotification, final Rule rule) { - Notification restrictedNotification = initialNotification; - if (canRestrictNotificationToRuleProjects(initialNotification, rule)) { - Set ruleProjectsUuids = rule.getProjects().stream().map(Project::getUuid).map(UUID::toString).collect(Collectors.toSet()); - restrictedNotification = new Notification(); - restrictedNotification.setGroup(initialNotification.getGroup()); - restrictedNotification.setLevel(initialNotification.getLevel()); - restrictedNotification.scope(initialNotification.getScope()); - restrictedNotification.setContent(initialNotification.getContent()); - restrictedNotification.setTitle(initialNotification.getTitle()); - restrictedNotification.setTimestamp(initialNotification.getTimestamp()); - if (initialNotification.getSubject() instanceof final NewVulnerabilityIdentified subject) { - Set restrictedProjects = subject.getAffectedProjects().stream().filter(project -> ruleProjectsUuids.contains(project.getUuid().toString())).collect(Collectors.toSet()); - NewVulnerabilityIdentified restrictedSubject = new NewVulnerabilityIdentified(subject.getVulnerability(), subject.getComponent(), restrictedProjects, null); - restrictedNotification.setSubject(restrictedSubject); - } - } - return restrictedNotification; - } - - private boolean canRestrictNotificationToRuleProjects(final Notification initialNotification, final Rule rule) { - return initialNotification.getSubject() instanceof NewVulnerabilityIdentified - && rule.getProjects() != null - && !rule.getProjects().isEmpty(); - } - private String generateNotificationTitle(final Rule rule, final NotificationGroup group) { return "Scheduled Notification: " + group.name(); } From 9c9757876cc05e2c73a4f3fdb643d5e1781637c3 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 29 May 2024 17:06:35 +0200 Subject: [PATCH 297/429] moved notification title and content generation to NotificationUtil class Signed-off-by: Max Schiller --- .../tasks/SendScheduledNotificationTask.java | 57 +++++-------------- .../util/NotificationUtil.java | 50 ++++++++++++++++ 2 files changed, 63 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index 455de666a0..7d449122eb 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -20,8 +20,6 @@ import java.io.StringReader; import java.lang.reflect.InvocationTargetException; -import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; import java.util.List; import java.util.UUID; import javax.json.Json; @@ -30,11 +28,7 @@ import org.dependencytrack.exception.PublisherException; import org.dependencytrack.model.NotificationPublisher; -import org.dependencytrack.model.PolicyViolation; -import org.dependencytrack.model.Project; -import org.dependencytrack.model.Rule; import org.dependencytrack.model.ScheduledNotificationRule; -import org.dependencytrack.model.Vulnerability; import org.dependencytrack.notification.NotificationGroup; import org.dependencytrack.notification.publisher.PublishContext; import org.dependencytrack.notification.publisher.Publisher; @@ -42,6 +36,7 @@ import org.dependencytrack.notification.vo.ScheduledNewVulnerabilitiesIdentified; import org.dependencytrack.notification.vo.ScheduledPolicyViolationsIdentified; import org.dependencytrack.persistence.QueryManager; +import org.dependencytrack.util.NotificationUtil; import alpine.common.logging.Logger; import alpine.notification.Notification; @@ -70,7 +65,7 @@ public void run() { final Notification notificationProxy = new Notification() .scope(rule.getScope()) .group(group) - .title(generateNotificationTitle(rule, group)) + .title(NotificationUtil.generateNotificationTitle(group, rule.getProjects())) .level(rule.getNotificationLevel()); switch (group) { @@ -80,10 +75,11 @@ public void run() { continue; ScheduledNewVulnerabilitiesIdentified vulnSubject = new ScheduledNewVulnerabilitiesIdentified(newProjectVulnerabilities); notificationProxy - .content(generateVulnerabilityNotificationContent(rule, - vulnSubject.getNewVulnerabilitiesTotal(), - newProjectVulnerabilities.keySet().stream().toList(), - rule.getLastExecutionTime())) + .content(NotificationUtil.generateVulnerabilityScheduledNotificationContent( + rule, + vulnSubject.getNewVulnerabilitiesTotal(), + newProjectVulnerabilities.keySet().stream().toList(), + rule.getLastExecutionTime())) .subject(vulnSubject); break; case POLICY_VIOLATION: @@ -92,11 +88,12 @@ public void run() { continue; ScheduledPolicyViolationsIdentified policySubject = new ScheduledPolicyViolationsIdentified(newProjectPolicyViolations); notificationProxy - .content(generatePolicyNotificationContent(rule, - policySubject.getNewPolicyViolationsTotal(), - newProjectPolicyViolations.keySet().stream().toList(), - rule.getLastExecutionTime())) - .subject(policySubject); + .content(NotificationUtil.generatePolicyScheduledNotificationContent( + rule, + policySubject.getNewPolicyViolationsTotal(), + newProjectPolicyViolations.keySet().stream().toList(), + rule.getLastExecutionTime())) + .subject(policySubject); break; default: LOGGER.warn(group.name() + " is not a supported notification group for scheduled publishing"); @@ -161,32 +158,4 @@ public void run() { } } } - - private String generateNotificationTitle(final Rule rule, final NotificationGroup group) { - return "Scheduled Notification: " + group.name(); - } - - private String generateVulnerabilityNotificationContent(final Rule rule, final List vulnerabilities, final List projects, final ZonedDateTime lastExecutionTime) { - final String content; - - if (vulnerabilities.isEmpty()) { - content = "No new vulnerabilities found."; - } else { - content = "In total, " + vulnerabilities.size() + " new vulnerabilities in " + projects.size() + " projects were found since " + lastExecutionTime.toLocalDateTime().truncatedTo(ChronoUnit.SECONDS) + "."; - } - - return content; - } - - private String generatePolicyNotificationContent(final Rule rule, final List policyViolations, final List projects, final ZonedDateTime lastExecutionTime) { - final String content; - - if (policyViolations.isEmpty()) { - content = "No new policy violations found."; - } else { - content = "In total, " + policyViolations.size() + " new policy violations in " + projects.size() + " projects were found since " + lastExecutionTime.toLocalDateTime().truncatedTo(ChronoUnit.SECONDS) + "."; - } - - return content; - } } diff --git a/src/main/java/org/dependencytrack/util/NotificationUtil.java b/src/main/java/org/dependencytrack/util/NotificationUtil.java index ba1b9d9ef3..6eaaa873b8 100644 --- a/src/main/java/org/dependencytrack/util/NotificationUtil.java +++ b/src/main/java/org/dependencytrack/util/NotificationUtil.java @@ -36,6 +36,7 @@ import org.dependencytrack.model.PolicyCondition.Operator; import org.dependencytrack.model.PolicyViolation; import org.dependencytrack.model.Project; +import org.dependencytrack.model.Rule; import org.dependencytrack.model.Severity; import org.dependencytrack.model.Tag; import org.dependencytrack.model.Vex; @@ -72,6 +73,8 @@ import java.net.URLDecoder; import java.nio.file.Path; import java.util.Date; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -727,6 +730,30 @@ private static String generateNotificationContent(final ViolationAnalysis violat return "An violation analysis decision was made to a policy violation affecting a project"; } + public static String generateVulnerabilityScheduledNotificationContent(final Rule rule, final List vulnerabilities, final List projects, final ZonedDateTime lastExecutionTime) { + final String content; + + if (vulnerabilities.isEmpty()) { + content = "No new vulnerabilities found."; + } else { + content = "In total, " + vulnerabilities.size() + " new vulnerabilities in " + projects.size() + " projects were found since " + lastExecutionTime.toLocalDateTime().truncatedTo(ChronoUnit.SECONDS) + "."; + } + + return content; + } + + public static String generatePolicyScheduledNotificationContent(final Rule rule, final List policyViolations, final List projects, final ZonedDateTime lastExecutionTime) { + final String content; + + if (policyViolations.isEmpty()) { + content = "No new policy violations found."; + } else { + content = "In total, " + policyViolations.size() + " new policy violations in " + projects.size() + " projects were found since " + lastExecutionTime.toLocalDateTime().truncatedTo(ChronoUnit.SECONDS) + "."; + } + + return content; + } + public static String generateNotificationTitle(String messageType, Project project) { if (project != null) { return messageType + " on Project: [" + project.toString() + "]"; @@ -734,6 +761,29 @@ public static String generateNotificationTitle(String messageType, Project proje return messageType; } + public static String generateNotificationTitle(NotificationGroup notificationGroup, List projects) { + String messageType; + + switch (notificationGroup) { + case NEW_VULNERABILITY: + messageType = NotificationConstants.Title.NEW_VULNERABILITY; + break; + case POLICY_VIOLATION: + messageType = NotificationConstants.Title.POLICY_VIOLATION; + break; + default: + return notificationGroup.name(); + } + + if (projects != null) { + if (projects.size() == 1) { + return generateNotificationTitle(messageType, projects.get(0)); + } + } + + return messageType + " on " + projects.size() + " projects"; + } + public static Object generateSubject(String group) { final Project project = createProject(); final Vulnerability vuln = createVulnerability(); From cab20d4758c7f9c6506ebe3c2e126df04119991c Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Thu, 30 May 2024 17:21:27 +0200 Subject: [PATCH 298/429] removed check for instant execution api payload to match UI changes (uuid only) Signed-off-by: Max Schiller --- .../v1/ScheduledNotificationRuleResource.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java index 21a00dd1c2..54ab9e6d3a 100644 --- a/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResource.java @@ -356,28 +356,20 @@ public Response addTeamToRule( }) @PermissionRequired(Permissions.Constants.SYSTEM_CONFIGURATION) public Response executeScheduledNotificationRuleNow(ScheduledNotificationRule jsonRule) { - final Validator validator = super.getValidator(); - failOnValidationError( - validator.validateProperty(jsonRule, "name"), - validator.validateProperty(jsonRule, "publisherConfig"), - validator.validateProperty(jsonRule, "cronConfig"), - validator.validateProperty(jsonRule, "lastExecutionTime") - ); - try (QueryManager qm = new QueryManager()) { ScheduledNotificationRule rule = qm.getObjectByUuid(ScheduledNotificationRule.class, jsonRule.getUuid()); if (rule != null) { try { - ScheduledNotificationTaskManager.cancelActiveRuleTask(jsonRule.getUuid()); + ScheduledNotificationTaskManager.cancelActiveRuleTask(rule.getUuid()); if (rule.isEnabled()) { // schedule must be passed too, to schedule the next execution according to cron expression again - var schedule = Schedule.create(jsonRule.getCronConfig()); - ScheduledNotificationTaskManager.scheduleNextRuleTask(jsonRule.getUuid(), schedule, 0, TimeUnit.MILLISECONDS); + var schedule = Schedule.create(rule.getCronConfig()); + ScheduledNotificationTaskManager.scheduleNextRuleTask(rule.getUuid(), schedule, 0, TimeUnit.MILLISECONDS); } else { - ScheduledNotificationTaskManager.scheduleNextRuleTaskOnce(jsonRule.getUuid(), 0, TimeUnit.MILLISECONDS); + ScheduledNotificationTaskManager.scheduleNextRuleTaskOnce(rule.getUuid(), 0, TimeUnit.MILLISECONDS); } } catch (InvalidExpressionException e) { - LOGGER.error("Cron expression is invalid: " + jsonRule.getCronConfig()); + LOGGER.error("Cron expression is invalid: " + rule.getCronConfig()); return Response.status(Response.Status.BAD_REQUEST).entity("Invalid cron expression").build(); } From 27c8ce7ecfe8254cb4918db4e843e3f13065bf38 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Fri, 31 May 2024 15:37:49 +0200 Subject: [PATCH 299/429] added json serializer for ZonedDateTime for better readability in api json results Signed-off-by: Max Schiller --- .../model/ScheduledNotificationRule.java | 7 ++- .../Iso8601ZonedDateTimeSerializer.java | 46 +++++++++++++++++++ .../util/ZonedDateTimeUtil.java | 36 +++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/dependencytrack/resources/v1/serializers/Iso8601ZonedDateTimeSerializer.java create mode 100644 src/main/java/org/dependencytrack/util/ZonedDateTimeUtil.java diff --git a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java index b16ca821d8..5a1fce9601 100644 --- a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java +++ b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java @@ -27,6 +27,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + import javax.jdo.annotations.Column; import javax.jdo.annotations.Element; import javax.jdo.annotations.Extension; @@ -45,8 +47,10 @@ import org.apache.commons.collections4.CollectionUtils; import org.dependencytrack.notification.NotificationGroup; import org.dependencytrack.notification.NotificationScope; +import org.dependencytrack.resources.v1.serializers.Iso8601ZonedDateTimeSerializer; import java.io.Serializable; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collections; @@ -154,6 +158,7 @@ public class ScheduledNotificationRule implements Rule, Serializable { @Persistent(defaultFetchGroup = "true") @Column(name = "LAST_EXECUTION_TIME", allowsNull = "true") // new column, must allow nulls on existing databases + @JsonSerialize(using = Iso8601ZonedDateTimeSerializer.class) private ZonedDateTime lastExecutionTime; @Persistent @@ -314,7 +319,7 @@ public void setCronConfig(String cronConfig) { public ZonedDateTime getLastExecutionTime() { if (lastExecutionTime == null) { - return ZonedDateTime.now(); + return ZonedDateTime.now(ZoneOffset.UTC); } return lastExecutionTime; } diff --git a/src/main/java/org/dependencytrack/resources/v1/serializers/Iso8601ZonedDateTimeSerializer.java b/src/main/java/org/dependencytrack/resources/v1/serializers/Iso8601ZonedDateTimeSerializer.java new file mode 100644 index 0000000000..0716a7d1c9 --- /dev/null +++ b/src/main/java/org/dependencytrack/resources/v1/serializers/Iso8601ZonedDateTimeSerializer.java @@ -0,0 +1,46 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.resources.v1.serializers; + +import java.io.IOException; +import java.time.ZonedDateTime; +import org.dependencytrack.util.ZonedDateTimeUtil; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +public class Iso8601ZonedDateTimeSerializer extends StdSerializer { + + public Iso8601ZonedDateTimeSerializer() { + this(null); + } + + public Iso8601ZonedDateTimeSerializer(Class t) { + super(t); + } + + @Override + public void serialize(ZonedDateTime value, JsonGenerator gen, SerializerProvider arg2) + throws IOException, JsonProcessingException { + gen.writeString(ZonedDateTimeUtil.toISO8601(value)); + } + +} diff --git a/src/main/java/org/dependencytrack/util/ZonedDateTimeUtil.java b/src/main/java/org/dependencytrack/util/ZonedDateTimeUtil.java new file mode 100644 index 0000000000..2998de5bd1 --- /dev/null +++ b/src/main/java/org/dependencytrack/util/ZonedDateTimeUtil.java @@ -0,0 +1,36 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.util; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +public class ZonedDateTimeUtil { + public static String toISO8601(final ZonedDateTime date) { + return date.withZoneSameInstant(ZoneId.of("UTC")).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } + + public static ZonedDateTime fromISO8601(final String dateString) { + if (dateString == null) { + return null; + } + return ZonedDateTime.parse(dateString, DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } +} From 1ca9cbbd249001bebc33369db589520dc5ff7dc1 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Mon, 3 Jun 2024 16:05:42 +0200 Subject: [PATCH 300/429] Merge branch 'msr-scheduled-tests' into msr-issue-322 Signed-off-by: Max Schiller --- .../org/dependencytrack/ResourceTest.java | 15 + .../model/NotificationPublisherTest.java | 7 + .../model/ScheduledNotificationRuleTest.java | 207 +++++++ .../DefaultNotificationPublishersTest.java | 19 + ...duledNewVulnerabilitiesIdentifiedTest.java | 80 +++ ...heduledPolicyViolationsIdentifiedTest.java | 51 ++ .../v1/NotificationRuleResourceTest.java | 1 + ...ScheduledNotificationRuleResourceTest.java | 570 ++++++++++++++++++ 8 files changed, 950 insertions(+) create mode 100644 src/test/java/org/dependencytrack/model/ScheduledNotificationRuleTest.java create mode 100644 src/test/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentifiedTest.java create mode 100644 src/test/java/org/dependencytrack/notification/vo/ScheduledPolicyViolationsIdentifiedTest.java create mode 100644 src/test/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResourceTest.java diff --git a/src/test/java/org/dependencytrack/ResourceTest.java b/src/test/java/org/dependencytrack/ResourceTest.java index 4177c1e0ba..9b9f98191d 100644 --- a/src/test/java/org/dependencytrack/ResourceTest.java +++ b/src/test/java/org/dependencytrack/ResourceTest.java @@ -30,6 +30,12 @@ import org.junit.Before; import org.junit.BeforeClass; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + import jakarta.json.Json; import jakarta.json.JsonArray; import jakarta.json.JsonObject; @@ -56,6 +62,7 @@ public abstract class ResourceTest { protected final String V1_METRICS = "/v1/metrics"; protected final String V1_NOTIFICATION_PUBLISHER = "/v1/notification/publisher"; protected final String V1_NOTIFICATION_RULE = "/v1/notification/rule"; + protected final String V1_SCHEDULED_NOTIFICATION_RULE = "/v1/schedulednotification/rule"; protected final String V1_OIDC = "/v1/oidc"; protected final String V1_PERMISSION = "/v1/permission"; protected final String V1_OSV_ECOSYSTEM = "/v1/integration/osv/ecosystem"; @@ -89,6 +96,14 @@ public abstract class ResourceTest { protected QueryManager qm; protected Team team; protected String apiKey; + protected JsonMapper jsonMapper; + + public ResourceTest() { + // needed to deserialize Java time objects in tests + jsonMapper = JsonMapper.builder() + .addModule(new JavaTimeModule()) + .build(); + } @BeforeClass public static void init() { diff --git a/src/test/java/org/dependencytrack/model/NotificationPublisherTest.java b/src/test/java/org/dependencytrack/model/NotificationPublisherTest.java index 72bcd1b68f..e0a239fbd1 100644 --- a/src/test/java/org/dependencytrack/model/NotificationPublisherTest.java +++ b/src/test/java/org/dependencytrack/model/NotificationPublisherTest.java @@ -74,6 +74,13 @@ public void testDefaultPublisher() { Assert.assertTrue(publisher.isDefaultPublisher()); } + @Test + public void testPublishScheduled() { + NotificationPublisher publisher = new NotificationPublisher(); + publisher.setPublishScheduled(true); + Assert.assertTrue(publisher.isPublishScheduled()); + } + @Test public void testUuid() { UUID uuid = UUID.randomUUID(); diff --git a/src/test/java/org/dependencytrack/model/ScheduledNotificationRuleTest.java b/src/test/java/org/dependencytrack/model/ScheduledNotificationRuleTest.java new file mode 100644 index 0000000000..b4afa9974e --- /dev/null +++ b/src/test/java/org/dependencytrack/model/ScheduledNotificationRuleTest.java @@ -0,0 +1,207 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.model; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import org.dependencytrack.notification.NotificationGroup; +import org.dependencytrack.notification.NotificationScope; +import org.junit.Assert; +import org.junit.Test; + +import alpine.model.LdapUser; +import alpine.model.ManagedUser; +import alpine.model.OidcUser; +import alpine.model.Team; +import alpine.notification.NotificationLevel; + +public class ScheduledNotificationRuleTest { + @Test + public void testId() { + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setId(111L); + Assert.assertEquals(111L, rule.getId()); + } + + @Test + public void testName() { + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setName("Test Name"); + Assert.assertEquals("Test Name", rule.getName()); + } + + @Test + public void testEnabled() { + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setEnabled(true); + Assert.assertTrue(rule.isEnabled()); + } + + @Test + public void testScope() { + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setScope(NotificationScope.PORTFOLIO); + Assert.assertEquals(NotificationScope.PORTFOLIO, rule.getScope()); + } + + @Test + public void testNotificationLevel() { + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setNotificationLevel(NotificationLevel.INFORMATIONAL); + Assert.assertEquals(NotificationLevel.INFORMATIONAL, rule.getNotificationLevel()); + } + + @Test + public void testProjects() { + List projects = new ArrayList<>(); + Project project = new Project(); + projects.add(project); + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setProjects(projects); + Assert.assertEquals(1, rule.getProjects().size()); + Assert.assertEquals(project, rule.getProjects().get(0)); + } + + @Test + public void testMessage() { + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setMessage("Test Message"); + Assert.assertEquals("Test Message", rule.getMessage()); + } + + @Test + public void testNotifyOn() { + Set groups = new HashSet<>(); + groups.add(NotificationGroup.POLICY_VIOLATION); + groups.add(NotificationGroup.NEW_VULNERABILITY); + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setNotifyOn(groups); + Assert.assertEquals(2, rule.getNotifyOn().size()); + } + + @Test + public void testPublisher() { + NotificationPublisher publisher = new NotificationPublisher(); + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setPublisher(publisher); + Assert.assertEquals(publisher, rule.getPublisher()); + } + + @Test + public void testPublisherConfig() { + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setPublisherConfig("{ \"config\": \"configured\" }"); + Assert.assertEquals("{ \"config\": \"configured\" }", rule.getPublisherConfig()); + } + + @Test + public void testUuid() { + UUID uuid = UUID.randomUUID(); + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setUuid(uuid); + Assert.assertEquals(uuid.toString(), rule.getUuid().toString()); + } + + @Test + public void testTeams(){ + List teams = new ArrayList<>(); + Team team = new Team(); + teams.add(team); + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setTeams(teams); + Assert.assertEquals(1, rule.getTeams().size()); + Assert.assertEquals(team, rule.getTeams().get(0)); + } + + @Test + public void testManagedUsers(){ + List teams = new ArrayList<>(); + Team team = new Team(); + List managedUsers = new ArrayList<>(); + ManagedUser managedUser = new ManagedUser(); + managedUsers.add(managedUser); + team.setManagedUsers(managedUsers); + teams.add(team); + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setTeams(teams); + Assert.assertEquals(1, rule.getTeams().size()); + Assert.assertEquals(team, rule.getTeams().get(0)); + Assert.assertEquals(managedUser, rule.getTeams().get(0).getManagedUsers().get(0)); + } + + @Test + public void testLdapUsers(){ + List teams = new ArrayList<>(); + Team team = new Team(); + List ldapUsers = new ArrayList<>(); + LdapUser ldapUser = new LdapUser(); + ldapUsers.add(ldapUser); + team.setLdapUsers(ldapUsers); + teams.add(team); + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setTeams(teams); + Assert.assertEquals(1, rule.getTeams().size()); + Assert.assertEquals(team, rule.getTeams().get(0)); + Assert.assertEquals(ldapUser, rule.getTeams().get(0).getLdapUsers().get(0)); + } + + @Test + public void testOidcUsers(){ + List teams = new ArrayList<>(); + Team team = new Team(); + List oidcUsers = new ArrayList<>(); + OidcUser oidcUser = new OidcUser(); + oidcUsers.add(oidcUser); + team.setOidcUsers(oidcUsers); + teams.add(team); + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setTeams(teams); + Assert.assertEquals(1, rule.getTeams().size()); + Assert.assertEquals(team, rule.getTeams().get(0)); + Assert.assertEquals(oidcUser, rule.getTeams().get(0).getOidcUsers().get(0)); + } + + @Test + public void testCronExpression() { + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setCronConfig("0 0 12 * *"); + Assert.assertEquals("0 0 12 * *", rule.getCronConfig()); + } + + @Test + public void testLastExecutionTime() { + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + ZonedDateTime zdt = ZonedDateTime.of(2024, 5, 20, 12, 10, 13, 0, ZoneOffset.UTC); + rule.setLastExecutionTime(zdt); + Assert.assertEquals(zdt, rule.getLastExecutionTime()); + } + + @Test + public void testPublishOnlyWithUpdates() { + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setPublishOnlyWithUpdates(true); + Assert.assertTrue(rule.getPublishOnlyWithUpdates()); + } +} diff --git a/src/test/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishersTest.java b/src/test/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishersTest.java index b125d50766..153be61c1f 100644 --- a/src/test/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishersTest.java +++ b/src/test/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishersTest.java @@ -31,6 +31,7 @@ public void testEnums() { Assert.assertEquals("MS_TEAMS", DefaultNotificationPublishers.MS_TEAMS.name()); Assert.assertEquals("MATTERMOST", DefaultNotificationPublishers.MATTERMOST.name()); Assert.assertEquals("EMAIL", DefaultNotificationPublishers.EMAIL.name()); + Assert.assertEquals("SCHEDULED_EMAIL", DefaultNotificationPublishers.SCHEDULED_EMAIL.name()); Assert.assertEquals("CONSOLE", DefaultNotificationPublishers.CONSOLE.name()); Assert.assertEquals("WEBHOOK", DefaultNotificationPublishers.WEBHOOK.name()); Assert.assertEquals("JIRA", DefaultNotificationPublishers.JIRA.name()); @@ -44,6 +45,7 @@ public void testSlack() { Assert.assertEquals("/templates/notification/publisher/slack.peb", DefaultNotificationPublishers.SLACK.getPublisherTemplateFile()); Assert.assertEquals(MediaType.APPLICATION_JSON, DefaultNotificationPublishers.SLACK.getTemplateMimeType()); Assert.assertTrue(DefaultNotificationPublishers.SLACK.isDefaultPublisher()); + Assert.assertFalse(DefaultNotificationPublishers.SLACK.isPublishScheduled()); } @Test @@ -54,6 +56,7 @@ public void testMsTeams() { Assert.assertEquals("/templates/notification/publisher/msteams.peb", DefaultNotificationPublishers.MS_TEAMS.getPublisherTemplateFile()); Assert.assertEquals(MediaType.APPLICATION_JSON, DefaultNotificationPublishers.MS_TEAMS.getTemplateMimeType()); Assert.assertTrue(DefaultNotificationPublishers.MS_TEAMS.isDefaultPublisher()); + Assert.assertFalse(DefaultNotificationPublishers.MS_TEAMS.isPublishScheduled()); } @Test @@ -64,6 +67,7 @@ public void testMattermost() { Assert.assertEquals("/templates/notification/publisher/mattermost.peb", DefaultNotificationPublishers.MATTERMOST.getPublisherTemplateFile()); Assert.assertEquals(MediaType.APPLICATION_JSON, DefaultNotificationPublishers.MATTERMOST.getTemplateMimeType()); Assert.assertTrue(DefaultNotificationPublishers.MATTERMOST.isDefaultPublisher()); + Assert.assertFalse(DefaultNotificationPublishers.MATTERMOST.isPublishScheduled()); } @Test @@ -74,6 +78,18 @@ public void testEmail() { Assert.assertEquals("/templates/notification/publisher/email.peb", DefaultNotificationPublishers.EMAIL.getPublisherTemplateFile()); Assert.assertEquals("text/plain; charset=utf-8", DefaultNotificationPublishers.EMAIL.getTemplateMimeType()); Assert.assertTrue(DefaultNotificationPublishers.EMAIL.isDefaultPublisher()); + Assert.assertFalse(DefaultNotificationPublishers.EMAIL.isPublishScheduled()); + } + + @Test + public void testScheduledEmail() { + Assert.assertEquals("Scheduled Email", DefaultNotificationPublishers.SCHEDULED_EMAIL.getPublisherName()); + Assert.assertEquals("Sends summarized notifications to an email address in a defined schedule", DefaultNotificationPublishers.SCHEDULED_EMAIL.getPublisherDescription()); + Assert.assertEquals(SendMailPublisher.class, DefaultNotificationPublishers.SCHEDULED_EMAIL.getPublisherClass()); + Assert.assertEquals("/templates/notification/publisher/scheduled_email.peb", DefaultNotificationPublishers.SCHEDULED_EMAIL.getPublisherTemplateFile()); + Assert.assertEquals(MediaType.TEXT_PLAIN, DefaultNotificationPublishers.SCHEDULED_EMAIL.getTemplateMimeType()); + Assert.assertTrue(DefaultNotificationPublishers.SCHEDULED_EMAIL.isDefaultPublisher()); + Assert.assertTrue(DefaultNotificationPublishers.SCHEDULED_EMAIL.isPublishScheduled()); } @Test @@ -84,6 +100,7 @@ public void testConsole() { Assert.assertEquals("/templates/notification/publisher/console.peb", DefaultNotificationPublishers.CONSOLE.getPublisherTemplateFile()); Assert.assertEquals(MediaType.TEXT_PLAIN, DefaultNotificationPublishers.CONSOLE.getTemplateMimeType()); Assert.assertTrue(DefaultNotificationPublishers.CONSOLE.isDefaultPublisher()); + Assert.assertFalse(DefaultNotificationPublishers.CONSOLE.isPublishScheduled()); } @Test @@ -94,6 +111,7 @@ public void testWebhook() { Assert.assertEquals("/templates/notification/publisher/webhook.peb", DefaultNotificationPublishers.WEBHOOK.getPublisherTemplateFile()); Assert.assertEquals(MediaType.APPLICATION_JSON, DefaultNotificationPublishers.WEBHOOK.getTemplateMimeType()); Assert.assertTrue(DefaultNotificationPublishers.WEBHOOK.isDefaultPublisher()); + Assert.assertFalse(DefaultNotificationPublishers.WEBHOOK.isPublishScheduled()); } @Test @@ -104,5 +122,6 @@ public void testJira() { Assert.assertEquals("/templates/notification/publisher/jira.peb", DefaultNotificationPublishers.JIRA.getPublisherTemplateFile()); Assert.assertEquals(MediaType.APPLICATION_JSON, DefaultNotificationPublishers.JIRA.getTemplateMimeType()); Assert.assertTrue(DefaultNotificationPublishers.JIRA.isDefaultPublisher()); + Assert.assertFalse(DefaultNotificationPublishers.JIRA.isPublishScheduled()); } } diff --git a/src/test/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentifiedTest.java b/src/test/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentifiedTest.java new file mode 100644 index 0000000000..438561be08 --- /dev/null +++ b/src/test/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentifiedTest.java @@ -0,0 +1,80 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.notification.vo; + +import java.util.LinkedHashMap; +import java.util.List; +import org.dependencytrack.model.Project; +import org.dependencytrack.model.Severity; +import org.dependencytrack.model.Vulnerability; +import org.junit.Assert; +import org.junit.Test; + +public class ScheduledNewVulnerabilitiesIdentifiedTest { + @Test + public void testVo() { + Vulnerability critVuln = new Vulnerability(); + critVuln.setTitle("Critical Vulnerability"); + critVuln.setSeverity(Severity.CRITICAL); + Vulnerability highVuln = new Vulnerability(); + highVuln.setTitle("High Vulnerability"); + highVuln.setSeverity(Severity.HIGH); + Vulnerability mediumVuln = new Vulnerability(); + mediumVuln.setTitle("Medium Vulnerability"); + mediumVuln.setSeverity(Severity.MEDIUM); + Vulnerability lowVuln = new Vulnerability(); + lowVuln.setTitle("Low Vulnerability"); + lowVuln.setSeverity(Severity.LOW); + Vulnerability infoVuln = new Vulnerability(); + infoVuln.setTitle("Info Vulnerability"); + infoVuln.setSeverity(Severity.INFO); + + Project project1 = new Project(); + var project1Vulns = List.of(critVuln, highVuln, infoVuln); + var projectVulnerabilitiesMap = new LinkedHashMap>(); + projectVulnerabilitiesMap.put(project1, project1Vulns); + Project project2 = new Project(); + var project2Vulns = List.of(mediumVuln, lowVuln); + projectVulnerabilitiesMap.put(project2, project2Vulns); + + ScheduledNewVulnerabilitiesIdentified vo = new ScheduledNewVulnerabilitiesIdentified(projectVulnerabilitiesMap); + + Assert.assertEquals(2, vo.getNewProjectVulnerabilities().size()); + Assert.assertEquals(project1Vulns, vo.getNewProjectVulnerabilities().get(project1)); + Assert.assertEquals(project2Vulns, vo.getNewProjectVulnerabilities().get(project2)); + Assert.assertEquals(2, vo.getNewProjectVulnerabilitiesBySeverity().size()); + var projectVulnerabilitiesBySeverityMap = vo.getNewProjectVulnerabilitiesBySeverity(); + Assert.assertEquals(3, projectVulnerabilitiesBySeverityMap.get(project1).size()); + Assert.assertEquals(2, projectVulnerabilitiesBySeverityMap.get(project2).size()); + Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project1).get(Severity.CRITICAL).size()); + Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project1).get(Severity.HIGH).size()); + Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project1).get(Severity.INFO).size()); + Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project2).get(Severity.MEDIUM).size()); + Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project2).get(Severity.LOW).size()); + Assert.assertEquals(5, vo.getNewVulnerabilitiesTotal().size()); + Assert.assertEquals(List.of(critVuln, highVuln, infoVuln, mediumVuln, lowVuln), vo.getNewVulnerabilitiesTotal()); + Assert.assertEquals(5, vo.getNewVulnerabilitiesTotalBySeverity().size()); + var vulnerabilitiesBySeverity = vo.getNewVulnerabilitiesTotalBySeverity(); + Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.CRITICAL).size()); + Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.HIGH).size()); + Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.MEDIUM).size()); + Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.LOW).size()); + Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.INFO).size()); + } +} diff --git a/src/test/java/org/dependencytrack/notification/vo/ScheduledPolicyViolationsIdentifiedTest.java b/src/test/java/org/dependencytrack/notification/vo/ScheduledPolicyViolationsIdentifiedTest.java new file mode 100644 index 0000000000..e46a90d64f --- /dev/null +++ b/src/test/java/org/dependencytrack/notification/vo/ScheduledPolicyViolationsIdentifiedTest.java @@ -0,0 +1,51 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.notification.vo; + +import java.util.LinkedHashMap; +import java.util.List; + +import org.dependencytrack.model.PolicyViolation; +import org.dependencytrack.model.Project; +import org.junit.Assert; +import org.junit.Test; + +public class ScheduledPolicyViolationsIdentifiedTest { + @Test + public void testVo() { + Project project1 = new Project(); + PolicyViolation policyViolation1 = new PolicyViolation(); + PolicyViolation policyViolation2 = new PolicyViolation(); + Project project2 = new Project(); + PolicyViolation policyViolation3 = new PolicyViolation(); + PolicyViolation policyViolation4 = new PolicyViolation(); + PolicyViolation policyViolation5 = new PolicyViolation(); + var projectPolicyViolations = new LinkedHashMap>(); + projectPolicyViolations.put(project1, List.of(policyViolation1, policyViolation2)); + projectPolicyViolations.put(project2, List.of(policyViolation3, policyViolation4, policyViolation5)); + + ScheduledPolicyViolationsIdentified vo = new ScheduledPolicyViolationsIdentified(projectPolicyViolations); + + Assert.assertEquals(2, vo.getNewProjectPolicyViolations().size()); + Assert.assertEquals(List.of(policyViolation1, policyViolation2), vo.getNewProjectPolicyViolations().get(project1)); + Assert.assertEquals(List.of(policyViolation3, policyViolation4, policyViolation5), vo.getNewProjectPolicyViolations().get(project2)); + Assert.assertEquals(5, vo.getNewPolicyViolationsTotal().size()); + Assert.assertEquals(List.of(policyViolation1, policyViolation2, policyViolation3, policyViolation4, policyViolation5), vo.getNewPolicyViolationsTotal()); + } +} diff --git a/src/test/java/org/dependencytrack/resources/v1/NotificationRuleResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/NotificationRuleResourceTest.java index 9906483b4a..194f9b949a 100644 --- a/src/test/java/org/dependencytrack/resources/v1/NotificationRuleResourceTest.java +++ b/src/test/java/org/dependencytrack/resources/v1/NotificationRuleResourceTest.java @@ -545,6 +545,7 @@ public void addTeamToRuleWithCustomEmailPublisherTest() { "publisherClass": "org.dependencytrack.notification.publisher.SendMailPublisher", "templateMimeType": "templateMimeType", "defaultPublisher": false, + "publishScheduled": false, "uuid": "${json-unit.matches:publisherUuid}" }, "uuid": "${json-unit.matches:ruleUuid}" diff --git a/src/test/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResourceTest.java b/src/test/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResourceTest.java new file mode 100644 index 0000000000..6c8378d2f7 --- /dev/null +++ b/src/test/java/org/dependencytrack/resources/v1/ScheduledNotificationRuleResourceTest.java @@ -0,0 +1,570 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.resources.v1; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonValue; +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.dependencytrack.ResourceTest; +import org.dependencytrack.model.ConfigPropertyConstants; +import org.dependencytrack.model.NotificationPublisher; +import org.dependencytrack.model.Project; +import org.dependencytrack.model.ScheduledNotificationRule; +import org.dependencytrack.notification.NotificationGroup; +import org.dependencytrack.notification.NotificationScope; +import org.dependencytrack.notification.publisher.DefaultNotificationPublishers; +import org.dependencytrack.notification.publisher.SendMailPublisher; +import org.dependencytrack.persistence.DefaultObjectGenerator; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.servlet.ServletContainer; +import org.glassfish.jersey.test.DeploymentContext; +import org.glassfish.jersey.test.ServletDeploymentContext; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.fasterxml.jackson.core.JsonProcessingException; +import alpine.common.util.UuidUtil; +import alpine.model.Team; +import alpine.notification.NotificationLevel; +import alpine.server.filters.ApiFilter; +import alpine.server.filters.AuthenticationFilter; +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.equalTo; + +public class ScheduledNotificationRuleResourceTest extends ResourceTest { + + @Override + protected DeploymentContext configureDeployment() { + return ServletDeploymentContext.forServlet(new ServletContainer( + new ResourceConfig(ScheduledNotificationRuleResource.class) + .register(ApiFilter.class) + .register(AuthenticationFilter.class))) + .build(); + } + + @Before + public void before() throws Exception { + super.before(); + DefaultObjectGenerator generator = new DefaultObjectGenerator(); + generator.contextInitialized(null); + } + + @Test + public void getAllScheduledNotificationRulesTest(){ + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule r1 = qm.createScheduledNotificationRule("Rule 1", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + qm.createScheduledNotificationRule("Rule 2", NotificationScope.PORTFOLIO, NotificationLevel.WARNING, publisher); + qm.createScheduledNotificationRule("Rule 3", NotificationScope.SYSTEM, NotificationLevel.ERROR, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE).request() + .header(X_API_KEY, apiKey) + .get(Response.class); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertEquals(String.valueOf(3), response.getHeaderString(TOTAL_COUNT_HEADER)); + JsonArray json = parseJsonArray(response); + Assert.assertNotNull(json); + Assert.assertEquals(3, json.size()); + Assert.assertEquals("Rule 1", json.getJsonObject(0).getString("name")); + Assert.assertTrue(json.getJsonObject(0).getBoolean("enabled")); + Assert.assertEquals("PORTFOLIO", json.getJsonObject(0).getString("scope")); + Assert.assertEquals("INFORMATIONAL", json.getJsonObject(0).getString("notificationLevel")); + Assert.assertEquals(0, json.getJsonObject(0).getJsonArray("notifyOn").size()); + Assert.assertTrue(UuidUtil.isValidUUID(json.getJsonObject(0).getString("uuid"))); + Assert.assertEquals("Slack", json.getJsonObject(0).getJsonObject("publisher").getString("name")); + Assert.assertFalse(json.getJsonObject(0).getBoolean("logSuccessfulPublish")); + Assert.assertEquals(ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_EXPRESSION.getDefaultPropertyValue(), json.getJsonObject(0).getString("cronConfig")); + JsonValue jsonValue = json.getJsonObject(0).get("lastExecutionTime"); + try { + Assert.assertEquals(r1.getLastExecutionTime(), jsonMapper.readValue(jsonValue.toString(), ZonedDateTime.class).withZoneSameInstant(r1.getLastExecutionTime().getZone())); + } catch (JsonProcessingException e) { + Assert.fail(); + } + Assert.assertFalse(json.getJsonObject(0).getBoolean("publishOnlyWithUpdates")); + } + + @Test + public void createScheduledNotificationRuleTest() { + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setName("Example Rule"); + rule.setNotificationLevel(NotificationLevel.WARNING); + rule.setScope(NotificationScope.SYSTEM); + rule.setPublisher(publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(rule, MediaType.APPLICATION_JSON)); + Assert.assertEquals(201, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertNotNull(json); + Assert.assertEquals("Example Rule", json.getString("name")); + Assert.assertTrue(json.getBoolean("enabled")); + Assert.assertTrue(json.getBoolean("notifyChildren")); + Assert.assertFalse(json.getBoolean("logSuccessfulPublish")); + Assert.assertEquals("SYSTEM", json.getString("scope")); + Assert.assertEquals("WARNING", json.getString("notificationLevel")); + Assert.assertEquals(0, json.getJsonArray("notifyOn").size()); + Assert.assertTrue(UuidUtil.isValidUUID(json.getString("uuid"))); + Assert.assertEquals("Slack", json.getJsonObject("publisher").getString("name")); + Assert.assertEquals(ConfigPropertyConstants.NOTIFICATION_CRON_DEFAULT_EXPRESSION.getDefaultPropertyValue(), json.getString("cronConfig")); + Assert.assertFalse(json.getBoolean("publishOnlyWithUpdates")); + } + + @Test + public void createScheduledNotificationRuleInvalidPublisherTest() { + NotificationPublisher publisher = new NotificationPublisher(); + publisher.setUuid(UUID.randomUUID()); + ScheduledNotificationRule rule = new ScheduledNotificationRule(); + rule.setName("Example Rule"); + rule.setEnabled(true); + rule.setPublisherConfig("{ \"foo\": \"bar\" }"); + rule.setMessage("A message"); + rule.setNotificationLevel(NotificationLevel.WARNING); + rule.setScope(NotificationScope.SYSTEM); + rule.setPublisher(publisher); + rule.setCronConfig("0 * * * *"); + rule.setLastExecutionTime(ZonedDateTime.now()); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE).request() + .header(X_API_KEY, apiKey) + .put(Entity.entity(rule, MediaType.APPLICATION_JSON)); + Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The UUID of the notification publisher could not be found.", body); + } + + @Test + public void updateScheduledNotificationRuleTest() { + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Rule 1", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + rule.setName("Example Rule"); + rule.setNotifyOn(Collections.singleton(NotificationGroup.NEW_VULNERABILITY)); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(rule, MediaType.APPLICATION_JSON)); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertNotNull(json); + Assert.assertEquals("Example Rule", json.getString("name")); + Assert.assertTrue(json.getBoolean("enabled")); + Assert.assertEquals("PORTFOLIO", json.getString("scope")); + Assert.assertEquals("INFORMATIONAL", json.getString("notificationLevel")); + Assert.assertEquals("NEW_VULNERABILITY", json.getJsonArray("notifyOn").getString(0)); + Assert.assertTrue(UuidUtil.isValidUUID(json.getString("uuid"))); + Assert.assertEquals("Slack", json.getJsonObject("publisher").getString("name")); + } + + @Test + public void updateScheduledNotificationRuleInvalidTest() { + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Rule 1", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + rule = qm.detach(ScheduledNotificationRule.class, rule.getId()); + rule.setUuid(UUID.randomUUID()); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE).request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(rule, MediaType.APPLICATION_JSON)); + Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The UUID of the scheduled notification rule could not be found.", body); + } + + @Test + public void deleteScheduledNotificationRuleTest() { + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Rule 1", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + rule.setName("Example Rule"); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE).request() + .header(X_API_KEY, apiKey) + .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true) // HACK + .method("DELETE", Entity.entity(rule, MediaType.APPLICATION_JSON)); // HACK + // Hack: Workaround to https://github.com/eclipse-ee4j/jersey/issues/3798 + Assert.assertEquals(204, response.getStatus(), 0); + } + + @Test + public void addProjectToRuleTest() { + Project project = qm.createProject("Acme Example", null, null, null, null, null, true, false); + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/project/" + project.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .post(Entity.json("")); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertNotNull(json); + Assert.assertEquals("Example Rule", json.getString("name")); + Assert.assertEquals(1, json.getJsonArray("projects").size()); + Assert.assertEquals("Acme Example", json.getJsonArray("projects").getJsonObject(0).getString("name")); + Assert.assertEquals(project.getUuid().toString(), json.getJsonArray("projects").getJsonObject(0).getString("uuid")); + } + + @Test + public void addProjectToRuleInvalidRuleTest() { + Project project = qm.createProject("Acme Example", null, null, null, null, null, true, false); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + UUID.randomUUID().toString() + "/project/" + project.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .post(Entity.json("")); + Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The scheduled notification rule could not be found.", body); + } + + @Test + public void addProjectToRuleInvalidScopeTest() { + Project project = qm.createProject("Acme Example", null, null, null, null, null, true, false); + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.SYSTEM, NotificationLevel.INFORMATIONAL, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/project/" + project.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .post(Entity.json("")); + Assert.assertEquals(406, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("Project limitations are only possible on scheduled notification rules with PORTFOLIO scope.", body); + } + + @Test + public void addProjectToRuleInvalidProjectTest() { + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/project/" + UUID.randomUUID().toString()).request() + .header(X_API_KEY, apiKey) + .post(Entity.json("")); + Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The project could not be found.", body); + } + + @Test + public void addProjectToRuleDuplicateProjectTest() { + Project project = qm.createProject("Acme Example", null, null, null, null, null, true, false); + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + List projects = new ArrayList<>(); + projects.add(project); + rule.setProjects(projects); + qm.persist(rule); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/project/" + project.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .post(Entity.json("")); + Assert.assertEquals(304, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + } + + @Test + public void removeProjectFromRuleTest() { + Project project = qm.createProject("Acme Example", null, null, null, null, null, true, false); + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + List projects = new ArrayList<>(); + projects.add(project); + rule.setProjects(projects); + qm.persist(rule); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/project/" + project.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .delete(); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + } + + @Test + public void removeProjectFromRuleInvalidRuleTest() { + Project project = qm.createProject("Acme Example", null, null, null, null, null, true, false); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + UUID.randomUUID().toString() + "/project/" + project.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .delete(); + Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The scheduled notification rule could not be found.", body); + } + + @Test + public void removeProjectFromRuleInvalidScopeTest() { + Project project = qm.createProject("Acme Example", null, null, null, null, null, true, false); + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.SYSTEM, NotificationLevel.INFORMATIONAL, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/project/" + project.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .delete(); + Assert.assertEquals(406, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("Project limitations are only possible on scheduled notification rules with PORTFOLIO scope.", body); + } + + @Test + public void removeProjectFromRuleInvalidProjectTest() { + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/project/" + UUID.randomUUID().toString()).request() + .header(X_API_KEY, apiKey) + .delete(); + Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The project could not be found.", body); + } + + @Test + public void removeProjectFromRuleDuplicateProjectTest() { + Project project = qm.createProject("Acme Example", null, null, null, null, null, true, false); + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/project/" + project.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .delete(); + Assert.assertEquals(304, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + } + + @Test + public void addTeamToRuleTest(){ + Team team = qm.createTeam("Team Example", false); + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.EMAIL.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/team/" + team.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .post(Entity.json("")); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertNotNull(json); + Assert.assertEquals("Example Rule", json.getString("name")); + Assert.assertEquals(1, json.getJsonArray("teams").size()); + Assert.assertEquals("Team Example", json.getJsonArray("teams").getJsonObject(0).getString("name")); + Assert.assertEquals(team.getUuid().toString(), json.getJsonArray("teams").getJsonObject(0).getString("uuid")); + } + + @Test + public void addTeamToRuleInvalidRuleTest(){ + Team team = qm.createTeam("Team Example", false); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + UUID.randomUUID().toString() + "/team/" + team.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .post(Entity.json("")); + Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The scheduled notification rule could not be found.", body); + } + + @Test + public void addTeamToRuleInvalidTeamTest() { + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.EMAIL.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/team/" + UUID.randomUUID().toString()).request() + .header(X_API_KEY, apiKey) + .post(Entity.json("")); + Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The team could not be found.", body); + } + + @Test + public void addTeamToRuleDuplicateTeamTest() { + Team team = qm.createTeam("Team Example", false); + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.EMAIL.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + List teams = new ArrayList<>(); + teams.add(team); + rule.setTeams(teams); + qm.persist(rule); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/team/" + team.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .post(Entity.json("")); + Assert.assertEquals(304, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + } + + @Test + public void addTeamToRuleInvalidPublisherTest(){ + Team team = qm.createTeam("Team Example", false); + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/team/" + team.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .post(Entity.json("")); + Assert.assertEquals(406, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("Team subscriptions are only possible on scheduled notification rules with EMAIL publisher.", body); + } + + @Test + public void addTeamToRuleWithCustomEmailPublisherTest() { + final Team team = qm.createTeam("Team Example", false); + final NotificationPublisher publisher = qm.createNotificationPublisher("foo", "description", SendMailPublisher.class, "template", "templateMimeType", false, true); + final ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + final ZonedDateTime testTime = ZonedDateTime.parse("2024-05-31T13:24:46Z", DateTimeFormatter.ISO_OFFSET_DATE_TIME); + rule.setLastExecutionTime(testTime); + final Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid() + "/team/" + team.getUuid()).request() + .header(X_API_KEY, apiKey) + .post(Entity.json("")); + assertThat(response.getStatus()).isEqualTo(200); + assertThatJson(getPlainTextBody(response)) + .withMatcher("publisherUuid", equalTo(publisher.getUuid().toString())) + .withMatcher("ruleUuid", equalTo(rule.getUuid().toString())) + .withMatcher("teamUuid", equalTo(team.getUuid().toString())) + .withMatcher("cronConfig", equalTo(rule.getCronConfig())) + .isEqualTo(""" + { + "name": "Example Rule", + "enabled": true, + "notifyChildren": true, + "logSuccessfulPublish": false, + "scope": "PORTFOLIO", + "notificationLevel": "INFORMATIONAL", + "projects": [], + "teams": [ + { + "uuid": "${json-unit.matches:teamUuid}", + "name": "Team Example", + "permissions": [] + } + ], + "notifyOn": [], + "publisher": { + "name": "foo", + "description": "description", + "publisherClass": "org.dependencytrack.notification.publisher.SendMailPublisher", + "templateMimeType": "templateMimeType", + "defaultPublisher": false, + "publishScheduled": true, + "uuid": "${json-unit.matches:publisherUuid}" + }, + "uuid": "${json-unit.matches:ruleUuid}", + "cronConfig": "${json-unit.matches:cronConfig}", + "lastExecutionTime": "2024-05-31T13:24:46Z", + "publishOnlyWithUpdates": false + } + """); + } + + @Test + public void removeTeamFromRuleTest() { + Team team = qm.createTeam("Team Example", false); + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SCHEDULED_EMAIL.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + List teams = new ArrayList<>(); + teams.add(team); + rule.setTeams(teams); + qm.persist(rule); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/team/" + team.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .delete(); + Assert.assertEquals(200, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + } + + @Test + public void removeTeamFromRuleInvalidRuleTest() { + Team team = qm.createTeam("Team Example", false); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + UUID.randomUUID().toString() + "/team/" + team.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .delete(); + Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The scheduled notification rule could not be found.", body); + } + + @Test + public void removeTeamFromRuleInvalidTeamTest() { + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SCHEDULED_EMAIL.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/team/" + UUID.randomUUID().toString()).request() + .header(X_API_KEY, apiKey) + .delete(); + Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The team could not be found.", body); + } + + @Test + public void removeTeamFromRuleDuplicateTeamTest() { + Team team = qm.createTeam("Team Example", false); + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SCHEDULED_EMAIL.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/team/" + team.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .delete(); + Assert.assertEquals(304, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + } + + @Test + public void removeTeamToRuleInvalidPublisherTest(){ + Team team = qm.createTeam("Team Example", false); + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SLACK.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/" + rule.getUuid().toString() + "/team/" + team.getUuid().toString()).request() + .header(X_API_KEY, apiKey) + .delete(); + Assert.assertEquals(406, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("Team subscriptions are only possible on scheduled notification rules with EMAIL publisher.", body); + } + + @Test + public void executeScheduledNotificationRuleNowTest(){ + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SCHEDULED_EMAIL.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/execute").request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(rule, MediaType.APPLICATION_JSON)); + Assert.assertEquals(200, response.getStatus(), 0); + JsonObject json = parseJsonObject(response); + Assert.assertNotNull(json); + Assert.assertEquals("Example Rule", json.getString("name")); + Assert.assertEquals(rule.getUuid().toString(), json.getString("uuid")); + } + + @Test + public void executeScheduledNotificationRuleNowInvalidRuleTest(){ + NotificationPublisher publisher = qm.getNotificationPublisher(DefaultNotificationPublishers.SCHEDULED_EMAIL.getPublisherName()); + ScheduledNotificationRule rule = qm.createScheduledNotificationRule("Example Rule", NotificationScope.PORTFOLIO, NotificationLevel.INFORMATIONAL, publisher); + // detach the rule to uncouple from database, else setUuid(...) will update the persistent entry and the request will be valid with http code 200 + rule = qm.detach(ScheduledNotificationRule.class, rule.getId()); + rule.setUuid(UUID.randomUUID()); + Response response = target(V1_SCHEDULED_NOTIFICATION_RULE + "/execute").request() + .header(X_API_KEY, apiKey) + .post(Entity.entity(rule, MediaType.APPLICATION_JSON)); + Assert.assertEquals(404, response.getStatus(), 0); + Assert.assertNull(response.getHeaderString(TOTAL_COUNT_HEADER)); + String body = getPlainTextBody(response); + Assert.assertEquals("The UUID of the scheduled notification rule could not be found.", body); + } +} From ebfd7a004d01e5c3541d99b43380ef24dd60e81d Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 5 Jun 2024 13:23:18 +0200 Subject: [PATCH 301/429] added new data models to match new provided pebble template Signed-off-by: Max Schiller --- .../model/scheduled/DetailInfo.java | 93 +++++++++++++ .../model/scheduled/Details.java | 45 +++++++ .../model/scheduled/Overview.java | 91 +++++++++++++ .../model/scheduled/Summary.java | 45 +++++++ .../model/scheduled/SummaryInfo.java | 62 +++++++++ .../publisher/scheduled_email_summary.peb | 125 ++++++++++++++++++ 6 files changed, 461 insertions(+) create mode 100644 src/main/java/org/dependencytrack/model/scheduled/DetailInfo.java create mode 100644 src/main/java/org/dependencytrack/model/scheduled/Details.java create mode 100644 src/main/java/org/dependencytrack/model/scheduled/Overview.java create mode 100644 src/main/java/org/dependencytrack/model/scheduled/Summary.java create mode 100644 src/main/java/org/dependencytrack/model/scheduled/SummaryInfo.java create mode 100644 src/main/resources/templates/notification/publisher/scheduled_email_summary.peb diff --git a/src/main/java/org/dependencytrack/model/scheduled/DetailInfo.java b/src/main/java/org/dependencytrack/model/scheduled/DetailInfo.java new file mode 100644 index 0000000000..159f23c62d --- /dev/null +++ b/src/main/java/org/dependencytrack/model/scheduled/DetailInfo.java @@ -0,0 +1,93 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.model.scheduled; + +import java.text.DateFormat; +import java.text.ParseException; +import java.util.Date; +import org.dependencytrack.model.Finding; + +import alpine.common.logging.Logger; + +public class DetailInfo { + private static final Logger LOGGER = Logger.getLogger(DetailInfo.class); + + private final String componentName; + private final String componentVersion; + private final String componentGroup; + private final String vulnerabilityId; + private final String vulnerabilitySeverity; + private final String analyzer; + private Date attributedOn; + private final String analysisState; + private final Boolean suppressed; + + public DetailInfo(Finding finding) { + this.componentName = (String) finding.getComponent().get("name"); + this.componentVersion = (String) finding.getComponent().get("version"); + this.componentGroup = (String) finding.getComponent().get("group"); + this.vulnerabilityId = (String) finding.getVulnerability().get("vulnId"); + this.vulnerabilitySeverity = (String) finding.getVulnerability().get("severity"); + this.analyzer = (String) finding.getAttribution().get("analyzerIdentity"); + try { + this.attributedOn = DateFormat.getDateTimeInstance().parse((String) finding.getAttribution().get("attributedOn")); + } catch (ParseException e) { + this.attributedOn = null; + LOGGER.error("An error occurred while parsing the attributedOn date for component" + this.componentName); + } + this.analysisState = (String) finding.getAnalysis().get("state"); + this.suppressed = (Boolean) finding.getAnalysis().get("isSuppressed"); + } + + public String getComponentName() { + return componentName; + } + + public String getComponentVersion() { + return componentVersion; + } + + public String getComponentGroup() { + return componentGroup; + } + + public String getVulnerabilityId() { + return vulnerabilityId; + } + + public String getVulnerabilitySeverity() { + return vulnerabilitySeverity; + } + + public String getAnalyzer() { + return analyzer; + } + + public Date getAttributedOn() { + return attributedOn; + } + + public String getAnalysisState() { + return analysisState; + } + + public Boolean getSuppressed() { + return suppressed; + } +} diff --git a/src/main/java/org/dependencytrack/model/scheduled/Details.java b/src/main/java/org/dependencytrack/model/scheduled/Details.java new file mode 100644 index 0000000000..7ca7b228b6 --- /dev/null +++ b/src/main/java/org/dependencytrack/model/scheduled/Details.java @@ -0,0 +1,45 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.model.scheduled; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.dependencytrack.model.Project; +import org.dependencytrack.persistence.QueryManager; + +public final class Details { + private final Map> affectedProjectFindings = new TreeMap<>(); + + public Details(final List affectedProjects, ZonedDateTime lastExecution) { + try (var qm = new QueryManager()) { + for (Project project : affectedProjects) { + var findings = qm.getFindingsSince(project, false, lastExecution.withZoneSameInstant(ZoneOffset.UTC)); + affectedProjectFindings.put(project, findings.stream().map(f -> new DetailInfo(f)).toList()); + } + } + } + + public Map> getAffectedProjectFindings() { + return affectedProjectFindings; + } +} diff --git a/src/main/java/org/dependencytrack/model/scheduled/Overview.java b/src/main/java/org/dependencytrack/model/scheduled/Overview.java new file mode 100644 index 0000000000..f5a65ceaf2 --- /dev/null +++ b/src/main/java/org/dependencytrack/model/scheduled/Overview.java @@ -0,0 +1,91 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.model.scheduled; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.EnumMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import org.dependencytrack.model.Component; +import org.dependencytrack.model.Finding; +import org.dependencytrack.model.Project; +import org.dependencytrack.model.Severity; +import org.dependencytrack.model.Vulnerability; +import org.dependencytrack.persistence.QueryManager; + +public final class Overview { + private final Integer affectedProjectsCount; + private final Integer newVulnerabilitiesCount; + private final Map newVulnerabilitiesBySeverity = new EnumMap<>(Severity.class); + private final Integer affectedComponentsCount; + private final Integer suppressedNewVulnerabilitiesCount; + + public Overview(final List affectedProjects, ZonedDateTime lastExecution) { + var componentCache = new HashSet(); + var vulnerabilityCache = new HashSet(); + var suppressedVulnerabilityCache = new HashSet(); + + try (var qm = new QueryManager()) { + for (Project project : affectedProjects) { + var findings = qm.getFindingsSince(project, false, lastExecution.withZoneSameInstant(ZoneOffset.UTC)); + for (Finding finding : findings) { + Component component = qm.getObjectByUuid(Component.class, (String) finding.getComponent().get("uuid")); + componentCache.add(component); + + Vulnerability vulnerability = qm.getObjectByUuid(Vulnerability.class, (String) finding.getVulnerability().get("uuid")); + if (finding.getAnalysis().get("istSuppressed") instanceof Boolean suppressed) { + if (suppressed) { + suppressedVulnerabilityCache.add(vulnerability); + } else { + vulnerabilityCache.add(vulnerability); + newVulnerabilitiesBySeverity.merge(vulnerability.getSeverity(), 1, Integer::sum); + } + } + } + } + } + + affectedProjectsCount = affectedProjects.size(); + newVulnerabilitiesCount = vulnerabilityCache.size(); + affectedComponentsCount = componentCache.size(); + suppressedNewVulnerabilitiesCount = suppressedVulnerabilityCache.size(); + } + + public Integer getAffectedProjectsCount() { + return affectedProjectsCount; + } + + public Integer getNewVulnerabilitiesCount() { + return newVulnerabilitiesCount; + } + + public Map getNewVulnerabilitiesBySeverity() { + return newVulnerabilitiesBySeverity; + } + + public Integer getAffectedComponentsCount() { + return affectedComponentsCount; + } + + public Integer getSuppressedNewVulnerabilitiesCount() { + return suppressedNewVulnerabilitiesCount; + } +} diff --git a/src/main/java/org/dependencytrack/model/scheduled/Summary.java b/src/main/java/org/dependencytrack/model/scheduled/Summary.java new file mode 100644 index 0000000000..1e4fd64c80 --- /dev/null +++ b/src/main/java/org/dependencytrack/model/scheduled/Summary.java @@ -0,0 +1,45 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.model.scheduled; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.dependencytrack.model.Project; +import org.dependencytrack.persistence.QueryManager; + +public final class Summary { + private final Map affectedProjectSummaries = new TreeMap<>(); + + public Summary(final List affectedProjects, ZonedDateTime lastExecution) { + try (var qm = new QueryManager()) { + for (Project project : affectedProjects) { + var findings = qm.getFindingsSince(project, false, lastExecution.withZoneSameInstant(ZoneOffset.UTC)); + affectedProjectSummaries.put(project, new SummaryInfo(findings)); + } + } + } + + public Map getAffectedProjectSummaries() { + return affectedProjectSummaries; + } +} diff --git a/src/main/java/org/dependencytrack/model/scheduled/SummaryInfo.java b/src/main/java/org/dependencytrack/model/scheduled/SummaryInfo.java new file mode 100644 index 0000000000..51319b2376 --- /dev/null +++ b/src/main/java/org/dependencytrack/model/scheduled/SummaryInfo.java @@ -0,0 +1,62 @@ +/* + * This file is part of Dependency-Track. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) OWASP Foundation. All Rights Reserved. + */ +package org.dependencytrack.model.scheduled; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +import org.dependencytrack.model.Finding; +import org.dependencytrack.model.Severity; +import org.dependencytrack.model.Vulnerability; +import org.dependencytrack.persistence.QueryManager; + +public final class SummaryInfo { + private final Map newVulnerabilitiesBySeverity = new EnumMap<>(Severity.class); + private final Map totalProjectVulnerabilitiesBySeverity = new EnumMap<>(Severity.class); + private final Map suppressedNewVulnerabilitiesBySeverity = new EnumMap<>(Severity.class); + + public SummaryInfo(final List findings) { + try (var qm = new QueryManager()) { + for (Finding finding : findings) { + Vulnerability vulnerability = qm.getObjectByUuid(Vulnerability.class, (String) finding.getVulnerability().get("uuid")); + if (finding.getAnalysis().get("isSuppressed") instanceof Boolean suppressed) { + if (suppressed) { + suppressedNewVulnerabilitiesBySeverity.merge(vulnerability.getSeverity(), 1, Integer::sum); + } else { + newVulnerabilitiesBySeverity.merge(vulnerability.getSeverity(), 1, Integer::sum); + } + totalProjectVulnerabilitiesBySeverity.merge(vulnerability.getSeverity(), 1, Integer::sum); + } + } + } + } + + public Map getNewVulnerabilitiesBySeverity() { + return newVulnerabilitiesBySeverity; + } + + public Map getTotalProjectVulnerabilitiesBySeverity() { + return totalProjectVulnerabilitiesBySeverity; + } + + public Map getSuppressedNewVulnerabilitiesBySeverity() { + return suppressedNewVulnerabilitiesBySeverity; + } +} diff --git a/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb b/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb new file mode 100644 index 0000000000..4e03e83960 --- /dev/null +++ b/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb @@ -0,0 +1,125 @@ + + + + {{ notification.title }} + ------------- + {{ notification.description }} + + + +

          Overview

          + + + + + + + + + + {% if subject.overview.newVulnerabilitiesBySeverity|length > 0 %}{% for entry in subject.overview.newVulnerabilitiesBySeverity %} + + + + {% endfor %}{% endif %} + + + + + + + + + +
          Projects included in this rule{{ subject.overview.affectedProjectsCount }}
          Total new vulnerabilities{{ subject.overview.newVulnerabilitiesCount }}
          New vulnerabilities ({{ entry.key }}){{ entry.value }}
          Components affected by new vulnerabilities{{ subject.overview.affectedComponentsCount }}
          Suppressed new vulnerabilities (not included above){{ subject.overview.suppressedNewVulnerabilitiesCount }}
          + +

          Summary per project in this rule

          + + + + + + + + + + + {% for entry in subject.summary.affectedProjectSummaries %} + + + + + + + {% endfor %} + +
          Project NameVersionNew VulnerabilitiesAll VulnerabilitiesSuppressed Vulnerabilities
          + + {{ entry.key.name }} + + {{ entry.key.version }} + {% for newVulnEntry in entry.value.newVulnerabilitiesBySeverity %} + {{ newVulnEntry.key }}: {{ newVulnEntry.value }}
          + {% endfor %} +
          + {% for projVulnEntry in entry.value.totalProjectVulnerabilitiesBySeverity %} + {{ projVulnEntry.key }}: {{ projVulnEntry.value }}
          + {% endfor %} +
          + {% for supprVulnEntry in entry.value.suppressedNewVulnerabilitiesBySeverity %} + {{ supprVulnEntry.key }}: {{ supprVulnEntry.value }}
          + {% endfor %} +
          + +

          New vulnerabilities per project

          + + {% if subject.details.affectedProjectFindings|length > 0 %}{% for affProjEntry in subject.details.affectedProjectFindings %} +

          Project "{{ affProjEntry.key.name }}"" [Version: {{ affProjEntry.key.version }}]

          + + + + + + + + + + + + + + + {% for finding in findings %} + + + + + + + + + + + {% endfor %} + +
          ComponentVersionGroupVulnerabilitySeverityAnalyzerAttributed OnAnalysisSuppressed
          + + {{ affProjEntry.value.componentName }} + + {{ affProjEntry.value.componentVersion }} + + {{ affProjEntry.value.componentGroup }} + + + + {{ affProjEntry.value.vulnerabilityId }} + + {{ affProjEntry.value.vulnerabilitySeverity }}{{ affProjEntry.value.analyzer }}{{ affProjEntry.value.attributedOn }}{{ affProjEntry.value.analysisState }}{{ affProjEntry.value.suppressed }}
          + {% endfor %}{% endif %} + +
          +

          + {{ timestamp }} +

          +
          + From d1a184f48807a095220a7f9be0e436700741db54 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 5 Jun 2024 13:24:38 +0200 Subject: [PATCH 302/429] changed depending classes to use new template models Signed-off-by: Max Schiller --- .../org/dependencytrack/model/Finding.java | 46 ++++++ .../model/ScheduledNotificationRule.java | 2 +- .../DefaultNotificationPublishers.java | 2 +- .../publisher/PublishContext.java | 3 +- ...ScheduledNewVulnerabilitiesIdentified.java | 50 +++---- .../persistence/FindingsQueryManager.java | 102 +++++--------- .../persistence/QueryManager.java | 4 + .../tasks/SendScheduledNotificationTask.java | 48 ++++--- .../util/NotificationUtil.java | 132 +++++++++++------- ...duledNewVulnerabilitiesIdentifiedTest.java | 44 +++--- 10 files changed, 237 insertions(+), 196 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/Finding.java b/src/main/java/org/dependencytrack/model/Finding.java index 13867227b2..e986a120b1 100644 --- a/src/main/java/org/dependencytrack/model/Finding.java +++ b/src/main/java/org/dependencytrack/model/Finding.java @@ -104,6 +104,52 @@ public class Finding implements Serializable { AND (:includeSuppressed = :true OR "ANALYSIS"."SUPPRESSED" IS NULL OR "ANALYSIS"."SUPPRESSED" = :false) """; + // language=SQL + public static final String QUERY_SINCE_ATTRIBUTION = """ + SELECT "COMPONENT"."UUID" + , "COMPONENT"."NAME" + , "COMPONENT"."GROUP" + , "COMPONENT"."VERSION" + , "COMPONENT"."PURL" + , "COMPONENT"."CPE" + , "VULNERABILITY"."UUID" + , "VULNERABILITY"."SOURCE" + , "VULNERABILITY"."VULNID" + , "VULNERABILITY"."TITLE" + , "VULNERABILITY"."SUBTITLE" + , "VULNERABILITY"."DESCRIPTION" + , "VULNERABILITY"."RECOMMENDATION" + , "VULNERABILITY"."SEVERITY" + , "VULNERABILITY"."CVSSV2BASESCORE" + , "VULNERABILITY"."CVSSV3BASESCORE" + , "VULNERABILITY"."OWASPRRLIKELIHOODSCORE" + , "VULNERABILITY"."OWASPRRTECHNICALIMPACTSCORE" + , "VULNERABILITY"."OWASPRRBUSINESSIMPACTSCORE" + , "VULNERABILITY"."EPSSSCORE" + , "VULNERABILITY"."EPSSPERCENTILE" + , "VULNERABILITY"."CWES" + , "FINDINGATTRIBUTION"."ANALYZERIDENTITY" + , "FINDINGATTRIBUTION"."ATTRIBUTED_ON" + , "FINDINGATTRIBUTION"."ALT_ID" + , "FINDINGATTRIBUTION"."REFERENCE_URL" + , "ANALYSIS"."STATE" + , "ANALYSIS"."SUPPRESSED" + FROM "COMPONENT" + INNER JOIN "COMPONENTS_VULNERABILITIES" + ON "COMPONENT"."ID" = "COMPONENTS_VULNERABILITIES"."COMPONENT_ID" + INNER JOIN "VULNERABILITY" + ON "COMPONENTS_VULNERABILITIES"."VULNERABILITY_ID" = "VULNERABILITY"."ID" + INNER JOIN "FINDINGATTRIBUTION" + ON "COMPONENT"."ID" = "FINDINGATTRIBUTION"."COMPONENT_ID" + AND "VULNERABILITY"."ID" = "FINDINGATTRIBUTION"."VULNERABILITY_ID" + LEFT JOIN "ANALYSIS" + ON "COMPONENT"."ID" = "ANALYSIS"."COMPONENT_ID" + AND "VULNERABILITY"."ID" = "ANALYSIS"."VULNERABILITY_ID" + AND "COMPONENT"."PROJECT_ID" = "ANALYSIS"."PROJECT_ID" + WHERE "COMPONENT"."PROJECT_ID" = ? + AND "FINDINGATTRIBUTION"."ATTRIBUTED_ON" BETWEEN ? AND ? + """; + // language=SQL public static final String QUERY_ALL_FINDINGS = """ SELECT "COMPONENT"."UUID" diff --git a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java index 5a1fce9601..7d9535c2e8 100644 --- a/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java +++ b/src/main/java/org/dependencytrack/model/ScheduledNotificationRule.java @@ -321,7 +321,7 @@ public ZonedDateTime getLastExecutionTime() { if (lastExecutionTime == null) { return ZonedDateTime.now(ZoneOffset.UTC); } - return lastExecutionTime; + return lastExecutionTime.withZoneSameInstant(ZoneOffset.UTC); } public void setLastExecutionTime(ZonedDateTime lastExecutionTime) { diff --git a/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java b/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java index 8e22a3141b..3df8c7b635 100644 --- a/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java +++ b/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java @@ -26,7 +26,7 @@ public enum DefaultNotificationPublishers { MS_TEAMS("Microsoft Teams", "Publishes notifications to a Microsoft Teams channel", MsTeamsPublisher.class, "/templates/notification/publisher/msteams.peb", MediaType.APPLICATION_JSON, true), MATTERMOST("Mattermost", "Publishes notifications to a Mattermost channel", MattermostPublisher.class, "/templates/notification/publisher/mattermost.peb", MediaType.APPLICATION_JSON, true), EMAIL("Email", "Sends notifications to an email address", SendMailPublisher.class, "/templates/notification/publisher/email.peb", MediaType.TEXT_PLAIN, true), - SCHEDULED_EMAIL("Scheduled Email", "Sends summarized notifications to an email address in a defined schedule", SendMailPublisher.class, "/templates/notification/publisher/scheduled_email.peb", MediaType.TEXT_PLAIN, true, true), + SCHEDULED_EMAIL("Scheduled Email", "Sends summarized notifications to an email address in a defined schedule", SendMailPublisher.class, "/templates/notification/publisher/scheduled_email_summary.peb", MediaType.TEXT_HTML, true, true), CONSOLE("Console", "Displays notifications on the system console", ConsolePublisher.class, "/templates/notification/publisher/console.peb", MediaType.TEXT_PLAIN, true), // SCHEDULED_CONSOLE("Scheduled Console", "Displays summarized notifications on the system console in a defined schedule", ConsolePublisher.class, "/templates/notification/publisher/scheduled_console.peb", MediaType.TEXT_PLAIN, true, true), WEBHOOK("Outbound Webhook", "Publishes notifications to a configurable endpoint", WebhookPublisher.class, "/templates/notification/publisher/webhook.peb", MediaType.APPLICATION_JSON, true), diff --git a/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java b/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java index 208b9ff5ad..d01b69a255 100644 --- a/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java +++ b/src/main/java/org/dependencytrack/notification/publisher/PublishContext.java @@ -105,8 +105,7 @@ public static PublishContext from(final Notification notification) { } else if (notification.getSubject() instanceof final VexConsumedOrProcessed subject) { notificationSubjects.put(SUBJECT_PROJECT, Project.convert(subject.getProject())); } else if (notification.getSubject() instanceof final ScheduledNewVulnerabilitiesIdentified subject) { - notificationSubjects.put(SUBJECT_PROJECTS, subject.getNewProjectVulnerabilities().keySet().stream().map(Project::convert).toList()); - notificationSubjects.put(SUBJECT_VULNERABILITIES, subject.getNewVulnerabilitiesTotal().stream().map(Vulnerability::convert).toList()); + notificationSubjects.put(SUBJECT_PROJECTS, subject.getSummary().getAffectedProjectSummaries().keySet().stream().map(Project::convert).toList()); } else if (notification.getSubject() instanceof final ScheduledPolicyViolationsIdentified subject) { notificationSubjects.put(SUBJECT_PROJECTS, subject.getNewProjectPolicyViolations().keySet().stream().map(Project::convert).toList()); } diff --git a/src/main/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentified.java b/src/main/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentified.java index 6a25b94a2e..a834cc8cd5 100644 --- a/src/main/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentified.java +++ b/src/main/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentified.java @@ -18,46 +18,34 @@ */ package org.dependencytrack.notification.vo; -import java.util.EnumMap; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import org.dependencytrack.model.Project; -import org.dependencytrack.model.Severity; -import org.dependencytrack.model.Vulnerability; +import org.dependencytrack.model.scheduled.Details; +import org.dependencytrack.model.scheduled.Overview; +import org.dependencytrack.model.scheduled.Summary; public class ScheduledNewVulnerabilitiesIdentified { - private final Map> newProjectVulnerabilities; - private final Map>> newProjectVulnerabilitiesBySeverity; - private final List newVulnerabilitiesTotal; - private final Map> newVulnerabilitiesTotalBySeverity; - - public ScheduledNewVulnerabilitiesIdentified(Map> newProjectVulnerabilities) { - this.newProjectVulnerabilities = newProjectVulnerabilities; - this.newProjectVulnerabilitiesBySeverity = newProjectVulnerabilities.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().stream() - .collect(Collectors.groupingBy(Vulnerability::getSeverity, () -> new EnumMap<>(Severity.class), Collectors.toList())))); - this.newVulnerabilitiesTotal = newProjectVulnerabilities.values().stream() - .flatMap(List::stream) - .collect(Collectors.toList()); - this.newVulnerabilitiesTotalBySeverity = newVulnerabilitiesTotal.stream() - .collect(Collectors.groupingBy(Vulnerability::getSeverity, () -> new EnumMap<>(Severity.class), Collectors.toList())); - } - - public Map> getNewProjectVulnerabilities() { - return newProjectVulnerabilities; + private final Overview overview; + private final Summary summary; + private final Details details; + + public ScheduledNewVulnerabilitiesIdentified(final List ruleProjects, ZonedDateTime lastExecution) { + this.overview = new Overview(ruleProjects, lastExecution.withZoneSameInstant(ZoneOffset.UTC)); + this.summary = new Summary(ruleProjects, lastExecution.withZoneSameInstant(ZoneOffset.UTC)); + this.details = new Details(ruleProjects, lastExecution.withZoneSameInstant(ZoneOffset.UTC)); } - public Map>> getNewProjectVulnerabilitiesBySeverity() { - return newProjectVulnerabilitiesBySeverity; + public Overview getOverview() { + return overview; } - public List getNewVulnerabilitiesTotal() { - return newVulnerabilitiesTotal; + public Summary getSummary() { + return summary; } - public Map> getNewVulnerabilitiesTotalBySeverity() { - return newVulnerabilitiesTotalBySeverity; + public Details getDetails() { + return details; } } diff --git a/src/main/java/org/dependencytrack/persistence/FindingsQueryManager.java b/src/main/java/org/dependencytrack/persistence/FindingsQueryManager.java index d56394f2d1..2abded25f7 100644 --- a/src/main/java/org/dependencytrack/persistence/FindingsQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/FindingsQueryManager.java @@ -37,6 +37,9 @@ import javax.jdo.PersistenceManager; import javax.jdo.Query; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.Collections; import java.util.Date; import java.util.List; @@ -258,81 +261,52 @@ void deleteAnalysisTrail(Project project) { */ @SuppressWarnings("unchecked") public List getFindings(Project project) { - return getFindings(project, false); + return getFindings(project, false, null); } /** * Returns a List of Finding objects for the specified project. * @param project the project to retrieve findings for * @param includeSuppressed determines if suppressed vulnerabilities should be included or not + * @param sinceAttributedOn only include findings that have been attributed on or after this datetime (optional) * @return a List of Finding objects */ @SuppressWarnings("unchecked") - public List getFindings(Project project, boolean includeSuppressed) { - final Query query = pm.newQuery(Query.SQL, Finding.QUERY); - query.setNamedParameters(Map.ofEntries( - Map.entry("projectId", project.getId()), - Map.entry("includeSuppressed", includeSuppressed), - // NB: These are required for MSSQL, apparently it doesn't have - // a native boolean type, or DataNucleus maps booleans to a type - // that doesn't have boolean semantics. Fun! - Map.entry("false", false), - Map.entry("true", true) - )); - final List queryResultRows = executeAndCloseList(query); - - final List findings = queryResultRows.stream() - .map(row -> new Finding(project.getUuid(), row)) - .toList(); - - final Map> findingsByVulnIdAndSource = findings.stream() - .collect(Collectors.groupingBy( - finding -> new VulnIdAndSource( - (String) finding.getVulnerability().get("vulnId"), - (String) finding.getVulnerability().get("source") - ) - )); - final Map> aliasesByVulnIdAndSource = - getVulnerabilityAliases(findingsByVulnIdAndSource.keySet()); - for (final VulnIdAndSource vulnIdAndSource : findingsByVulnIdAndSource.keySet()) { - final List affectedFindings = findingsByVulnIdAndSource.get(vulnIdAndSource); - final List aliases = aliasesByVulnIdAndSource.getOrDefault(vulnIdAndSource, Collections.emptyList()); - - for (final Finding finding : affectedFindings) { - finding.addVulnerabilityAliases(aliases); - } + public List getFindings(Project project, boolean includeSuppressed, ZonedDateTime sinceAttributedOn) { + final Query query; + if (sinceAttributedOn != null) { + query = pm.newQuery(Query.SQL, Finding.QUERY); + query.setParameters(project.getId()); + } else { + query = pm.newQuery(Query.SQL, Finding.QUERY_SINCE_ATTRIBUTION); + query.setParameters(project.getId(), sinceAttributedOn, ZonedDateTime.now(ZoneOffset.UTC)); } - - final Map> findingsByMetaComponentSearch = findings.stream() - .filter(finding -> finding.getComponent().get("purl") != null) - .map(finding -> { - final PackageURL purl = PurlUtil.silentPurl((String) finding.getComponent().get("purl")); - if (purl == null) { - return null; + final List list = query.executeList(); + final List findings = new ArrayList<>(); + for (final Object[] o: list) { + final Finding finding = new Finding(project.getUuid(), o); + final Component component = getObjectByUuid(Component.class, (String)finding.getComponent().get("uuid")); + final Vulnerability vulnerability = getObjectByUuid(Vulnerability.class, (String)finding.getVulnerability().get("uuid")); + final Analysis analysis = getAnalysis(component, vulnerability); + final List aliases = detach(getVulnerabilityAliases(vulnerability)); + finding.addVulnerabilityAliases(aliases); + if (includeSuppressed || analysis == null || !analysis.isSuppressed()) { // do not add globally suppressed findings + // These are CLOB fields. Handle these here so that database-specific deserialization doesn't need to be performed (in Finding) + finding.getVulnerability().put("description", vulnerability.getDescription()); + finding.getVulnerability().put("recommendation", vulnerability.getRecommendation()); + final PackageURL purl = component.getPurl(); + if (purl != null) { + final RepositoryType type = RepositoryType.resolve(purl); + if (RepositoryType.UNSUPPORTED != type) { + final RepositoryMetaComponent repoMetaComponent = getRepositoryMetaComponent(type, purl.getNamespace(), purl.getName()); + if (repoMetaComponent != null) { + finding.getComponent().put("latestVersion", repoMetaComponent.getLatestVersion()); + } } - - final var repositoryType = RepositoryType.resolve(purl); - if (repositoryType == RepositoryType.UNSUPPORTED) { - return null; - } - - final var search = new RepositoryMetaComponentSearch(repositoryType, purl.getNamespace(), purl.getName()); - return Map.entry(search, finding); - }) - .filter(Objects::nonNull) - .collect(Collectors.groupingBy( - Map.Entry::getKey, - Collectors.mapping(Map.Entry::getValue, Collectors.toList()) - )); - getRepositoryMetaComponents(List.copyOf(findingsByMetaComponentSearch.keySet())) - .forEach(metaComponent -> { - final var search = new RepositoryMetaComponentSearch(metaComponent.getRepositoryType(), metaComponent.getNamespace(), metaComponent.getName()); - final List affectedFindings = findingsByMetaComponentSearch.get(search); - for (final Finding finding : affectedFindings) { - finding.getComponent().put("latestVersion", metaComponent.getLatestVersion()); - } - }); - + } + findings.add(finding); + } + } return findings; } } diff --git a/src/main/java/org/dependencytrack/persistence/QueryManager.java b/src/main/java/org/dependencytrack/persistence/QueryManager.java index 170999c83d..890236f13b 100644 --- a/src/main/java/org/dependencytrack/persistence/QueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/QueryManager.java @@ -1174,6 +1174,10 @@ public List getFindings(Project project, boolean includeSuppressed) { return getFindingsQueryManager().getFindings(project, includeSuppressed); } + public List getFindingsSince(Project project, boolean includeSuppressed, ZonedDateTime sinceAttributedOn) { + return getFindingsQueryManager().getFindings(project, includeSuppressed, sinceAttributedOn); + } + public PaginatedResult getAllFindings(final Map filters, final boolean showSuppressed, final boolean showInactive) { return getFindingsSearchQueryManager().getAllFindings(filters, showSuppressed, showInactive); } diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index 7d449122eb..f907438579 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -20,6 +20,9 @@ import java.io.StringReader; import java.lang.reflect.InvocationTargetException; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; import java.util.UUID; import javax.json.Json; @@ -28,13 +31,13 @@ import org.dependencytrack.exception.PublisherException; import org.dependencytrack.model.NotificationPublisher; +import org.dependencytrack.model.Project; import org.dependencytrack.model.ScheduledNotificationRule; import org.dependencytrack.notification.NotificationGroup; import org.dependencytrack.notification.publisher.PublishContext; import org.dependencytrack.notification.publisher.Publisher; import org.dependencytrack.notification.publisher.SendMailPublisher; import org.dependencytrack.notification.vo.ScheduledNewVulnerabilitiesIdentified; -import org.dependencytrack.notification.vo.ScheduledPolicyViolationsIdentified; import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.util.NotificationUtil; @@ -55,45 +58,46 @@ public SendScheduledNotificationTask(UUID scheduledNotificationRuleUuid) { public void run() { try (var qm = new QueryManager()) { var rule = qm.getObjectByUuid(ScheduledNotificationRule.class, scheduledNotificationRuleUuid); - final List projectIds = rule.getProjects().stream().map(proj -> proj.getId()).toList(); Boolean errorsDuringExecution = false; Boolean atLeastOneSuccessfulPublish = false; LOGGER.info("Processing notification publishing for scheduled notification rule " + rule.getUuid()); for (NotificationGroup group : rule.getNotifyOn()) { + List affectedProjects = rule.getProjects(); + // if rule does not limit to specific projects, get all projects + if (affectedProjects.isEmpty()) + affectedProjects = qm.getProjects().getList(Project.class); final Notification notificationProxy = new Notification() .scope(rule.getScope()) .group(group) - .title(NotificationUtil.generateNotificationTitle(group, rule.getProjects())) + .title(NotificationUtil.generateNotificationTitle(group, affectedProjects)) .level(rule.getNotificationLevel()); switch (group) { case NEW_VULNERABILITY: - var newProjectVulnerabilities = qm.getNewVulnerabilitiesForProjectsSince(rule.getLastExecutionTime(), projectIds); - if(newProjectVulnerabilities.isEmpty() && rule.getPublishOnlyWithUpdates()) + ScheduledNewVulnerabilitiesIdentified vulnSubject = new ScheduledNewVulnerabilitiesIdentified(affectedProjects, ZonedDateTime.of(2023, 05, 20, 0, 0, 0, 0, ZoneId.systemDefault())/* rule.getLastExecutionTime() */); + if(vulnSubject.getOverview().getNewVulnerabilitiesCount() == 0 && rule.getPublishOnlyWithUpdates()) continue; - ScheduledNewVulnerabilitiesIdentified vulnSubject = new ScheduledNewVulnerabilitiesIdentified(newProjectVulnerabilities); notificationProxy - .content(NotificationUtil.generateVulnerabilityScheduledNotificationContent( - rule, - vulnSubject.getNewVulnerabilitiesTotal(), - newProjectVulnerabilities.keySet().stream().toList(), - rule.getLastExecutionTime())) + .title("[Dependency-Track] Scheduled Notification of Rule “" + rule.getName() + "”") + .content("Find below a summary of new vulnerabilities since " + + rule.getLastExecutionTime().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) + + " in Scheduled Notification Rule “" + rule.getName() + "”.") .subject(vulnSubject); break; case POLICY_VIOLATION: - var newProjectPolicyViolations = qm.getNewPolicyViolationsForProjectsSince(rule.getLastExecutionTime(), projectIds); - if(newProjectPolicyViolations.isEmpty() && rule.getPublishOnlyWithUpdates()) - continue; - ScheduledPolicyViolationsIdentified policySubject = new ScheduledPolicyViolationsIdentified(newProjectPolicyViolations); - notificationProxy - .content(NotificationUtil.generatePolicyScheduledNotificationContent( - rule, - policySubject.getNewPolicyViolationsTotal(), - newProjectPolicyViolations.keySet().stream().toList(), - rule.getLastExecutionTime())) - .subject(policySubject); + // var newProjectPolicyViolations = qm.getNewPolicyViolationsForProjectsSince(rule.getLastExecutionTime(), projectIds); + // if(newProjectPolicyViolations.isEmpty() && rule.getPublishOnlyWithUpdates()) + // continue; + // ScheduledPolicyViolationsIdentified policySubject = new ScheduledPolicyViolationsIdentified(newProjectPolicyViolations); + // notificationProxy + // .content(NotificationUtil.generatePolicyScheduledNotificationContent( + // rule, + // policySubject.getNewPolicyViolationsTotal(), + // newProjectPolicyViolations.keySet().stream().toList(), + // rule.getLastExecutionTime())) + // .subject(policySubject); break; default: LOGGER.warn(group.name() + " is not a supported notification group for scheduled publishing"); diff --git a/src/main/java/org/dependencytrack/util/NotificationUtil.java b/src/main/java/org/dependencytrack/util/NotificationUtil.java index 6eaaa873b8..a426a5ec8a 100644 --- a/src/main/java/org/dependencytrack/util/NotificationUtil.java +++ b/src/main/java/org/dependencytrack/util/NotificationUtil.java @@ -44,6 +44,11 @@ import org.dependencytrack.model.ViolationAnalysisState; import org.dependencytrack.model.Vulnerability; import org.dependencytrack.model.VulnerabilityAnalysisLevel; +import org.dependencytrack.model.scheduled.DetailInfo; +import org.dependencytrack.model.scheduled.Details; +import org.dependencytrack.model.scheduled.Overview; +import org.dependencytrack.model.scheduled.Summary; +import org.dependencytrack.model.scheduled.SummaryInfo; import org.dependencytrack.notification.NotificationConstants; import org.dependencytrack.notification.NotificationGroup; import org.dependencytrack.notification.NotificationScope; @@ -68,6 +73,8 @@ import jakarta.json.JsonObject; import jakarta.json.JsonObjectBuilder; import javax.jdo.FetchPlan; +import javax.json.JsonValue; + import java.io.File; import java.io.IOException; import java.net.URLDecoder; @@ -571,64 +578,83 @@ public static JsonObject toJson(final Policy policy) { public static JsonObject toJson(final ScheduledNewVulnerabilitiesIdentified vo) { final JsonObjectBuilder builder = Json.createObjectBuilder(); + builder.add("overview", toJson(vo.getOverview())); + builder.add("summary", toJson(vo.getSummary())); + builder.add("details", toJson(vo.getDetails())); + return builder.build(); + } + + public static JsonObject toJson(final Overview overview) { + final JsonObjectBuilder builder = Json.createObjectBuilder(); + builder.add("affectedProjectsCount", overview.getAffectedProjectsCount()); + builder.add("newVulnerabilitiesCount", overview.getNewVulnerabilitiesCount()); + builder.add("affectedComponentsCount", overview.getAffectedComponentsCount()); + builder.add("suppressedNewVulnerabilitiesCount", overview.getSuppressedNewVulnerabilitiesCount()); + final JsonObjectBuilder newVulnerabilitiesBySeverityBuilder = Json.createObjectBuilder(); + for (final Map.Entry entry : overview.getNewVulnerabilitiesBySeverity().entrySet()) { + newVulnerabilitiesBySeverityBuilder.add(entry.getKey().name(), entry.getValue()); + } + builder.add("newVulnerabilitiesBySeverity", newVulnerabilitiesBySeverityBuilder.build()); + return builder.build(); + } + + public static JsonObject toJson(final Summary summary){ + final JsonObjectBuilder builder = Json.createObjectBuilder(); + final JsonObjectBuilder affectedProjectSummariesBuilder = Json.createObjectBuilder(); + for (final Map.Entry entry : summary.getAffectedProjectSummaries().entrySet()) { + affectedProjectSummariesBuilder.add("projectName", entry.getKey().getName()); + affectedProjectSummariesBuilder.add("projectVersion", entry.getKey().getVersion()); + affectedProjectSummariesBuilder.add("projectSummary", toJson(entry.getValue())); + } + builder.add("projectSummaries", affectedProjectSummariesBuilder.build()); + return builder.build(); + } + + private static JsonValue toJson(SummaryInfo info) { + final JsonObjectBuilder builder = Json.createObjectBuilder(); - if (vo.getNewProjectVulnerabilities() != null && vo.getNewProjectVulnerabilities().size() > 0) { - final JsonArrayBuilder projectsBuilder = Json.createArrayBuilder(); - for (final Map.Entry> entry : vo.getNewProjectVulnerabilities().entrySet()) { - final JsonObjectBuilder projectBuilder = Json.createObjectBuilder(); - projectBuilder.add("project", toJson(entry.getKey())); - final JsonArrayBuilder vulnsBuilder = Json.createArrayBuilder(); - for (final Vulnerability vulnerability : entry.getValue()) { - vulnsBuilder.add(toJson(vulnerability)); - } - projectBuilder.add("vulnerabilities", vulnsBuilder.build()); - projectsBuilder.add(projectBuilder.build()); - } - builder.add("newProjectVulnerabilities", projectsBuilder.build()); + final JsonObjectBuilder newVulnerabilitiesBySeverityBuilder = Json.createObjectBuilder(); + for (final Map.Entry entry : info.getNewVulnerabilitiesBySeverity().entrySet()) { + newVulnerabilitiesBySeverityBuilder.add(entry.getKey().name(), entry.getValue()); } - if(vo.getNewProjectVulnerabilitiesBySeverity() != null && vo.getNewProjectVulnerabilitiesBySeverity().size() > 0) { - final JsonArrayBuilder projectsBuilder = Json.createArrayBuilder(); - for (final Map.Entry>> entry : vo.getNewProjectVulnerabilitiesBySeverity().entrySet()) { - final JsonObjectBuilder projectBuilder = Json.createObjectBuilder(); - projectBuilder.add("project", toJson(entry.getKey())); - final JsonArrayBuilder vulnsBySeverityBuilder = Json.createArrayBuilder(); - for (final Map.Entry> vulnEntry : entry.getValue().entrySet()) { - final JsonObjectBuilder severityBuilder = Json.createObjectBuilder(); - severityBuilder.add("severity", vulnEntry.getKey().name()); - final JsonArrayBuilder vulnsBuilder = Json.createArrayBuilder(); - for (final Vulnerability vulnerability : vulnEntry.getValue()) { - vulnsBuilder.add(toJson(vulnerability)); - } - severityBuilder.add("vulnerabilities", vulnsBuilder.build()); - vulnsBySeverityBuilder.add(severityBuilder.build()); - } - projectBuilder.add("vulnerabilitiesBySeverity", vulnsBySeverityBuilder.build()); - projectsBuilder.add(projectBuilder.build()); - } - builder.add("newProjectVulnerabilitiesBySeverity", projectsBuilder.build()); + builder.add("newVulnerabilitiesBySeverity", newVulnerabilitiesBySeverityBuilder.build()); + + final JsonObjectBuilder totalProjectVulnerabilitiesBySeverityBuilder = Json.createObjectBuilder(); + for (final Map.Entry entry : info.getTotalProjectVulnerabilitiesBySeverity().entrySet()) { + totalProjectVulnerabilitiesBySeverityBuilder.add(entry.getKey().name(), entry.getValue()); } - if (vo.getNewVulnerabilitiesTotal() != null && vo.getNewVulnerabilitiesTotal().size() > 0) { - final JsonArrayBuilder vulnsBuilder = Json.createArrayBuilder(); - for (final Vulnerability vulnerability : vo.getNewVulnerabilitiesTotal()) { - vulnsBuilder.add(toJson(vulnerability)); - } - builder.add("newVulnerabilitiesTotal", vulnsBuilder.build()); - } - if(vo.getNewVulnerabilitiesTotalBySeverity() != null && vo.getNewVulnerabilitiesTotalBySeverity().size() > 0) { - final JsonArrayBuilder vulnsBySeverityBuilder = Json.createArrayBuilder(); - for (final Map.Entry> vulnEntry : vo.getNewVulnerabilitiesTotalBySeverity().entrySet()) { - final JsonObjectBuilder severityBuilder = Json.createObjectBuilder(); - severityBuilder.add("severity", vulnEntry.getKey().name()); - final JsonArrayBuilder vulnsBuilder = Json.createArrayBuilder(); - for (final Vulnerability vulnerability : vulnEntry.getValue()) { - vulnsBuilder.add(toJson(vulnerability)); - } - severityBuilder.add("vulnerabilities", vulnsBuilder.build()); - vulnsBySeverityBuilder.add(severityBuilder.build()); - } - builder.add("newVulnerabilitiesTotalBySeverity", vulnsBySeverityBuilder.build()); + builder.add("totalProjectVulnerabilitiesBySeverity", totalProjectVulnerabilitiesBySeverityBuilder.build()); + + final JsonObjectBuilder suppressedNewVulnerabilitiesBySeverityBuilder = Json.createObjectBuilder(); + for (final Map.Entry entry : info.getSuppressedNewVulnerabilitiesBySeverity().entrySet()) { + suppressedNewVulnerabilitiesBySeverityBuilder.add(entry.getKey().name(), entry.getValue()); } + builder.add("suppressedNewVulnerabilitiesBySeverity", suppressedNewVulnerabilitiesBySeverityBuilder.build()); + + return builder.build(); + } + private static JsonObject toJson(Details details) { + final JsonObjectBuilder builder = Json.createObjectBuilder(); + final JsonObjectBuilder affectedProjectFindingsBuilder = Json.createObjectBuilder(); + for (final Map.Entry> entry : details.getAffectedProjectFindings().entrySet()) { + affectedProjectFindingsBuilder.add("projectName", entry.getKey().getName()); + affectedProjectFindingsBuilder.add("projectVersion", entry.getKey().getVersion()); + final JsonObjectBuilder findingsBuilder = Json.createObjectBuilder(); + for (final DetailInfo detailInfo : entry.getValue()) { + findingsBuilder.add("componentName", detailInfo.getComponentName()); + findingsBuilder.add("componentVersion", detailInfo.getComponentVersion()); + findingsBuilder.add("componentGroup", detailInfo.getComponentGroup()); + findingsBuilder.add("vulnerabilityId", detailInfo.getVulnerabilityId()); + findingsBuilder.add("vulnerabilitySeverity", detailInfo.getVulnerabilitySeverity()); + findingsBuilder.add("analyzer", detailInfo.getAnalyzer()); + findingsBuilder.add("attributedOn", detailInfo.getAttributedOn() == null ? "---" : DateUtil.toISO8601(detailInfo.getAttributedOn())); + findingsBuilder.add("analysisState", detailInfo.getAnalysisState()); + findingsBuilder.add("suppressed", detailInfo.getSuppressed()); + } + affectedProjectFindingsBuilder.add("projectFindings", findingsBuilder.build()); + } + builder.add("projectDetails", affectedProjectFindingsBuilder.build()); return builder.build(); } diff --git a/src/test/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentifiedTest.java b/src/test/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentifiedTest.java index 438561be08..3a07be3a5d 100644 --- a/src/test/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentifiedTest.java +++ b/src/test/java/org/dependencytrack/notification/vo/ScheduledNewVulnerabilitiesIdentifiedTest.java @@ -53,28 +53,28 @@ public void testVo() { var project2Vulns = List.of(mediumVuln, lowVuln); projectVulnerabilitiesMap.put(project2, project2Vulns); - ScheduledNewVulnerabilitiesIdentified vo = new ScheduledNewVulnerabilitiesIdentified(projectVulnerabilitiesMap); + // ScheduledNewVulnerabilitiesIdentified vo = new ScheduledNewVulnerabilitiesIdentified(projectVulnerabilitiesMap); - Assert.assertEquals(2, vo.getNewProjectVulnerabilities().size()); - Assert.assertEquals(project1Vulns, vo.getNewProjectVulnerabilities().get(project1)); - Assert.assertEquals(project2Vulns, vo.getNewProjectVulnerabilities().get(project2)); - Assert.assertEquals(2, vo.getNewProjectVulnerabilitiesBySeverity().size()); - var projectVulnerabilitiesBySeverityMap = vo.getNewProjectVulnerabilitiesBySeverity(); - Assert.assertEquals(3, projectVulnerabilitiesBySeverityMap.get(project1).size()); - Assert.assertEquals(2, projectVulnerabilitiesBySeverityMap.get(project2).size()); - Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project1).get(Severity.CRITICAL).size()); - Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project1).get(Severity.HIGH).size()); - Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project1).get(Severity.INFO).size()); - Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project2).get(Severity.MEDIUM).size()); - Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project2).get(Severity.LOW).size()); - Assert.assertEquals(5, vo.getNewVulnerabilitiesTotal().size()); - Assert.assertEquals(List.of(critVuln, highVuln, infoVuln, mediumVuln, lowVuln), vo.getNewVulnerabilitiesTotal()); - Assert.assertEquals(5, vo.getNewVulnerabilitiesTotalBySeverity().size()); - var vulnerabilitiesBySeverity = vo.getNewVulnerabilitiesTotalBySeverity(); - Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.CRITICAL).size()); - Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.HIGH).size()); - Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.MEDIUM).size()); - Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.LOW).size()); - Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.INFO).size()); + // Assert.assertEquals(2, vo.getNewProjectVulnerabilities().size()); + // Assert.assertEquals(project1Vulns, vo.getNewProjectVulnerabilities().get(project1)); + // Assert.assertEquals(project2Vulns, vo.getNewProjectVulnerabilities().get(project2)); + // Assert.assertEquals(2, vo.getNewProjectVulnerabilitiesBySeverity().size()); + // var projectVulnerabilitiesBySeverityMap = vo.getNewProjectVulnerabilitiesBySeverity(); + // Assert.assertEquals(3, projectVulnerabilitiesBySeverityMap.get(project1).size()); + // Assert.assertEquals(2, projectVulnerabilitiesBySeverityMap.get(project2).size()); + // Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project1).get(Severity.CRITICAL).size()); + // Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project1).get(Severity.HIGH).size()); + // Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project1).get(Severity.INFO).size()); + // Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project2).get(Severity.MEDIUM).size()); + // Assert.assertEquals(1, projectVulnerabilitiesBySeverityMap.get(project2).get(Severity.LOW).size()); + // Assert.assertEquals(5, vo.getNewVulnerabilitiesTotal().size()); + // Assert.assertEquals(List.of(critVuln, highVuln, infoVuln, mediumVuln, lowVuln), vo.getNewVulnerabilitiesTotal()); + // Assert.assertEquals(5, vo.getNewVulnerabilitiesTotalBySeverity().size()); + // var vulnerabilitiesBySeverity = vo.getNewVulnerabilitiesTotalBySeverity(); + // Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.CRITICAL).size()); + // Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.HIGH).size()); + // Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.MEDIUM).size()); + // Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.LOW).size()); + // Assert.assertEquals(1, vulnerabilitiesBySeverity.get(Severity.INFO).size()); } } From 1477d3985b8623975218b2118cd13bec56cf2fbd Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 5 Jun 2024 18:05:43 +0200 Subject: [PATCH 303/429] fixed wrong query in getting findings with since-date-filter Signed-off-by: Max Schiller --- .../org/dependencytrack/persistence/FindingsQueryManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/persistence/FindingsQueryManager.java b/src/main/java/org/dependencytrack/persistence/FindingsQueryManager.java index 2abded25f7..5a59a599c8 100644 --- a/src/main/java/org/dependencytrack/persistence/FindingsQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/FindingsQueryManager.java @@ -274,7 +274,7 @@ public List getFindings(Project project) { @SuppressWarnings("unchecked") public List getFindings(Project project, boolean includeSuppressed, ZonedDateTime sinceAttributedOn) { final Query query; - if (sinceAttributedOn != null) { + if (sinceAttributedOn == null) { query = pm.newQuery(Query.SQL, Finding.QUERY); query.setParameters(project.getId()); } else { From 24d3ad6554bfce6262f029853c96e84915257d0b Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Wed, 5 Jun 2024 18:30:36 +0200 Subject: [PATCH 304/429] fixed typo in overview model Signed-off-by: Max Schiller --- src/main/java/org/dependencytrack/model/scheduled/Overview.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/model/scheduled/Overview.java b/src/main/java/org/dependencytrack/model/scheduled/Overview.java index f5a65ceaf2..94ec9858a2 100644 --- a/src/main/java/org/dependencytrack/model/scheduled/Overview.java +++ b/src/main/java/org/dependencytrack/model/scheduled/Overview.java @@ -51,7 +51,7 @@ public Overview(final List affectedProjects, ZonedDateTime lastExecutio componentCache.add(component); Vulnerability vulnerability = qm.getObjectByUuid(Vulnerability.class, (String) finding.getVulnerability().get("uuid")); - if (finding.getAnalysis().get("istSuppressed") instanceof Boolean suppressed) { + if (finding.getAnalysis().get("isSuppressed") instanceof Boolean suppressed) { if (suppressed) { suppressedVulnerabilityCache.add(vulnerability); } else { From ad013c7f03fe5dba1c0f8959a4921ddb32b00391 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Thu, 6 Jun 2024 11:17:53 +0200 Subject: [PATCH 305/429] fixed StackOverflowException due to missing method definition Signed-off-by: Max Schiller --- .../persistence/FindingsQueryManager.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dependencytrack/persistence/FindingsQueryManager.java b/src/main/java/org/dependencytrack/persistence/FindingsQueryManager.java index 5a59a599c8..ba98cd3b9f 100644 --- a/src/main/java/org/dependencytrack/persistence/FindingsQueryManager.java +++ b/src/main/java/org/dependencytrack/persistence/FindingsQueryManager.java @@ -259,11 +259,20 @@ void deleteAnalysisTrail(Project project) { * @param project the project to retrieve findings for * @return a List of Finding objects */ - @SuppressWarnings("unchecked") public List getFindings(Project project) { return getFindings(project, false, null); } + /** + * Returns a List of Finding objects for the specified project. + * @param project the project to retrieve findings for + * @param includeSuppressed determines if suppressed vulnerabilities should be included or not + * @return a List of Finding objects + */ + public List getFindings(Project project, boolean includeSuppressed) { + return getFindings(project, includeSuppressed, null); + } + /** * Returns a List of Finding objects for the specified project. * @param project the project to retrieve findings for From d5fbc2dd543cb27ba48519f605c34d032342ccee Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Thu, 6 Jun 2024 13:42:38 +0200 Subject: [PATCH 306/429] fixed ignore of suppressed violations, fixed error on pebble template population with null values in json, added important fields for template Signed-off-by: Max Schiller --- .../model/scheduled/DetailInfo.java | 62 ++-- .../model/scheduled/Details.java | 7 +- .../model/scheduled/Overview.java | 10 +- .../model/scheduled/Summary.java | 7 +- .../org/dependencytrack/util/JsonUtil.java | 14 + .../util/NotificationUtil.java | 43 +-- .../publisher/scheduled_email_summary.peb | 284 +++++++++++------- 7 files changed, 267 insertions(+), 160 deletions(-) diff --git a/src/main/java/org/dependencytrack/model/scheduled/DetailInfo.java b/src/main/java/org/dependencytrack/model/scheduled/DetailInfo.java index 159f23c62d..20abffec6e 100644 --- a/src/main/java/org/dependencytrack/model/scheduled/DetailInfo.java +++ b/src/main/java/org/dependencytrack/model/scheduled/DetailInfo.java @@ -18,41 +18,55 @@ */ package org.dependencytrack.model.scheduled; -import java.text.DateFormat; -import java.text.ParseException; import java.util.Date; import org.dependencytrack.model.Finding; +import org.dependencytrack.util.DateUtil; import alpine.common.logging.Logger; public class DetailInfo { private static final Logger LOGGER = Logger.getLogger(DetailInfo.class); + private final String componentUuid; private final String componentName; private final String componentVersion; private final String componentGroup; + private final String vulnerabilitySource; private final String vulnerabilityId; private final String vulnerabilitySeverity; private final String analyzer; - private Date attributedOn; + private final String attributionReferenceUrl; + private final String attributedOn; private final String analysisState; - private final Boolean suppressed; + private final String suppressed; public DetailInfo(Finding finding) { - this.componentName = (String) finding.getComponent().get("name"); - this.componentVersion = (String) finding.getComponent().get("version"); - this.componentGroup = (String) finding.getComponent().get("group"); - this.vulnerabilityId = (String) finding.getVulnerability().get("vulnId"); - this.vulnerabilitySeverity = (String) finding.getVulnerability().get("severity"); - this.analyzer = (String) finding.getAttribution().get("analyzerIdentity"); - try { - this.attributedOn = DateFormat.getDateTimeInstance().parse((String) finding.getAttribution().get("attributedOn")); - } catch (ParseException e) { - this.attributedOn = null; - LOGGER.error("An error occurred while parsing the attributedOn date for component" + this.componentName); - } - this.analysisState = (String) finding.getAnalysis().get("state"); - this.suppressed = (Boolean) finding.getAnalysis().get("isSuppressed"); + this.componentUuid = getValueOrUnknownIfNull(finding.getComponent().get("uuid")); + this.componentName = getValueOrUnknownIfNull(finding.getComponent().get("name")); + this.componentVersion = getValueOrUnknownIfNull(finding.getComponent().get("version")); + this.componentGroup = getValueOrUnknownIfNull(finding.getComponent().get("group")); + this.vulnerabilitySource = getValueOrUnknownIfNull(finding.getVulnerability().get("source")); + this.vulnerabilityId = getValueOrUnknownIfNull(finding.getVulnerability().get("vulnId")); + this.vulnerabilitySeverity = getValueOrUnknownIfNull(finding.getVulnerability().get("severity")); + this.analyzer = getValueOrUnknownIfNull(finding.getAttribution().get("analyzerIdentity")); + this.attributionReferenceUrl = getValueOrUnknownIfNull(finding.getAttribution().get("referenceUrl")); + this.attributedOn = getDateOrUnknownIfNull((Date) finding.getAttribution().get("attributedOn")); + this.analysisState = getValueOrUnknownIfNull(finding.getAnalysis().get("state")); + this.suppressed = finding.getAnalysis().get("isSuppressed") instanceof Boolean + ? (Boolean) finding.getAnalysis().get("isSuppressed") ? "Yes" : "No" + : "No"; + } + + private static String getValueOrUnknownIfNull(Object value) { + return value == null ? "" : value.toString(); + } + + private static String getDateOrUnknownIfNull(Date date) { + return date == null ? "Unknown" : DateUtil.toISO8601(date); + } + + public String getComponentUuid() { + return componentUuid; } public String getComponentName() { @@ -67,6 +81,10 @@ public String getComponentGroup() { return componentGroup; } + public String getVulnerabilitySource() { + return vulnerabilitySource; + } + public String getVulnerabilityId() { return vulnerabilityId; } @@ -79,7 +97,11 @@ public String getAnalyzer() { return analyzer; } - public Date getAttributedOn() { + public String getAttributionReferenceUrl() { + return attributionReferenceUrl; + } + + public String getAttributedOn() { return attributedOn; } @@ -87,7 +109,7 @@ public String getAnalysisState() { return analysisState; } - public Boolean getSuppressed() { + public String getSuppressed() { return suppressed; } } diff --git a/src/main/java/org/dependencytrack/model/scheduled/Details.java b/src/main/java/org/dependencytrack/model/scheduled/Details.java index 7ca7b228b6..f817b80706 100644 --- a/src/main/java/org/dependencytrack/model/scheduled/Details.java +++ b/src/main/java/org/dependencytrack/model/scheduled/Details.java @@ -20,20 +20,19 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.TreeMap; - import org.dependencytrack.model.Project; import org.dependencytrack.persistence.QueryManager; public final class Details { - private final Map> affectedProjectFindings = new TreeMap<>(); + private final Map> affectedProjectFindings = new LinkedHashMap<>(); public Details(final List affectedProjects, ZonedDateTime lastExecution) { try (var qm = new QueryManager()) { for (Project project : affectedProjects) { - var findings = qm.getFindingsSince(project, false, lastExecution.withZoneSameInstant(ZoneOffset.UTC)); + var findings = qm.getFindingsSince(project, true, lastExecution.withZoneSameInstant(ZoneOffset.UTC)); affectedProjectFindings.put(project, findings.stream().map(f -> new DetailInfo(f)).toList()); } } diff --git a/src/main/java/org/dependencytrack/model/scheduled/Overview.java b/src/main/java/org/dependencytrack/model/scheduled/Overview.java index 94ec9858a2..186dea1269 100644 --- a/src/main/java/org/dependencytrack/model/scheduled/Overview.java +++ b/src/main/java/org/dependencytrack/model/scheduled/Overview.java @@ -45,7 +45,7 @@ public Overview(final List affectedProjects, ZonedDateTime lastExecutio try (var qm = new QueryManager()) { for (Project project : affectedProjects) { - var findings = qm.getFindingsSince(project, false, lastExecution.withZoneSameInstant(ZoneOffset.UTC)); + var findings = qm.getFindingsSince(project, true, lastExecution.withZoneSameInstant(ZoneOffset.UTC)); for (Finding finding : findings) { Component component = qm.getObjectByUuid(Component.class, (String) finding.getComponent().get("uuid")); componentCache.add(component); @@ -56,13 +56,19 @@ public Overview(final List affectedProjects, ZonedDateTime lastExecutio suppressedVulnerabilityCache.add(vulnerability); } else { vulnerabilityCache.add(vulnerability); - newVulnerabilitiesBySeverity.merge(vulnerability.getSeverity(), 1, Integer::sum); } } } } } + for (Severity severity : Severity.values()) { + newVulnerabilitiesBySeverity.put(severity, 0); + } + for (Vulnerability vulnerability : vulnerabilityCache) { + newVulnerabilitiesBySeverity.merge(vulnerability.getSeverity(), 1, Integer::sum); + } + affectedProjectsCount = affectedProjects.size(); newVulnerabilitiesCount = vulnerabilityCache.size(); affectedComponentsCount = componentCache.size(); diff --git a/src/main/java/org/dependencytrack/model/scheduled/Summary.java b/src/main/java/org/dependencytrack/model/scheduled/Summary.java index 1e4fd64c80..4e38e476af 100644 --- a/src/main/java/org/dependencytrack/model/scheduled/Summary.java +++ b/src/main/java/org/dependencytrack/model/scheduled/Summary.java @@ -20,20 +20,19 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.TreeMap; - import org.dependencytrack.model.Project; import org.dependencytrack.persistence.QueryManager; public final class Summary { - private final Map affectedProjectSummaries = new TreeMap<>(); + private final Map affectedProjectSummaries = new LinkedHashMap<>(); public Summary(final List affectedProjects, ZonedDateTime lastExecution) { try (var qm = new QueryManager()) { for (Project project : affectedProjects) { - var findings = qm.getFindingsSince(project, false, lastExecution.withZoneSameInstant(ZoneOffset.UTC)); + var findings = qm.getFindingsSince(project, true, lastExecution.withZoneSameInstant(ZoneOffset.UTC)); affectedProjectSummaries.put(project, new SummaryInfo(findings)); } } diff --git a/src/main/java/org/dependencytrack/util/JsonUtil.java b/src/main/java/org/dependencytrack/util/JsonUtil.java index 5bf7e3c370..afaeade08d 100644 --- a/src/main/java/org/dependencytrack/util/JsonUtil.java +++ b/src/main/java/org/dependencytrack/util/JsonUtil.java @@ -38,6 +38,13 @@ public static JsonObjectBuilder add(final JsonObjectBuilder builder, final Strin return builder; } + public static JsonObjectBuilder add(final JsonObjectBuilder builder, final String key, final Integer value) { + if (value != null) { + builder.add(key, value); + } + return builder; + } + public static JsonObjectBuilder add(final JsonObjectBuilder builder, final String key, final BigInteger value) { if (value != null) { builder.add(key, value); @@ -51,6 +58,13 @@ public static JsonObjectBuilder add(final JsonObjectBuilder builder, final Strin } return builder; } + + public static JsonObjectBuilder add(final JsonObjectBuilder builder, final String key, final Boolean value) { + if (value != null) { + builder.add(key, value); + } + return builder; + } public static JsonObjectBuilder add(final JsonObjectBuilder builder, final String key, final Enum value) { if (value != null) { diff --git a/src/main/java/org/dependencytrack/util/NotificationUtil.java b/src/main/java/org/dependencytrack/util/NotificationUtil.java index a426a5ec8a..415276c1be 100644 --- a/src/main/java/org/dependencytrack/util/NotificationUtil.java +++ b/src/main/java/org/dependencytrack/util/NotificationUtil.java @@ -586,13 +586,13 @@ public static JsonObject toJson(final ScheduledNewVulnerabilitiesIdentified vo) public static JsonObject toJson(final Overview overview) { final JsonObjectBuilder builder = Json.createObjectBuilder(); - builder.add("affectedProjectsCount", overview.getAffectedProjectsCount()); - builder.add("newVulnerabilitiesCount", overview.getNewVulnerabilitiesCount()); - builder.add("affectedComponentsCount", overview.getAffectedComponentsCount()); - builder.add("suppressedNewVulnerabilitiesCount", overview.getSuppressedNewVulnerabilitiesCount()); + JsonUtil.add(builder, "affectedProjectsCount", overview.getAffectedProjectsCount()); + JsonUtil.add(builder, "newVulnerabilitiesCount", overview.getNewVulnerabilitiesCount()); + JsonUtil.add(builder, "affectedComponentsCount", overview.getAffectedComponentsCount()); + JsonUtil.add(builder, "suppressedNewVulnerabilitiesCount", overview.getSuppressedNewVulnerabilitiesCount()); final JsonObjectBuilder newVulnerabilitiesBySeverityBuilder = Json.createObjectBuilder(); for (final Map.Entry entry : overview.getNewVulnerabilitiesBySeverity().entrySet()) { - newVulnerabilitiesBySeverityBuilder.add(entry.getKey().name(), entry.getValue()); + JsonUtil.add(newVulnerabilitiesBySeverityBuilder, entry.getKey().name(), entry.getValue()); } builder.add("newVulnerabilitiesBySeverity", newVulnerabilitiesBySeverityBuilder.build()); return builder.build(); @@ -602,8 +602,7 @@ public static JsonObject toJson(final Summary summary){ final JsonObjectBuilder builder = Json.createObjectBuilder(); final JsonObjectBuilder affectedProjectSummariesBuilder = Json.createObjectBuilder(); for (final Map.Entry entry : summary.getAffectedProjectSummaries().entrySet()) { - affectedProjectSummariesBuilder.add("projectName", entry.getKey().getName()); - affectedProjectSummariesBuilder.add("projectVersion", entry.getKey().getVersion()); + affectedProjectSummariesBuilder.add("project", toJson(entry.getKey())); affectedProjectSummariesBuilder.add("projectSummary", toJson(entry.getValue())); } builder.add("projectSummaries", affectedProjectSummariesBuilder.build()); @@ -615,19 +614,19 @@ private static JsonValue toJson(SummaryInfo info) { final JsonObjectBuilder newVulnerabilitiesBySeverityBuilder = Json.createObjectBuilder(); for (final Map.Entry entry : info.getNewVulnerabilitiesBySeverity().entrySet()) { - newVulnerabilitiesBySeverityBuilder.add(entry.getKey().name(), entry.getValue()); + JsonUtil.add(newVulnerabilitiesBySeverityBuilder, entry.getKey().name(), entry.getValue()); } builder.add("newVulnerabilitiesBySeverity", newVulnerabilitiesBySeverityBuilder.build()); final JsonObjectBuilder totalProjectVulnerabilitiesBySeverityBuilder = Json.createObjectBuilder(); for (final Map.Entry entry : info.getTotalProjectVulnerabilitiesBySeverity().entrySet()) { - totalProjectVulnerabilitiesBySeverityBuilder.add(entry.getKey().name(), entry.getValue()); + JsonUtil.add(totalProjectVulnerabilitiesBySeverityBuilder, entry.getKey().name(), entry.getValue()); } builder.add("totalProjectVulnerabilitiesBySeverity", totalProjectVulnerabilitiesBySeverityBuilder.build()); final JsonObjectBuilder suppressedNewVulnerabilitiesBySeverityBuilder = Json.createObjectBuilder(); for (final Map.Entry entry : info.getSuppressedNewVulnerabilitiesBySeverity().entrySet()) { - suppressedNewVulnerabilitiesBySeverityBuilder.add(entry.getKey().name(), entry.getValue()); + JsonUtil.add(suppressedNewVulnerabilitiesBySeverityBuilder, entry.getKey().name(), entry.getValue()); } builder.add("suppressedNewVulnerabilitiesBySeverity", suppressedNewVulnerabilitiesBySeverityBuilder.build()); @@ -638,19 +637,21 @@ private static JsonObject toJson(Details details) { final JsonObjectBuilder builder = Json.createObjectBuilder(); final JsonObjectBuilder affectedProjectFindingsBuilder = Json.createObjectBuilder(); for (final Map.Entry> entry : details.getAffectedProjectFindings().entrySet()) { - affectedProjectFindingsBuilder.add("projectName", entry.getKey().getName()); - affectedProjectFindingsBuilder.add("projectVersion", entry.getKey().getVersion()); + affectedProjectFindingsBuilder.add("project", toJson(entry.getKey())); final JsonObjectBuilder findingsBuilder = Json.createObjectBuilder(); for (final DetailInfo detailInfo : entry.getValue()) { - findingsBuilder.add("componentName", detailInfo.getComponentName()); - findingsBuilder.add("componentVersion", detailInfo.getComponentVersion()); - findingsBuilder.add("componentGroup", detailInfo.getComponentGroup()); - findingsBuilder.add("vulnerabilityId", detailInfo.getVulnerabilityId()); - findingsBuilder.add("vulnerabilitySeverity", detailInfo.getVulnerabilitySeverity()); - findingsBuilder.add("analyzer", detailInfo.getAnalyzer()); - findingsBuilder.add("attributedOn", detailInfo.getAttributedOn() == null ? "---" : DateUtil.toISO8601(detailInfo.getAttributedOn())); - findingsBuilder.add("analysisState", detailInfo.getAnalysisState()); - findingsBuilder.add("suppressed", detailInfo.getSuppressed()); + JsonUtil.add(findingsBuilder, "componentUuid", detailInfo.getComponentUuid()); + JsonUtil.add(findingsBuilder, "componentName", detailInfo.getComponentName()); + JsonUtil.add(findingsBuilder, "componentVersion", detailInfo.getComponentVersion()); + JsonUtil.add(findingsBuilder, "componentGroup", detailInfo.getComponentGroup()); + JsonUtil.add(findingsBuilder, "vulnerabilitySource", detailInfo.getVulnerabilitySource()); + JsonUtil.add(findingsBuilder, "vulnerabilityId", detailInfo.getVulnerabilityId()); + JsonUtil.add(findingsBuilder, "vulnerabilitySeverity", detailInfo.getVulnerabilitySeverity()); + JsonUtil.add(findingsBuilder, "analyzer", detailInfo.getAnalyzer()); + JsonUtil.add(findingsBuilder, "attributionReferenceUrl", detailInfo.getAttributionReferenceUrl()); + JsonUtil.add(findingsBuilder, "attributedOn", detailInfo.getAttributedOn()); + JsonUtil.add(findingsBuilder, "analysisState", detailInfo.getAnalysisState()); + JsonUtil.add(findingsBuilder, "suppressed", detailInfo.getSuppressed()); } affectedProjectFindingsBuilder.add("projectFindings", findingsBuilder.build()); } diff --git a/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb b/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb index 4e03e83960..d22938bb28 100644 --- a/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb +++ b/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb @@ -1,125 +1,191 @@ - - {{ notification.title }} - ------------- - {{ notification.description }} - + +

          {{ notification.title }}

          +

          -------------

          +

          {{ notification.content }}

          +

          Overview

          - +
          +
          - - - - - - - - {% if subject.overview.newVulnerabilitiesBySeverity|length > 0 %}{% for entry in subject.overview.newVulnerabilitiesBySeverity %} - - - - {% endfor %}{% endif %} - - - - - - - - + + + + + + + + + {% if subject.overview.newVulnerabilitiesBySeverity|length > 0 %}{% + for entry in subject.overview.newVulnerabilitiesBySeverity %}{% if entry.value > 0 %} + + + + + {% endif %}{% endfor %}{% endif %} + + + + + + + + -
          Projects included in this rule{{ subject.overview.affectedProjectsCount }}
          Total new vulnerabilities{{ subject.overview.newVulnerabilitiesCount }}
          New vulnerabilities ({{ entry.key }}){{ entry.value }}
          Components affected by new vulnerabilities{{ subject.overview.affectedComponentsCount }}
          Suppressed new vulnerabilities (not included above){{ subject.overview.suppressedNewVulnerabilitiesCount }}
          Projects included in this rule{{ subject.overview.affectedProjectsCount }}
          Total new vulnerabilities{{ subject.overview.newVulnerabilitiesCount }}
          New vulnerabilities ({{ entry.key }}){{ entry.value }}
          Components affected by new vulnerabilities{{ subject.overview.affectedComponentsCount }}
          Suppressed new vulnerabilities (not included above){{ subject.overview.suppressedNewVulnerabilitiesCount }}
          + +

          Summary per project in this rule

          - - - - - - - - - - - {% for entry in subject.summary.affectedProjectSummaries %} - - - - - - - {% endfor %} - -
          Project NameVersionNew VulnerabilitiesAll VulnerabilitiesSuppressed Vulnerabilities
          - - {{ entry.key.name }} - - {{ entry.key.version }} - {% for newVulnEntry in entry.value.newVulnerabilitiesBySeverity %} - {{ newVulnEntry.key }}: {{ newVulnEntry.value }}
          - {% endfor %} -
          - {% for projVulnEntry in entry.value.totalProjectVulnerabilitiesBySeverity %} - {{ projVulnEntry.key }}: {{ projVulnEntry.value }}
          - {% endfor %} -
          - {% for supprVulnEntry in entry.value.suppressedNewVulnerabilitiesBySeverity %} - {{ supprVulnEntry.key }}: {{ supprVulnEntry.value }}
          - {% endfor %} -
          +
          + + + + + + + + + + + + {% for entry in subject.summary.affectedProjectSummaries %} + + + + + + + + {% endfor %} + +
          Project NameVersionNew VulnerabilitiesAll VulnerabilitiesSuppressed Vulnerabilities
          + + {{ entry.key.name }} + + {{ entry.key.version }} + {% for newVulnEntry in entry.value.newVulnerabilitiesBySeverity %} + {{ newVulnEntry.key }}: {{ newVulnEntry.value }}
          + {% endfor %} +
          + {% for projVulnEntry in + entry.value.totalProjectVulnerabilitiesBySeverity %} + {{ projVulnEntry.key }}: {{ projVulnEntry.value }}
          + {% endfor %} +
          + {% for supprVulnEntry in + entry.value.suppressedNewVulnerabilitiesBySeverity %} + {{ supprVulnEntry.key }}: {{ supprVulnEntry.value }}
          + {% endfor %} +
          +

          New vulnerabilities per project

          - {% if subject.details.affectedProjectFindings|length > 0 %}{% for affProjEntry in subject.details.affectedProjectFindings %} -

          Project "{{ affProjEntry.key.name }}"" [Version: {{ affProjEntry.key.version }}]

          - - - - - - - - - - - - - - - {% for finding in findings %} - - - - - - - - - - - {% endfor %} - -
          ComponentVersionGroupVulnerabilitySeverityAnalyzerAttributed OnAnalysisSuppressed
          - - {{ affProjEntry.value.componentName }} - - {{ affProjEntry.value.componentVersion }} - - {{ affProjEntry.value.componentGroup }} - - - - {{ affProjEntry.value.vulnerabilityId }} - - {{ affProjEntry.value.vulnerabilitySeverity }}{{ affProjEntry.value.analyzer }}{{ affProjEntry.value.attributedOn }}{{ affProjEntry.value.analysisState }}{{ affProjEntry.value.suppressed }}
          - {% endfor %}{% endif %} + {% if subject.details.affectedProjectFindings|length > 0 %}{% for + affProjEntry in subject.details.affectedProjectFindings %}{% if affProjEntry.value|length > 0 %} +

          + Project "{{ affProjEntry.key.name }}" [Version: + {{ affProjEntry.key.version }}] +

          +
          + + + + + + + + + + + + + + + + {% for vulnerableComponent in affProjEntry.value %} + + + + + + + + + + + + {% endfor %} + +
          ComponentVersionGroupVulnerabilitySeverityAnalyzerAttributed OnAnalysisSuppressed
          + {% if vulnerableComponent.componentUuid is empty %} + {{ vulnerableComponent.componentName }} + {% else %} + + {{ vulnerableComponent.componentName }} + + {% endif %} + + {{ vulnerableComponent.componentVersion }} + + {{ vulnerableComponent.componentGroup }} + + {% if vulnerableComponent.vulnerabilityId is empty %} + {{ vulnerableComponent.vulnerabilityId }} + {% else %} + + {{ vulnerableComponent.vulnerabilityId }} + + {% endif %} + + {{ vulnerableComponent.vulnerabilitySeverity }} + + {% if vulnerableComponent.attributionReferenceUrl is empty %} + {{ vulnerableComponent.analyzer }} + {% else %} + + {{ vulnerableComponent.analyzer }} + + {% endif %} + + {{ vulnerableComponent.attributedOn }} + + {{ vulnerableComponent.analysisState }} + + {{ vulnerableComponent.suppressed }} +
          +
          + {% endif %}{% endfor %}{% endif %}
          -

          - {{ timestamp }} -

          +

          Executed: {{ timestamp }}

          From 87f682ef39f801026ed4f97785ff99080a6fd4a5 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Thu, 6 Jun 2024 13:45:15 +0200 Subject: [PATCH 307/429] added child projects audit in scheduled notification mail Signed-off-by: Max Schiller --- .../tasks/SendScheduledNotificationTask.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index f907438579..7935182225 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -64,10 +64,20 @@ public void run() { LOGGER.info("Processing notification publishing for scheduled notification rule " + rule.getUuid()); for (NotificationGroup group : rule.getNotifyOn()) { - List affectedProjects = rule.getProjects(); + List affectedProjects = rule.getProjects() == null ? List.of() : rule.getProjects(); // if rule does not limit to specific projects, get all projects - if (affectedProjects.isEmpty()) - affectedProjects = qm.getProjects().getList(Project.class); + if (affectedProjects.isEmpty()) { + List allProjects = qm.getProjects().getList(Project.class); + affectedProjects.addAll(allProjects); + } + + // detach the projects to avoid issues while modifying the list + affectedProjects = qm.detach(affectedProjects); + + if (!affectedProjects.isEmpty() && rule.isNotifyChildren()) { + extendProjectListWithChildren(affectedProjects); + } + final Notification notificationProxy = new Notification() .scope(rule.getScope()) .group(group) @@ -161,5 +171,19 @@ public void run() { LOGGER.error("Errors occured while processing notification publishing for scheduled notification rule " + scheduledNotificationRuleUuid); } } + catch (Exception e) { + LOGGER.error("An error occurred while processing scheduled notification rule " + scheduledNotificationRuleUuid, e); + } + } + + private void extendProjectListWithChildren(final List affectedProjects) { + var allProjects = List.copyOf(affectedProjects); + for (Project project : allProjects) { + if (project == null || project.getChildren() == null || project.getChildren().isEmpty()) { + continue; + } + var parentIndex = affectedProjects.indexOf(project); + affectedProjects.addAll(++parentIndex, project.getChildren()); + } } } From 82c4faaf91a9d047a200470d9f9c5afddc99dfa5 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Thu, 6 Jun 2024 13:51:55 +0200 Subject: [PATCH 308/429] ignore version label in template if not set Signed-off-by: Max Schiller --- .../notification/publisher/scheduled_email_summary.peb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb b/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb index d22938bb28..dda04ffdf8 100644 --- a/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb +++ b/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb @@ -112,8 +112,8 @@ {% if subject.details.affectedProjectFindings|length > 0 %}{% for affProjEntry in subject.details.affectedProjectFindings %}{% if affProjEntry.value|length > 0 %}

          - Project "{{ affProjEntry.key.name }}" [Version: - {{ affProjEntry.key.version }}] + Project "{{ affProjEntry.key.name }}"{% if affProjEntry.key.version %} [Version: + {{ affProjEntry.key.version }}]{% endif %}

          From b570c7bb3f6ad563460c7b20b8960fbcee91a6f4 Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Thu, 6 Jun 2024 14:19:28 +0200 Subject: [PATCH 309/429] fixed detach in scheduled task to avoid implicit modification of notification rule project limitation Signed-off-by: Max Schiller --- .../tasks/SendScheduledNotificationTask.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index 7935182225..bacff14efc 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -64,16 +64,13 @@ public void run() { LOGGER.info("Processing notification publishing for scheduled notification rule " + rule.getUuid()); for (NotificationGroup group : rule.getNotifyOn()) { - List affectedProjects = rule.getProjects() == null ? List.of() : rule.getProjects(); + List affectedProjects = rule.getProjects() == null ? List.of() : qm.detach(rule.getProjects()); // if rule does not limit to specific projects, get all projects if (affectedProjects.isEmpty()) { List allProjects = qm.getProjects().getList(Project.class); - affectedProjects.addAll(allProjects); + affectedProjects.addAll(qm.detach(allProjects)); } - // detach the projects to avoid issues while modifying the list - affectedProjects = qm.detach(affectedProjects); - if (!affectedProjects.isEmpty() && rule.isNotifyChildren()) { extendProjectListWithChildren(affectedProjects); } From d6e56313dea4a5abb79d6f68760a921f066aeacd Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Fri, 7 Jun 2024 16:25:33 +0200 Subject: [PATCH 310/429] fixed determination of affected project in scheduled notification rule to consider limiting projects and children setting Signed-off-by: Max Schiller --- .../tasks/SendScheduledNotificationTask.java | 76 +++++++++++++------ 1 file changed, 54 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java index bacff14efc..f75b35ddfe 100644 --- a/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java +++ b/src/main/java/org/dependencytrack/tasks/SendScheduledNotificationTask.java @@ -20,7 +20,7 @@ import java.io.StringReader; import java.lang.reflect.InvocationTargetException; -import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.List; @@ -28,6 +28,7 @@ import javax.json.Json; import javax.json.JsonObject; import javax.json.JsonReader; +import java.util.stream.Collectors; import org.dependencytrack.exception.PublisherException; import org.dependencytrack.model.NotificationPublisher; @@ -39,7 +40,6 @@ import org.dependencytrack.notification.publisher.SendMailPublisher; import org.dependencytrack.notification.vo.ScheduledNewVulnerabilitiesIdentified; import org.dependencytrack.persistence.QueryManager; -import org.dependencytrack.util.NotificationUtil; import alpine.common.logging.Logger; import alpine.notification.Notification; @@ -62,35 +62,27 @@ public void run() { Boolean atLeastOneSuccessfulPublish = false; LOGGER.info("Processing notification publishing for scheduled notification rule " + rule.getUuid()); + final ZonedDateTime lastExecutionTime = ZonedDateTime.of(2024, 05, 16, 0, 0, 0, 0, ZoneOffset.UTC); // rule.getLastExecutionTime(); for (NotificationGroup group : rule.getNotifyOn()) { - List affectedProjects = rule.getProjects() == null ? List.of() : qm.detach(rule.getProjects()); - // if rule does not limit to specific projects, get all projects - if (affectedProjects.isEmpty()) { - List allProjects = qm.getProjects().getList(Project.class); - affectedProjects.addAll(qm.detach(allProjects)); - } - - if (!affectedProjects.isEmpty() && rule.isNotifyChildren()) { - extendProjectListWithChildren(affectedProjects); - } + List affectedProjects = List.of(); + affectedProjects = evaluateAffectedProjects(qm, rule); final Notification notificationProxy = new Notification() .scope(rule.getScope()) .group(group) - .title(NotificationUtil.generateNotificationTitle(group, affectedProjects)) .level(rule.getNotificationLevel()); switch (group) { case NEW_VULNERABILITY: - ScheduledNewVulnerabilitiesIdentified vulnSubject = new ScheduledNewVulnerabilitiesIdentified(affectedProjects, ZonedDateTime.of(2023, 05, 20, 0, 0, 0, 0, ZoneId.systemDefault())/* rule.getLastExecutionTime() */); + ScheduledNewVulnerabilitiesIdentified vulnSubject = new ScheduledNewVulnerabilitiesIdentified(affectedProjects, lastExecutionTime); if(vulnSubject.getOverview().getNewVulnerabilitiesCount() == 0 && rule.getPublishOnlyWithUpdates()) continue; notificationProxy - .title("[Dependency-Track] Scheduled Notification of Rule “" + rule.getName() + "”") + .title(vulnSubject.getOverview().getNewVulnerabilitiesCount() + " new Vulnerabilities in " + vulnSubject.getOverview().getAffectedComponentsCount() + " components in Scheduled Rule '" + rule.getName() + "'") .content("Find below a summary of new vulnerabilities since " - + rule.getLastExecutionTime().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) - + " in Scheduled Notification Rule “" + rule.getName() + "”.") + + lastExecutionTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) + + " in Scheduled Notification Rule '" + rule.getName() + "'.") .subject(vulnSubject); break; case POLICY_VIOLATION: @@ -173,14 +165,54 @@ public void run() { } } + private List evaluateAffectedProjects(QueryManager qm, ScheduledNotificationRule rule) { + List affectedProjects; + /* + * TODO: + * To workaround the inconsitent parent-child relationship in projects delivered + * by QueryManager.getAllProjects() (and some other multi-project methods), we + * need to retrieve them one by one by their UUIDs. This way it was empirically + * proven that the parent-child relationship is (more) consistent. + */ + if(rule.getProjects().isEmpty()){ + // if rule does not limit to specific projects, get all projects and their children, if configured + affectedProjects = qm.detach(qm.getAllProjects()) + .stream() + .filter(p -> rule.isNotifyChildren() ? true : p.getParent() == null) + .collect(Collectors.toList()); + } else { + // use projects defined in rule and with children if rule is set to notify children + affectedProjects = qm.detach(rule.getProjects()) + .stream() + .collect(Collectors.toList()); + if (rule.isNotifyChildren()) { + extendProjectListWithChildren(affectedProjects); + } + } + return affectedProjects; + } + private void extendProjectListWithChildren(final List affectedProjects) { var allProjects = List.copyOf(affectedProjects); - for (Project project : allProjects) { - if (project == null || project.getChildren() == null || project.getChildren().isEmpty()) { - continue; + try (var qm = new QueryManager()) { + for (Project project : allProjects) { + if (project == null || project.getChildren() == null || project.getChildren().isEmpty()) { + continue; + } + var parentIndex = affectedProjects.indexOf(project); + var childCounter = 0; + for (Project child : project.getChildren()) { + if (affectedProjects + .stream() + .filter(p -> p.getUuid().equals(child.getUuid())) + .findAny() + .isPresent()) { + continue; + } + childCounter++; + affectedProjects.add(parentIndex + childCounter, child); + } } - var parentIndex = affectedProjects.indexOf(project); - affectedProjects.addAll(++parentIndex, project.getChildren()); } } } From 1e449237705471e64f0605740ec9291201dcef6d Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Mon, 10 Jun 2024 10:42:33 +0200 Subject: [PATCH 311/429] updated console default publisher and template to support scheduled notifications Signed-off-by: Max Schiller --- .../DefaultNotificationPublishers.java | 2 +- .../publisher/scheduled_console.peb | 10 +++++ .../publisher/scheduled_email.peb | 44 ------------------- 3 files changed, 11 insertions(+), 45 deletions(-) create mode 100644 src/main/resources/templates/notification/publisher/scheduled_console.peb delete mode 100644 src/main/resources/templates/notification/publisher/scheduled_email.peb diff --git a/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java b/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java index 3df8c7b635..218a3a75c9 100644 --- a/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java +++ b/src/main/java/org/dependencytrack/notification/publisher/DefaultNotificationPublishers.java @@ -28,7 +28,7 @@ public enum DefaultNotificationPublishers { EMAIL("Email", "Sends notifications to an email address", SendMailPublisher.class, "/templates/notification/publisher/email.peb", MediaType.TEXT_PLAIN, true), SCHEDULED_EMAIL("Scheduled Email", "Sends summarized notifications to an email address in a defined schedule", SendMailPublisher.class, "/templates/notification/publisher/scheduled_email_summary.peb", MediaType.TEXT_HTML, true, true), CONSOLE("Console", "Displays notifications on the system console", ConsolePublisher.class, "/templates/notification/publisher/console.peb", MediaType.TEXT_PLAIN, true), - // SCHEDULED_CONSOLE("Scheduled Console", "Displays summarized notifications on the system console in a defined schedule", ConsolePublisher.class, "/templates/notification/publisher/scheduled_console.peb", MediaType.TEXT_PLAIN, true, true), + SCHEDULED_CONSOLE("Scheduled Console", "Displays summarized notifications on the system console in a defined schedule", ConsolePublisher.class, "/templates/notification/publisher/scheduled_console.peb", MediaType.TEXT_PLAIN, true, true), WEBHOOK("Outbound Webhook", "Publishes notifications to a configurable endpoint", WebhookPublisher.class, "/templates/notification/publisher/webhook.peb", MediaType.APPLICATION_JSON, true), CS_WEBEX("Cisco Webex", "Publishes notifications to a Cisco Webex Teams channel", CsWebexPublisher.class, "/templates/notification/publisher/cswebex.peb", MediaType.APPLICATION_JSON, true), JIRA("Jira", "Creates a Jira issue in a configurable Jira instance and queue", JiraPublisher.class, "/templates/notification/publisher/jira.peb", MediaType.APPLICATION_JSON, true); diff --git a/src/main/resources/templates/notification/publisher/scheduled_console.peb b/src/main/resources/templates/notification/publisher/scheduled_console.peb new file mode 100644 index 0000000000..a3ba5fcba5 --- /dev/null +++ b/src/main/resources/templates/notification/publisher/scheduled_console.peb @@ -0,0 +1,10 @@ +-------------------------------------------------------------------------------- +Notification + -- timestamp: {{ timestamp }} + -- level: {{ notification.level }} + -- scope: {{ notification.scope }} + -- group: {{ notification.group }} + -- title: {{ notification.title }}{% if subject.overview.newVulnerabilitiesCount > 0 %} + -- details:{% for entry in subject.summary.affectedProjectSummaries %}{% if entry.value.newVulnerabilitiesBySeverity|length > 0 %} + -- project: {{ entry.key.name }} {% if entry.key.version %}[{{ entry.key.version }}]{% endif %}{% for newVulnEntry in entry.value.newVulnerabilitiesBySeverity %}{% if newVulnEntry.value > 0 %} + -- {{ newVulnEntry.key }}: {{ newVulnEntry.value }}{% endif %}{% endfor %}{% endif %}{% endfor %}{% endif %} diff --git a/src/main/resources/templates/notification/publisher/scheduled_email.peb b/src/main/resources/templates/notification/publisher/scheduled_email.peb deleted file mode 100644 index b5c3df931a..0000000000 --- a/src/main/resources/templates/notification/publisher/scheduled_email.peb +++ /dev/null @@ -1,44 +0,0 @@ -{{ notification.title }} - -{{ notification.content }} - -=================================================================================================== - -{% if notification.group == "NEW_VULNERABILITY" %} -{% for entry in subject.newProjectVulnerabilitiesBySeverity %} -Project-Name: {{ entry.key.name }} -Project-URL: {{ baseUrl }}/projects/{{ entry.key.uuid }} -{% for severityEntry in entry.value %} - - {{ severityEntry.key }}: {{ severityEntry.value|length }} new vulnerabilities -{% endfor %} -{% endfor %} - ---------------------------------------------------------------------------------------------------- - -Details to all {{ subject.newVulnerabilitiesTotal|length }} new vulnerabilities: -{% for entry in subject.newProjectVulnerabilities %} -------------------------------------------------- -Project-Name: {{ entry.key.name }} -{% for vuln in entry.value %} - -ID: {{ vuln.vulnId }} -Severity: {{ vuln.severity }} -Description: {{ vuln.description }} -Source: {{ vuln.source }} -{% endfor %} -{% endfor %} -{% elseif notification.group == "POLICY_VIOLATION" %} - -New Policy Violations: {{ subject.newPolicyViolationsTotal|length }} -PolicyViolations: -{% for projectEntry in subject.newProjectPolicyViolations %} -{{ projectEntry.key.name }} ({{ projectEntry.key.uuid }}) -{% for policyEntry in projectEntry.value %} -- [{{ policyEntry.type }}] {{ policyEntry.timestamp }} {{ policyEntry.policyCondition }} -{% endfor %} -{% endfor %} -{% endif %} - -=================================================================================================== - -{{ timestamp }} \ No newline at end of file From 3b9e4e947f0b23b5b8ba3ec7967ccfda2627d9cb Mon Sep 17 00:00:00 2001 From: Max Schiller Date: Mon, 10 Jun 2024 15:29:11 +0200 Subject: [PATCH 312/429] hide details part in mail if no new vulnerabilities were found Signed-off-by: Max Schiller --- .../notification/publisher/scheduled_email_summary.peb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb b/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb index dda04ffdf8..9d7f4af0ab 100644 --- a/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb +++ b/src/main/resources/templates/notification/publisher/scheduled_email_summary.peb @@ -62,7 +62,7 @@
          - +

          Summary per project in this rule

          @@ -105,7 +105,7 @@ {% endfor %}
          -
          + {% if subject.overview.newVulnerabilitiesCount > 0 %}

          New vulnerabilities per project

          @@ -183,7 +183,7 @@ - {% endif %}{% endfor %}{% endif %} + {% endif %}{% endfor %}{% endif %}{% endif %}