mirror of
https://github.com/lunchcat/sif.git
synced 2026-07-28 14:37:01 -07:00
* refactor(cli): extract per-target scan into scanTarget pull the target loop body into scanTarget, which returns a targetScan holding that target's findings, report rows, scan labels and log files instead of appending onto run-wide slices. the run loop merges those in target order and finishRun handles the post-loop notify/silent/report/ summary steps. behavior is identical for the sequential run today; the isolation is the seam a bounded worker pool needs next. * feat(cli): scan targets in parallel with -concurrency add -concurrency N (default 1) to run the per-target scan pool over several targets at once. scanTarget already returns isolated accumulators, so workers share only the console: output.SetConcurrent serializes sink writes and de-animates spinners/progress so parallel lines stay whole. results merge in input order, and concurrency 1 keeps the sequential path unchanged.
81 lines
3.1 KiB
Go
81 lines
3.1 KiB
Go
/*
|
|
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
|
: :
|
|
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
|
: ▄█ █ █▀ · BSD 3-Clause License :
|
|
: :
|
|
: (c) 2022-2026 vmfunc, xyzeva, :
|
|
: lunchcat alumni & contributors :
|
|
: :
|
|
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
|
*/
|
|
|
|
package sif
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/vmfunc/sif/internal/output"
|
|
)
|
|
|
|
// TestScanAllTargetsConcurrentIsolation runs the target pool at concurrency 3
|
|
// against three separate servers and asserts each target's result is isolated:
|
|
// exactly one scan recorded per target, results in input order. Run under -race
|
|
// it also backstops scanTarget's concurrency safety and the serialized sink.
|
|
func TestScanAllTargetsConcurrentIsolation(t *testing.T) {
|
|
defer output.SetConcurrent(false)
|
|
|
|
srvA := okServer()
|
|
defer srvA.Close()
|
|
srvB := okServer()
|
|
defer srvB.Close()
|
|
srvC := okServer()
|
|
defer srvC.Close()
|
|
|
|
app := headersOnlyApp()
|
|
app.targets = []string{srvA.URL, srvB.URL, srvC.URL}
|
|
app.settings.Concurrency = 3
|
|
|
|
results, err := app.scanAllTargets("", false)
|
|
if err != nil {
|
|
t.Fatalf("scanAllTargets: %v", err)
|
|
}
|
|
if len(results) != len(app.targets) {
|
|
t.Fatalf("got %d results, want %d", len(results), len(app.targets))
|
|
}
|
|
for i, ts := range results {
|
|
if len(ts.scansRun) != 1 || ts.scansRun[0] != "HTTP Headers" {
|
|
t.Errorf("target %d scansRun = %v, want exactly [HTTP Headers] (accumulator leaked across targets)", i, ts.scansRun)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestScanAllTargetsSequentialMatchesInputOrder pins that concurrency 1 keeps the
|
|
// plain sequential path: one result per target, in order, no pool engaged.
|
|
func TestScanAllTargetsSequentialMatchesInputOrder(t *testing.T) {
|
|
srvA := okServer()
|
|
defer srvA.Close()
|
|
srvB := okServer()
|
|
defer srvB.Close()
|
|
|
|
app := headersOnlyApp()
|
|
app.targets = []string{srvA.URL, srvB.URL}
|
|
app.settings.Concurrency = 1
|
|
|
|
results, err := app.scanAllTargets("", false)
|
|
if err != nil {
|
|
t.Fatalf("scanAllTargets: %v", err)
|
|
}
|
|
if len(results) != 2 {
|
|
t.Fatalf("got %d results, want 2", len(results))
|
|
}
|
|
for i, ts := range results {
|
|
if len(ts.scansRun) != 1 || ts.scansRun[0] != "HTTP Headers" {
|
|
t.Errorf("target %d scansRun = %v, want exactly [HTTP Headers]", i, ts.scansRun)
|
|
}
|
|
}
|
|
if output.Concurrent() {
|
|
t.Error("concurrency 1 must not switch output into concurrent mode")
|
|
}
|
|
}
|