diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index f83666b..fce5561 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -47,3 +47,5 @@ jobs: with: files: ./coverage.out fail_ci_if_error: false + - name: run integration tests + run: go test -tags=integration -race ./internal/scan/... diff --git a/docs/development.md b/docs/development.md index 0b3ef69..1777609 100644 --- a/docs/development.md +++ b/docs/development.md @@ -137,6 +137,15 @@ the module system is in `internal/modules/`: go test ./internal/... ``` +### integration tests + +run the scanners against a local testbed that plants the artifacts each one +should find (network-free, behind a build tag): + +```bash +go test -tags=integration ./internal/scan/... +``` + ### functional test ```bash diff --git a/internal/scan/dirlist.go b/internal/scan/dirlist.go index 10033f3..102c523 100644 --- a/internal/scan/dirlist.go +++ b/internal/scan/dirlist.go @@ -26,11 +26,13 @@ import ( "github.com/dropalldatabases/sif/internal/output" ) +// directoryURL is a var so integration tests can repoint it at a fixture. +var directoryURL = "https://raw.githubusercontent.com/dropalldatabases/sif-runtime/main/dirlist/" + const ( - directoryURL = "https://raw.githubusercontent.com/dropalldatabases/sif-runtime/main/dirlist/" - smallFile = "directory-list-2.3-small.txt" - mediumFile = "directory-list-2.3-medium.txt" - bigFile = "directory-list-2.3-big.txt" + smallFile = "directory-list-2.3-small.txt" + mediumFile = "directory-list-2.3-medium.txt" + bigFile = "directory-list-2.3-big.txt" ) type DirectoryResult struct { diff --git a/internal/scan/dnslist.go b/internal/scan/dnslist.go index 83321bd..c23f3e3 100644 --- a/internal/scan/dnslist.go +++ b/internal/scan/dnslist.go @@ -25,8 +25,10 @@ import ( "github.com/dropalldatabases/sif/internal/output" ) +// dnsURL is a var so integration tests can repoint it at a fixture. +var dnsURL = "https://raw.githubusercontent.com/dropalldatabases/sif-runtime/main/dnslist/" + const ( - dnsURL = "https://raw.githubusercontent.com/dropalldatabases/sif-runtime/main/dnslist/" dnsSmallFile = "subdomains-100.txt" dnsMediumFile = "subdomains-1000.txt" dnsBigFile = "subdomains-10000.txt" diff --git a/internal/scan/git.go b/internal/scan/git.go index bece147..0a4d3a7 100644 --- a/internal/scan/git.go +++ b/internal/scan/git.go @@ -26,10 +26,10 @@ import ( "github.com/dropalldatabases/sif/internal/output" ) -const ( - gitURL = "https://raw.githubusercontent.com/dropalldatabases/sif-runtime/main/git/" - gitFile = "git.txt" -) +// gitURL is a var so integration tests can repoint it at a fixture. +var gitURL = "https://raw.githubusercontent.com/dropalldatabases/sif-runtime/main/git/" + +const gitFile = "git.txt" func Git(url string, timeout time.Duration, threads int, logdir string) ([]string, error) { log := output.Module("GIT") diff --git a/internal/scan/integration_test.go b/internal/scan/integration_test.go new file mode 100644 index 0000000..5e09ca4 --- /dev/null +++ b/internal/scan/integration_test.go @@ -0,0 +1,219 @@ +//go:build integration + +/* +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +: : +: █▀ █ █▀▀ · Blazing-fast pentesting suite : +: ▄█ █ █▀ · BSD 3-Clause License : +: : +: (c) 2022-2026 vmfunc, xyzeva, : +: lunchcat alumni & contributors : +: : +·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━· +*/ + +// These tests run the real scanners against a local server standing in for a +// deliberately-vulnerable app, asserting the findings each one should produce. +// They're behind the `integration` build tag so the default `go test` stays +// network-free; run with `go test -tags=integration ./internal/scan/...`. +package scan + +import ( + "context" + "net" + "net/http" + "net/http/httptest" + "strconv" + "strings" + "testing" + "time" +) + +// newVulnApp serves the planted artifacts each scanner is meant to find, plus +// the wordlists the remote-list scanners fetch. +func newVulnApp() *httptest.Server { + mux := http.NewServeMux() + + // wordlists the remote-list scanners download + mux.HandleFunc("/git.txt", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(".git/HEAD\n.git/config\n")) + }) + mux.HandleFunc("/directory-list-2.3-small.txt", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("admin\nlogin\nnope\n")) + }) + + // an exposed git repo: HEAD is a real find, config is html so it's excluded + mux.HandleFunc("/.git/HEAD", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/octet-stream") + w.Write([]byte("ref: refs/heads/main\n")) + }) + mux.HandleFunc("/.git/config", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + w.Write([]byte("nope")) + }) + + // live directories for dirlist + mux.HandleFunc("/admin", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) + mux.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) + + // an exposed db admin panel for sql recon + mux.HandleFunc("/phpmyadmin/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("