diff --git a/core/e2e_test.go b/core/e2e_test.go index 952c46e..9e1b137 100644 --- a/core/e2e_test.go +++ b/core/e2e_test.go @@ -263,6 +263,32 @@ func TestE2E_MultiTargetCheck(t *testing.T) { } } +func TestE2E_QuietSuppressesCheckOutput(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + w.WriteHeader(400) + fmt.Fprint(w, "bad request") + })) + defer server.Close() + + urlFile := writeTempFile(t, server.URL+"\n") + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + out, err := runSpray(t, ctx, []string{ + "-l", urlFile, + "-q", + "--no-bar", + "--no-stat", + }) + if err != nil { + t.Fatalf("RunWithArgs: %v", err) + } + if strings.TrimSpace(out) != "" { + t.Fatalf("quiet check mode output = %q, want empty", out) + } +} + // --------------------------------------------------------------------------- // E2E: mixed status codes with valid findings // --------------------------------------------------------------------------- diff --git a/core/pool/pool.go b/core/pool/pool.go index 4f584c5..e1c15e8 100644 --- a/core/pool/pool.go +++ b/core/pool/pool.go @@ -118,6 +118,8 @@ func (pool *BasePool) putToFuzzy(bl *baseline.Baseline) { pool.Outwg.Add(1) bl.IsFuzzy = true out := bl.Snapshot() + out.IsValid = false + out.IsFuzzy = true select { case pool.FuzzyCh <- out: case <-pool.ctx.Done(): diff --git a/core/pool/pool_test.go b/core/pool/pool_test.go index 58b036a..3d2131d 100644 --- a/core/pool/pool_test.go +++ b/core/pool/pool_test.go @@ -559,6 +559,9 @@ func TestPutToFuzzy_Normal(t *testing.T) { if !bl.IsFuzzy { t.Fatal("IsFuzzy should be true") } + if bl.IsValid { + t.Fatal("fuzzy output snapshot should not stay valid") + } case <-time.After(time.Second): t.Fatal("putToFuzzy: receive timed out") } diff --git a/core/runner.go b/core/runner.go index 5bd7e96..f12b414 100644 --- a/core/runner.go +++ b/core/runner.go @@ -498,10 +498,13 @@ func (r *Runner) Output(bl *baseline.Baseline) { out = bl.String() } - if bl.IsValid { - logs.Log.Console(out + "\n") - } else if r.Fuzzy && bl.IsFuzzy { - logs.Log.Console("[fuzzy] " + out + "\n") + quiet := r.Option != nil && r.Option.Quiet + if !quiet { + if bl.IsValid { + logs.Log.Console(out + "\n") + } else if r.Fuzzy && bl.IsFuzzy { + logs.Log.Console("[fuzzy] " + out + "\n") + } } if r.OutputFile != nil { @@ -555,7 +558,9 @@ func (r *Runner) OutputHandler() { if !ok { return } - r.Output(bl) + if r.Fuzzy { + r.Output(bl) + } r.OutWg.Done() } } diff --git a/core/runner_test.go b/core/runner_test.go index 3688677..aa533d4 100644 --- a/core/runner_test.go +++ b/core/runner_test.go @@ -1,10 +1,14 @@ package core import ( + "bytes" "sync" "testing" "time" + "github.com/chainreactors/logs" + "github.com/chainreactors/parsers" + "github.com/chainreactors/spray/core/baseline" "github.com/chainreactors/spray/pkg" ) @@ -55,3 +59,43 @@ func TestRecordStat_NilStat(t *testing.T) { t.Fatalf("stats.Requests = %d, want 0 for nil stat", stats.Requests) } } + +func TestOutputHandlerSuppressesFuzzyChannelWithoutFuzzyFlag(t *testing.T) { + var out bytes.Buffer + oldLog := logs.Log + logs.Log = logs.NewLogger(oldLog.Level) + logs.Log.SetOutput(&out) + t.Cleanup(func() { + logs.Log = oldLog + }) + + r := &Runner{ + Option: &Option{}, + OutputCh: make(chan *baseline.Baseline, 1), + FuzzyCh: make(chan *baseline.Baseline, 1), + OutWg: &sync.WaitGroup{}, + } + r.OutputHandler() + + r.OutWg.Add(1) + r.FuzzyCh <- &baseline.Baseline{ + SprayResult: &parsers.SprayResult{ + UrlString: "http://example.com/fuzzy", + Status: 400, + BodyLength: 100, + IsValid: true, + IsFuzzy: true, + Reason: pkg.ErrFuzzyCompareFailed.Error(), + Source: parsers.CommonFileSource, + }, + } + mustFinishCore(t, 2*time.Second, "fuzzy output was not drained", func() { + r.OutWg.Wait() + }) + close(r.OutputCh) + close(r.FuzzyCh) + + if got := out.String(); got != "" { + t.Fatalf("fuzzy output without --fuzzy = %q, want empty", got) + } +}