Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</parent>

<artifactId>scanner-core</artifactId>
<version>6.2.4-SNAPSHOT</version>
<version>6.2.3</version>

<name>Scanner Core</name>
<description>A generic scanner framework which can execute a set of probes and combine the results into a report.</description>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Scanner Core - A Modular Framework for Probe Definition, Execution, and Result Analysis.
*
* Copyright 2017-2023 Ruhr University Bochum, Paderborn University, Technology Innovation Institute, and Hackmanit GmbH
*
* Licensed under Apache License, Version 2.0
* http://www.apache.org/licenses/LICENSE-2.0.txt
*/
package de.rub.nds.scanner.core.execution;

import de.rub.nds.scanner.core.probe.ScannerProbe;
import de.rub.nds.scanner.core.report.ScanReport;

/**
* Callback interface for receiving probe execution progress updates. This interface allows external
* components to be notified when individual probes complete during a scan, enabling real-time
* progress monitoring and streaming of partial results.
*
* @param <ReportT> the type of scan report
* @param <StateT> the type of state object used by probes
*/
@FunctionalInterface
public interface ProbeProgressCallback<ReportT extends ScanReport, StateT> {

/**
* Called when a probe has completed execution and merged its results into the report.
*
* @param probe the probe that completed execution
* @param report the scan report with the probe's results merged in
* @param completedProbes the number of probes that have completed so far
* @param totalProbes the total number of probes scheduled for this scan
*/
void onProbeCompleted(
ScannerProbe<ReportT, StateT> probe,
ReportT report,
int completedProbes,
int totalProbes);

/**
* Creates a no-op callback that does nothing when probes complete. Useful as a default when no
* progress tracking is needed.
*
* @param <R> the type of scan report
* @param <S> the type of state object
* @return a callback that performs no operations
*/
static <R extends ScanReport, S> ProbeProgressCallback<R, S> noOp() {
return (probe, report, completedProbes, totalProbes) -> {
// No operation
};
}
}
17 changes: 17 additions & 0 deletions src/main/java/de/rub/nds/scanner/core/execution/Scanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ public abstract class Scanner<
private final List<AfterProbeT> afterList;
private final boolean fillProbeListsAtScanStart;

// Optional callback for probe progress updates
private ProbeProgressCallback<ReportT, StateT> progressCallback = ProbeProgressCallback.noOp();

/**
* Creates a new scanner instance.
*
Expand Down Expand Up @@ -131,6 +134,18 @@ protected List<Guideline> getGuidelines() {
return List.of();
}

/**
* Sets the progress callback to be invoked when probes complete during scanning. This allows
* external components to receive real-time updates about scan progress and partial results.
*
* @param progressCallback the callback to invoke on probe completion, or null to disable
* callbacks
*/
public void setProgressCallback(ProbeProgressCallback<ReportT, StateT> progressCallback) {
this.progressCallback =
progressCallback != null ? progressCallback : ProbeProgressCallback.noOp();
}

/**
* Performs the scan. It will take care of all the necessary steps to perform a scan, including
* filling the probe list by calling {@link #fillProbeLists}, checking the scan prerequisites by
Expand Down Expand Up @@ -166,6 +181,8 @@ public ReportT scan() {
scanJob,
executorConfig.getParallelProbes(),
"ScannerProbeExecutor " + report.getRemoteName())) {
// Set the progress callback on the executor
scanJobExecutor.setProgressCallback(progressCallback);
ProgressSpinner.startSpinnerTask("Executing:");
report.setScanStartTime(System.currentTimeMillis());
scanJobExecutor.execute(report);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ public class ThreadedScanJobExecutor<
private volatile int probeCount;
private final AtomicInteger finishedProbes = new AtomicInteger(0);

// Callback for probe progress updates (optional)
private ProbeProgressCallback<ReportT, StateT> progressCallback = ProbeProgressCallback.noOp();

/**
* Creates a new ThreadedScanJobExecutor with a custom thread pool.
*
Expand Down Expand Up @@ -103,6 +106,18 @@ public ThreadedScanJobExecutor(
this.futureResults = new LinkedList<>();
}

/**
* Sets the progress callback to be invoked when probes complete. This allows external
* components to receive real-time updates about scan progress.
*
* @param progressCallback the callback to invoke on probe completion, or null to disable
* callbacks
*/
public void setProgressCallback(ProbeProgressCallback<ReportT, StateT> progressCallback) {
this.progressCallback =
progressCallback != null ? progressCallback : ProbeProgressCallback.noOp();
}

/**
* Executes the scan job by running probes concurrently and populating the report with results.
* This method manages probe dependencies and ensures probes are executed in the correct order.
Expand Down Expand Up @@ -146,7 +161,8 @@ private void executeProbesTillNoneCanBeExecuted(ReportT report) throws Interrupt
try {
probeResult = result.get();
LOGGER.info(
"[{}/{}] {} probe executed",
"[{}] [{}/{}] {} probe executed",
report.getRemoteName(),
String.format("%2d", currentFinishedProbes),
String.format("%2d", probeCount),
probeResult.getType().getName());
Expand All @@ -157,6 +173,14 @@ private void executeProbesTillNoneCanBeExecuted(ReportT report) throws Interrupt
finishedFutures.add(result);
probeResult.merge(report);
report.markProbeAsExecuted(probeResult);

// Notify progress callback
try {
progressCallback.onProbeCompleted(
probeResult, report, currentFinishedProbes, probeCount);
} catch (Exception e) {
LOGGER.warn("Progress callback threw exception, continuing scan", e);
}
}
}
futureResults.removeAll(finishedFutures);
Expand Down