From 429baeaed63da0b9ab54641075dccc7f235edb2e Mon Sep 17 00:00:00 2001 From: leec94 Date: Wed, 17 Jul 2024 17:37:40 -0400 Subject: [PATCH 1/3] saving backend progress Signed-off-by: leec94 --- .../event/HistoricalRiskScoreUpdateEvent.java | 46 ++++++++++ .../org/dependencytrack/metrics/Metrics.java | 11 +++ .../persistence/StoredProcedures.java | 1 + .../resources/v1/MetricsResource.java | 18 ++++ .../HistoricalRiskScoreUpdateTask.java | 88 +++++++++++++++++++ ...rocedure_update_historical_risk_scores.sql | 14 +++ 6 files changed, 178 insertions(+) create mode 100644 src/main/java/org/dependencytrack/event/HistoricalRiskScoreUpdateEvent.java create mode 100644 src/main/java/org/dependencytrack/tasks/metrics/HistoricalRiskScoreUpdateTask.java create mode 100644 src/main/resources/migration/procedures/procedure_update_historical_risk_scores.sql diff --git a/src/main/java/org/dependencytrack/event/HistoricalRiskScoreUpdateEvent.java b/src/main/java/org/dependencytrack/event/HistoricalRiskScoreUpdateEvent.java new file mode 100644 index 000000000..3373ed246 --- /dev/null +++ b/src/main/java/org/dependencytrack/event/HistoricalRiskScoreUpdateEvent.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.event; + +import alpine.event.framework.Event; +import alpine.event.framework.SingletonCapableEvent; + +/** + * Defines an {@link Event} used to trigger historical risk score metrics updates. + * + * @since 5.0.0 + */ +public class HistoricalRiskScoreUpdateEvent extends SingletonCapableEvent { + + private final boolean weightHistoryEnabled; + + public HistoricalRiskScoreUpdateEvent() { + this(true); + } + + public HistoricalRiskScoreUpdateEvent(final boolean weightHistoryEnabled) { + this.setSingleton(true); + this.weightHistoryEnabled = weightHistoryEnabled; + } + + public boolean isWeightHistoryEnabled() { + return weightHistoryEnabled; + } + +} diff --git a/src/main/java/org/dependencytrack/metrics/Metrics.java b/src/main/java/org/dependencytrack/metrics/Metrics.java index 4fbff5527..940e028d4 100644 --- a/src/main/java/org/dependencytrack/metrics/Metrics.java +++ b/src/main/java/org/dependencytrack/metrics/Metrics.java @@ -63,6 +63,17 @@ public static void updatePortfolioMetrics() { StoredProcedures.execute(Procedure.UPDATE_PORTFOLIO_METRICS); } + + /** + * Update historical risk scores for the entire portfolio. + *

+ * + * @since 5.0.0 + */ + public static void updateHistoricalRiskScores() { + StoredProcedures.execute(Procedure.UPDATE_HISTORICAL_RISK_SCORES); + } + /** * Update metrics for a given {@link Project}. *

diff --git a/src/main/java/org/dependencytrack/persistence/StoredProcedures.java b/src/main/java/org/dependencytrack/persistence/StoredProcedures.java index dcacb5541..437ad1014 100644 --- a/src/main/java/org/dependencytrack/persistence/StoredProcedures.java +++ b/src/main/java/org/dependencytrack/persistence/StoredProcedures.java @@ -32,6 +32,7 @@ public final class StoredProcedures { public enum Procedure { + UPDATE_HISTORICAL_RISK_SCORES, UPDATE_COMPONENT_METRICS, UPDATE_PROJECT_METRICS, UPDATE_PORTFOLIO_METRICS; diff --git a/src/main/java/org/dependencytrack/resources/v1/MetricsResource.java b/src/main/java/org/dependencytrack/resources/v1/MetricsResource.java index 4c342772a..2fdf555cd 100644 --- a/src/main/java/org/dependencytrack/resources/v1/MetricsResource.java +++ b/src/main/java/org/dependencytrack/resources/v1/MetricsResource.java @@ -31,6 +31,7 @@ import org.dependencytrack.auth.Permissions; import org.dependencytrack.event.ComponentMetricsUpdateEvent; import org.dependencytrack.event.PortfolioMetricsUpdateEvent; +import org.dependencytrack.event.HistoricalRiskScoreUpdateEvent; import org.dependencytrack.event.ProjectMetricsUpdateEvent; import org.dependencytrack.model.Component; import org.dependencytrack.model.DependencyMetrics; @@ -81,6 +82,23 @@ public Response getVulnerabilityMetrics() { } } + @GET + @Path("/riskscore/refresh") + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Requests a refresh of the historical risk score metrics", + response = PortfolioMetrics.class, + notes = "

Requires permission PORTFOLIO_MANAGEMENT

" + ) + @ApiResponses(value = { + @ApiResponse(code = 401, message = "Unauthorized") + }) + @PermissionRequired(Permissions.Constants.PORTFOLIO_MANAGEMENT) + public Response RefreshHistoricalRiskScoreMetrics() { + Event.dispatch(new HistoricalRiskScoreUpdateEvent()); + return Response.ok().build(); + } + @GET @Path("/portfolio/current") @Produces(MediaType.APPLICATION_JSON) diff --git a/src/main/java/org/dependencytrack/tasks/metrics/HistoricalRiskScoreUpdateTask.java b/src/main/java/org/dependencytrack/tasks/metrics/HistoricalRiskScoreUpdateTask.java new file mode 100644 index 000000000..4fc13db8f --- /dev/null +++ b/src/main/java/org/dependencytrack/tasks/metrics/HistoricalRiskScoreUpdateTask.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.tasks.metrics; + +import alpine.common.logging.Logger; +import alpine.common.util.SystemUtil; +import alpine.event.framework.Event; +import alpine.event.framework.Subscriber; +import net.javacrumbs.shedlock.core.LockConfiguration; +import net.javacrumbs.shedlock.core.LockExtender; +import net.javacrumbs.shedlock.core.LockingTaskExecutor; +import org.apache.commons.collections4.ListUtils; +import org.dependencytrack.event.CallbackEvent; +import org.dependencytrack.event.HistoricalRiskScoreUpdateEvent; +import org.dependencytrack.event.ProjectMetricsUpdateEvent; +import org.dependencytrack.metrics.Metrics; +import org.dependencytrack.model.Project; +import org.dependencytrack.persistence.QueryManager; +import org.dependencytrack.util.LockProvider; + +import javax.jdo.PersistenceManager; +import javax.jdo.Query; +import java.time.Duration; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.dependencytrack.tasks.LockName.PORTFOLIO_METRICS_TASK_LOCK; +import static org.dependencytrack.util.LockProvider.isLockToBeExtended; + + +/** + * A {@link Subscriber} task that updates portfolio metrics. + * + * @since 4.6.0 + */ +public class HistoricalRiskScoreUpdateTask implements Subscriber { + + private static final Logger LOGGER = Logger.getLogger(PortfolioMetricsUpdateTask.class); + + @Override + public void inform(final Event e) { + if (e instanceof final HistoricalRiskScoreUpdateEvent event) { + try { + LockProvider.executeWithLock(PORTFOLIO_METRICS_TASK_LOCK, (LockingTaskExecutor.Task)() -> updateMetrics(event.isForceRefresh())); + } catch (Throwable ex) { + LOGGER.error("Error in acquiring lock and executing portfolio metrics task", ex); + } + } + } + + private void updateMetrics(final boolean weightHistoryEnabled) throws Exception { + LOGGER.info("Executing historical risk score metrics update"); + final long startTimeNs = System.nanoTime(); + + try { + if (weightHistoryEnabled) { + LOGGER.info("Refreshing historical risk score metrics"); + Metrics.updateHistoricalRiskScores(); + } + + Metrics.updatePortfolioMetrics(); + } finally { + LOGGER.info("Completed historical risk score metrics update in " + Duration.ofNanos(System.nanoTime() - startTimeNs)); + } + } + + public record ProjectProjection(long id, UUID uuid) { + } + +} diff --git a/src/main/resources/migration/procedures/procedure_update_historical_risk_scores.sql b/src/main/resources/migration/procedures/procedure_update_historical_risk_scores.sql new file mode 100644 index 000000000..60501613f --- /dev/null +++ b/src/main/resources/migration/procedures/procedure_update_historical_risk_scores.sql @@ -0,0 +1,14 @@ +CREATE OR REPLACE PROCEDURE "UPDATE_HISTORICAL_RISK_SCORES"() + LANGUAGE "plpgsql" +AS +$$ +DECLARE + "v_critical" INT; -- Number of vulnerabilities with critical severity + "v_high" INT; -- Number of vulnerabilities with high severity + "v_medium" INT; -- Number of vulnerabilities with medium severity + "v_low" INT; -- Number of vulnerabilities with low severity + "v_unassigned" INT; -- Number of vulnerabilities with unassigned severity + "v_risk_score" NUMERIC; -- Inherited risk score +BEGIN + UPDATE "DEPENDENCYMETRICS" SET "v_risk_score" = "CALC_RISK_SCORE"("v_critical", "v_high", "v_medium", "v_low", "v_unassigned"); +$$; \ No newline at end of file From 81b3a8d2bed82f0033c5819347dafb62c9464def Mon Sep 17 00:00:00 2001 From: leec94 Date: Fri, 19 Jul 2024 16:11:53 -0400 Subject: [PATCH 2/3] fix func name Signed-off-by: leec94 --- .../metrics/HistoricalRiskScoreUpdateTask.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/main/java/org/dependencytrack/tasks/metrics/HistoricalRiskScoreUpdateTask.java b/src/main/java/org/dependencytrack/tasks/metrics/HistoricalRiskScoreUpdateTask.java index 4fc13db8f..6c1d5e39d 100644 --- a/src/main/java/org/dependencytrack/tasks/metrics/HistoricalRiskScoreUpdateTask.java +++ b/src/main/java/org/dependencytrack/tasks/metrics/HistoricalRiskScoreUpdateTask.java @@ -19,31 +19,17 @@ package org.dependencytrack.tasks.metrics; import alpine.common.logging.Logger; -import alpine.common.util.SystemUtil; import alpine.event.framework.Event; import alpine.event.framework.Subscriber; -import net.javacrumbs.shedlock.core.LockConfiguration; -import net.javacrumbs.shedlock.core.LockExtender; import net.javacrumbs.shedlock.core.LockingTaskExecutor; -import org.apache.commons.collections4.ListUtils; -import org.dependencytrack.event.CallbackEvent; import org.dependencytrack.event.HistoricalRiskScoreUpdateEvent; -import org.dependencytrack.event.ProjectMetricsUpdateEvent; import org.dependencytrack.metrics.Metrics; -import org.dependencytrack.model.Project; -import org.dependencytrack.persistence.QueryManager; import org.dependencytrack.util.LockProvider; -import javax.jdo.PersistenceManager; -import javax.jdo.Query; import java.time.Duration; -import java.util.List; import java.util.UUID; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import static org.dependencytrack.tasks.LockName.PORTFOLIO_METRICS_TASK_LOCK; -import static org.dependencytrack.util.LockProvider.isLockToBeExtended; /** @@ -59,7 +45,7 @@ public class HistoricalRiskScoreUpdateTask implements Subscriber { public void inform(final Event e) { if (e instanceof final HistoricalRiskScoreUpdateEvent event) { try { - LockProvider.executeWithLock(PORTFOLIO_METRICS_TASK_LOCK, (LockingTaskExecutor.Task)() -> updateMetrics(event.isForceRefresh())); + LockProvider.executeWithLock(PORTFOLIO_METRICS_TASK_LOCK, (LockingTaskExecutor.Task)() -> updateMetrics(event.isWeightHistoryEnabled())); } catch (Throwable ex) { LOGGER.error("Error in acquiring lock and executing portfolio metrics task", ex); } From 1c066604538b760b81a75a7a5d1e0196464259dd Mon Sep 17 00:00:00 2001 From: leec94 Date: Fri, 19 Jul 2024 16:46:53 -0400 Subject: [PATCH 3/3] sql Signed-off-by: leec94 --- .../procedures/procedure_update_historical_risk_scores.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/migration/procedures/procedure_update_historical_risk_scores.sql b/src/main/resources/migration/procedures/procedure_update_historical_risk_scores.sql index 60501613f..0d1e275a1 100644 --- a/src/main/resources/migration/procedures/procedure_update_historical_risk_scores.sql +++ b/src/main/resources/migration/procedures/procedure_update_historical_risk_scores.sql @@ -11,4 +11,5 @@ DECLARE "v_risk_score" NUMERIC; -- Inherited risk score BEGIN UPDATE "DEPENDENCYMETRICS" SET "v_risk_score" = "CALC_RISK_SCORE"("v_critical", "v_high", "v_medium", "v_low", "v_unassigned"); +END; $$; \ No newline at end of file