mirror of
https://github.com/lunchcat/sif.git
synced 2026-06-12 19:11:25 -07:00
ef0408ee8d
sif can now slot into unix pipelines. stdin is drained for targets when it's a pipe (keyed off stdin's mode, not stdout), alongside -u/-f. naked hosts are accepted and default to https://; explicit http(s) is kept, other schemes rejected. -silent routes all banner/spinner/log chrome to stderr and prints one normalized finding per line to stdout via finding.Flatten, so `subfinder | sif -silent | notify` works.
114 lines
3.4 KiB
Go
114 lines
3.4 KiB
Go
/*
|
|
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
|
: :
|
|
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
|
|
: ▄█ █ █▀ · BSD 3-Clause License :
|
|
: :
|
|
: (c) 2022-2026 vmfunc, xyzeva, :
|
|
: lunchcat alumni & contributors :
|
|
: :
|
|
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
|
|
*/
|
|
|
|
package output
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// in silent mode chrome must land on stderr and leave stdout untouched, so a
|
|
// piped consumer downstream never sees a banner or log line.
|
|
func TestSetSilentRoutesChromeToStderr(t *testing.T) {
|
|
defer SetSilent(false)
|
|
|
|
outStr, errStr := captureStdoutStderr(t, func() {
|
|
// SetSilent reads os.Stderr at call time, so swap then set.
|
|
SetSilent(true)
|
|
Info("scanning %s", "example.com")
|
|
Success("done")
|
|
})
|
|
|
|
if outStr != "" {
|
|
t.Errorf("silent mode wrote chrome to stdout: %q", outStr)
|
|
}
|
|
if !strings.Contains(errStr, "scanning example.com") {
|
|
t.Errorf("silent chrome missing from stderr: %q", errStr)
|
|
}
|
|
}
|
|
|
|
// the default (non-silent) sink is stdout; flipping silent off must restore it.
|
|
func TestSetSilentOffRoutesChromeToStdout(t *testing.T) {
|
|
outStr, errStr := captureStdoutStderr(t, func() {
|
|
SetSilent(false)
|
|
Info("hello")
|
|
})
|
|
|
|
if !strings.Contains(outStr, "hello") {
|
|
t.Errorf("non-silent chrome missing from stdout: %q", outStr)
|
|
}
|
|
if strings.Contains(errStr, "hello") {
|
|
t.Errorf("non-silent chrome leaked to stderr: %q", errStr)
|
|
}
|
|
}
|
|
|
|
// Silent() reflects the toggle so callers can gate interactive widgets.
|
|
func TestSilentToggle(t *testing.T) {
|
|
defer SetSilent(false)
|
|
SetSilent(true)
|
|
if !Silent() {
|
|
t.Error("Silent() = false after SetSilent(true)")
|
|
}
|
|
SetSilent(false)
|
|
if Silent() {
|
|
t.Error("Silent() = true after SetSilent(false)")
|
|
}
|
|
}
|
|
|
|
// captureStdoutStderr swaps both real streams for pipes, runs fn, and returns
|
|
// what landed on each. SetSilent reads os.Stdout/os.Stderr at call time, so the
|
|
// swap has to happen before fn flips the sink - fn does that itself.
|
|
func captureStdoutStderr(t *testing.T, fn func()) (string, string) {
|
|
t.Helper()
|
|
|
|
outR, outW, err := os.Pipe()
|
|
if err != nil {
|
|
t.Fatalf("pipe stdout: %v", err)
|
|
}
|
|
errR, errW, err := os.Pipe()
|
|
if err != nil {
|
|
t.Fatalf("pipe stderr: %v", err)
|
|
}
|
|
|
|
savedOut, savedErr := os.Stdout, os.Stderr
|
|
os.Stdout, os.Stderr = outW, errW
|
|
|
|
outCh := drain(outR)
|
|
errCh := drain(errR)
|
|
|
|
fn()
|
|
|
|
os.Stdout, os.Stderr = savedOut, savedErr
|
|
outW.Close()
|
|
errW.Close()
|
|
return <-outCh, <-errCh
|
|
}
|
|
|
|
func drain(r *os.File) <-chan string {
|
|
ch := make(chan string, 1)
|
|
go func() {
|
|
buf := make([]byte, 0, 4096)
|
|
tmp := make([]byte, 1024)
|
|
for {
|
|
n, rerr := r.Read(tmp)
|
|
buf = append(buf, tmp[:n]...)
|
|
if rerr != nil {
|
|
break
|
|
}
|
|
}
|
|
ch <- string(buf)
|
|
}()
|
|
return ch
|
|
}
|