5849898283
Unit tests functional and coverage back to 100% Add more routes to dictionary, add more credentials, add default port 5554, rename cameradar logs ENV variable, improve unit test readability, remove tmp file
1782 lines
30 KiB
Go
1782 lines
30 KiB
Go
package nmap
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"os/exec"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// func TestNewScanner(t *testing.T) {
|
|
// tests := []struct {
|
|
// description string
|
|
|
|
// expectedErr error
|
|
// expectedScanner *Scanner
|
|
// }{
|
|
// {
|
|
// description: "valid targets, no errors",
|
|
|
|
// targets: []string{"80"},
|
|
|
|
// expectedScanner: &Scanner{
|
|
// args: []string{"80"},
|
|
// },
|
|
// },
|
|
// {
|
|
// description: "invalid targets (nil), error",
|
|
|
|
// targets: nil,
|
|
|
|
// expectedErr: ErrNoTargetsSpecified,
|
|
// },
|
|
// {
|
|
// description: "invalid targets (nil), error",
|
|
|
|
// targets: []string{},
|
|
|
|
// expectedErr: ErrNoTargetsSpecified,
|
|
// },
|
|
// }
|
|
|
|
// for _, test := range tests {
|
|
// t.Run(test.description, func(t *testing.T) {
|
|
// s, err := NewScanner(test.targets)
|
|
// if err != test.expectedErr {
|
|
// t.Errorf("expected error to be %v, got %v", test.expectedErr, err)
|
|
// }
|
|
|
|
// if test.expectedScanner == nil && s != nil {
|
|
// t.Errorf("expected scanner to be nil, got %+v", s)
|
|
// } else if test.expectedScanner != nil && s == nil {
|
|
// t.Errorf("expected scanner to be %+v, got nil", test.expectedScanner)
|
|
// } else if test.expectedScanner != nil && s != nil {
|
|
// if !reflect.DeepEqual(test.expectedScanner.args, s.args) {
|
|
// t.Errorf("expected scanner arguments to be %v, got %v", test.expectedScanner.args, s.args)
|
|
// }
|
|
// }
|
|
// })
|
|
// }
|
|
// }
|
|
|
|
func TestRun(t *testing.T) {
|
|
nmapPath, err := exec.LookPath("nmap")
|
|
if err != nil {
|
|
panic("nmap is required to run those tests")
|
|
}
|
|
|
|
tests := []struct {
|
|
description string
|
|
|
|
options []func(*Scanner)
|
|
|
|
testTimeout bool
|
|
|
|
expectedResult *Run
|
|
expectedErr error
|
|
}{
|
|
{
|
|
description: "invalid binary path",
|
|
|
|
options: []func(*Scanner){
|
|
WithTargets("0.0.0.0"),
|
|
WithBinaryPath("/invalid"),
|
|
},
|
|
|
|
expectedResult: nil,
|
|
expectedErr: errors.New("fork/exec /invalid: no such file or directory"),
|
|
},
|
|
{
|
|
description: "context timeout",
|
|
|
|
options: []func(*Scanner){
|
|
WithTargets("0.0.0.0/16"),
|
|
},
|
|
|
|
testTimeout: true,
|
|
|
|
expectedResult: nil,
|
|
expectedErr: ErrScanTimeout,
|
|
},
|
|
{
|
|
description: "scan localhost",
|
|
|
|
options: []func(*Scanner){
|
|
WithTargets("localhost"),
|
|
WithTimingTemplate(TimingFastest),
|
|
},
|
|
|
|
expectedResult: &Run{
|
|
Args: nmapPath + " -T5 -oX - localhost",
|
|
Scanner: "nmap",
|
|
},
|
|
},
|
|
{
|
|
description: "scan localhost with filters",
|
|
options: []func(*Scanner){
|
|
WithTargets("localhost"),
|
|
WithFilterHost(func(Host) bool {
|
|
return true
|
|
}),
|
|
WithFilterPort(func(Port) bool {
|
|
return true
|
|
}),
|
|
WithTimingTemplate(TimingFastest),
|
|
},
|
|
|
|
expectedResult: &Run{
|
|
Args: nmapPath + " -T5 -oX - localhost",
|
|
Scanner: "nmap",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.description, func(t *testing.T) {
|
|
if test.testTimeout {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 99*time.Hour)
|
|
test.options = append(test.options, WithContext(ctx))
|
|
|
|
go (func() {
|
|
// Cancel context to force timeout
|
|
defer cancel()
|
|
time.Sleep(1 * time.Millisecond)
|
|
})()
|
|
}
|
|
|
|
s, err := NewScanner(test.options...)
|
|
if err != nil {
|
|
panic(err) // this is never supposed to err, as we are testing run and not new.
|
|
}
|
|
|
|
result, err := s.Run()
|
|
if err != test.expectedErr {
|
|
if err.Error() != test.expectedErr.Error() {
|
|
t.Errorf("expected error %v got %v", test.expectedErr, err)
|
|
}
|
|
}
|
|
|
|
if result == nil && test.expectedResult == nil {
|
|
return
|
|
} else if result == nil && test.expectedResult != nil {
|
|
t.Error("expected non-nil result, got nil")
|
|
return
|
|
} else if result != nil && test.expectedResult == nil {
|
|
t.Error("expected nil result, got non-nil")
|
|
return
|
|
}
|
|
|
|
if result.Args != test.expectedResult.Args {
|
|
t.Errorf("expected args %s got %s", test.expectedResult.Args, result.Args)
|
|
}
|
|
|
|
if result.Scanner != test.expectedResult.Scanner {
|
|
t.Errorf("expected scanner %s got %s", test.expectedResult.Scanner, result.Scanner)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTargetSpecification(t *testing.T) {
|
|
tests := []struct {
|
|
description string
|
|
|
|
options []func(*Scanner)
|
|
|
|
expectedArgs []string
|
|
}{
|
|
{
|
|
description: "custom arguments",
|
|
|
|
options: []func(*Scanner){
|
|
WithTargets("0.0.0.0/24"),
|
|
WithCustomArguments("--invalid-argument"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"0.0.0.0/24",
|
|
"--invalid-argument",
|
|
},
|
|
},
|
|
{
|
|
description: "set target",
|
|
|
|
options: []func(*Scanner){
|
|
WithTargets("0.0.0.0/24"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"0.0.0.0/24",
|
|
},
|
|
},
|
|
{
|
|
description: "set multiple targets",
|
|
|
|
options: []func(*Scanner){
|
|
WithTargets("0.0.0.0", "192.168.1.1"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"0.0.0.0",
|
|
"192.168.1.1",
|
|
},
|
|
},
|
|
{
|
|
description: "set target from file",
|
|
|
|
options: []func(*Scanner){
|
|
WithTargetInput("/targets.txt"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-iL",
|
|
"/targets.txt",
|
|
},
|
|
},
|
|
{
|
|
description: "choose random targets",
|
|
|
|
options: []func(*Scanner){
|
|
WithRandomTargets(4),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-iR",
|
|
"4",
|
|
},
|
|
},
|
|
{
|
|
description: "target exclusion",
|
|
|
|
options: []func(*Scanner){
|
|
WithTargetExclusion("192.168.0.1,172.16.100.0/24"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--exclude",
|
|
"192.168.0.1,172.16.100.0/24",
|
|
},
|
|
},
|
|
{
|
|
description: "target exclusion from file",
|
|
|
|
options: []func(*Scanner){
|
|
WithTargetExclusionInput("/exclude_targets.txt"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--excludefile",
|
|
"/exclude_targets.txt",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.description, func(t *testing.T) {
|
|
s, err := NewScanner(test.options...)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(s.args, test.expectedArgs) {
|
|
t.Errorf("unexpected arguments, expected %s got %s", test.expectedArgs, s.args)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHostDiscovery(t *testing.T) {
|
|
tests := []struct {
|
|
description string
|
|
|
|
options []func(*Scanner)
|
|
|
|
expectedArgs []string
|
|
}{
|
|
{
|
|
description: "list targets to scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithListScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sL",
|
|
},
|
|
},
|
|
{
|
|
description: "ping scan - disable port scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithPingScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sn",
|
|
},
|
|
},
|
|
{
|
|
description: "skip host discovery",
|
|
|
|
options: []func(*Scanner){
|
|
WithSkipHostDiscovery(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-Pn",
|
|
},
|
|
},
|
|
{
|
|
description: "TCP SYN packets for all ports",
|
|
|
|
options: []func(*Scanner){
|
|
WithSYNDiscovery(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-PS",
|
|
},
|
|
},
|
|
{
|
|
description: "TCP SYN packets for specific ports",
|
|
|
|
options: []func(*Scanner){
|
|
WithSYNDiscovery("443", "8443"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-PS443,8443",
|
|
},
|
|
},
|
|
{
|
|
description: "TCP ACK packets for all ports",
|
|
|
|
options: []func(*Scanner){
|
|
WithACKDiscovery(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-PA",
|
|
},
|
|
},
|
|
{
|
|
description: "TCP ACK packets for specific ports",
|
|
|
|
options: []func(*Scanner){
|
|
WithACKDiscovery("443", "8443"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-PA443,8443",
|
|
},
|
|
},
|
|
{
|
|
description: "UDP packets for all ports",
|
|
|
|
options: []func(*Scanner){
|
|
WithUDPDiscovery(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-PU",
|
|
},
|
|
},
|
|
{
|
|
description: "UDP packets for specific ports",
|
|
|
|
options: []func(*Scanner){
|
|
WithUDPDiscovery("443", "8443"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-PU443,8443",
|
|
},
|
|
},
|
|
{
|
|
description: "SCTP packets for all ports",
|
|
|
|
options: []func(*Scanner){
|
|
WithSCTPDiscovery(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-PY",
|
|
},
|
|
},
|
|
{
|
|
description: "SCTP packets for specific ports",
|
|
|
|
options: []func(*Scanner){
|
|
WithSCTPDiscovery("443", "8443"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-PY443,8443",
|
|
},
|
|
},
|
|
{
|
|
description: "ICMP echo request discovery probes",
|
|
|
|
options: []func(*Scanner){
|
|
WithICMPEchoDiscovery(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-PE",
|
|
},
|
|
},
|
|
{
|
|
description: "ICMP Timestamp request discovery probes",
|
|
|
|
options: []func(*Scanner){
|
|
WithICMPTimestampDiscovery(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-PP",
|
|
},
|
|
},
|
|
{
|
|
description: "ICMP NetMask request discovery probes",
|
|
|
|
options: []func(*Scanner){
|
|
WithICMPNetMaskDiscovery(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-PM",
|
|
},
|
|
},
|
|
{
|
|
description: "IP protocol ping",
|
|
|
|
options: []func(*Scanner){
|
|
WithIPProtocolPingDiscovery("1", "2", "4"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-PO1,2,4",
|
|
},
|
|
},
|
|
{
|
|
description: "disable DNS resolution during discovery",
|
|
|
|
options: []func(*Scanner){
|
|
WithDisabledDNSResolution(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-n",
|
|
},
|
|
},
|
|
{
|
|
description: "enforce DNS resolution during discovery",
|
|
|
|
options: []func(*Scanner){
|
|
WithForcedDNSResolution(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-R",
|
|
},
|
|
},
|
|
{
|
|
description: "custom DNS server",
|
|
|
|
options: []func(*Scanner){
|
|
WithCustomDNSServers("8.8.8.8", "8.8.4.4"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--dns-servers",
|
|
"8.8.8.8,8.8.4.4",
|
|
},
|
|
},
|
|
{
|
|
description: "use system DNS",
|
|
|
|
options: []func(*Scanner){
|
|
WithSystemDNS(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--system-dns",
|
|
},
|
|
},
|
|
{
|
|
description: "traceroute",
|
|
|
|
options: []func(*Scanner){
|
|
WithTraceRoute(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--traceroute",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.description, func(t *testing.T) {
|
|
s, err := NewScanner(test.options...)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(s.args, test.expectedArgs) {
|
|
t.Errorf("unexpected arguments, expected %s got %s", test.expectedArgs, s.args)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestScanTechniques(t *testing.T) {
|
|
tests := []struct {
|
|
description string
|
|
|
|
options []func(*Scanner)
|
|
|
|
expectedArgs []string
|
|
}{
|
|
{
|
|
description: "TCP SYN scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithSYNScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sS",
|
|
},
|
|
},
|
|
{
|
|
description: "TCP Connect() scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithConnectScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sT",
|
|
},
|
|
},
|
|
{
|
|
description: "TCP ACK scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithACKScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sA",
|
|
},
|
|
},
|
|
{
|
|
description: "TCP Window scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithWindowScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sW",
|
|
},
|
|
},
|
|
{
|
|
description: "Maimon scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithMaimonScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sM",
|
|
},
|
|
},
|
|
{
|
|
description: "UDP scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithUDPScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sU",
|
|
},
|
|
},
|
|
{
|
|
description: "TCP Null scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithTCPNullScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sN",
|
|
},
|
|
},
|
|
{
|
|
description: "TCP FIN scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithTCPFINScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sF",
|
|
},
|
|
},
|
|
{
|
|
description: "TCP Xmas scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithTCPXmasScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sX",
|
|
},
|
|
},
|
|
{
|
|
description: "TCP custom scan flags",
|
|
|
|
options: []func(*Scanner){
|
|
WithTCPScanFlags(FlagACK, FlagFIN, FlagNULL),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--scanflags",
|
|
"11",
|
|
},
|
|
},
|
|
{
|
|
description: "idle scan through zombie host with probe port specified",
|
|
|
|
options: []func(*Scanner){
|
|
WithIdleScan("192.168.1.1", 61436),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sI",
|
|
"192.168.1.1:61436",
|
|
},
|
|
},
|
|
{
|
|
description: "idle scan through zombie host without probe port specified",
|
|
|
|
options: []func(*Scanner){
|
|
WithIdleScan("192.168.1.1", 0),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sI",
|
|
"192.168.1.1",
|
|
},
|
|
},
|
|
{
|
|
description: "SCTP INIT scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithSCTPInitScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sY",
|
|
},
|
|
},
|
|
{
|
|
description: "SCTP COOKIE-ECHO scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithSCTPCookieEchoScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sZ",
|
|
},
|
|
},
|
|
{
|
|
description: "IP protocol scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithIPProtocolScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sO",
|
|
},
|
|
},
|
|
{
|
|
description: "FTP bounce scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithFTPBounceScan("192.168.0.254"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-b",
|
|
"192.168.0.254",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.description, func(t *testing.T) {
|
|
s, err := NewScanner(test.options...)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(s.args, test.expectedArgs) {
|
|
t.Errorf("unexpected arguments, expected %s got %s", test.expectedArgs, s.args)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPortSpecAndScanOrder(t *testing.T) {
|
|
tests := []struct {
|
|
description string
|
|
|
|
options []func(*Scanner)
|
|
|
|
expectedPanic string
|
|
expectedArgs []string
|
|
}{
|
|
{
|
|
description: "specify ports to scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithPorts("554", "8554"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-p",
|
|
"554,8554",
|
|
},
|
|
},
|
|
{
|
|
description: "exclude ports to scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithPortExclusions("554", "8554"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--exclude-ports",
|
|
"554,8554",
|
|
},
|
|
},
|
|
{
|
|
description: "fast mode - scan fewer ports than the default scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithFastMode(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-F",
|
|
},
|
|
},
|
|
{
|
|
description: "consecutive port scanning",
|
|
|
|
options: []func(*Scanner){
|
|
WithConsecutivePortScanning(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-r",
|
|
},
|
|
},
|
|
{
|
|
description: "scan most commonly open ports",
|
|
|
|
options: []func(*Scanner){
|
|
WithMostCommonPorts(5),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--top-ports",
|
|
"5",
|
|
},
|
|
},
|
|
{
|
|
description: "scan most commonly open ports given a ratio - should be rounded to 0.4",
|
|
|
|
options: []func(*Scanner){
|
|
WithPortRatio(0.42010101),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--port-ratio",
|
|
"0.4",
|
|
},
|
|
},
|
|
{
|
|
description: "scan most commonly open ports given a ratio - should be invalid and panic",
|
|
|
|
options: []func(*Scanner){
|
|
WithPortRatio(2),
|
|
},
|
|
|
|
expectedPanic: "value given to nmap.WithPortRatio() should be between 0 and 1",
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.description, func(t *testing.T) {
|
|
if test.expectedPanic != "" {
|
|
defer func() {
|
|
recoveredMessage := recover()
|
|
|
|
if recoveredMessage != test.expectedPanic {
|
|
t.Errorf("expected panic message to be %q but got %q", test.expectedPanic, recoveredMessage)
|
|
}
|
|
}()
|
|
}
|
|
|
|
s, err := NewScanner(test.options...)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(s.args, test.expectedArgs) {
|
|
t.Errorf("unexpected arguments, expected %s got %s", test.expectedArgs, s.args)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServiceDetection(t *testing.T) {
|
|
tests := []struct {
|
|
description string
|
|
|
|
options []func(*Scanner)
|
|
|
|
expectedPanic string
|
|
expectedArgs []string
|
|
}{
|
|
{
|
|
description: "service detection",
|
|
|
|
options: []func(*Scanner){
|
|
WithServiceInfo(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sV",
|
|
},
|
|
},
|
|
{
|
|
description: "service detection custom intensity",
|
|
|
|
options: []func(*Scanner){
|
|
WithVersionIntensity(1),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--version-intensity",
|
|
"1",
|
|
},
|
|
},
|
|
{
|
|
description: "service detection custom intensity - should panic since not between 0 and 9",
|
|
|
|
options: []func(*Scanner){
|
|
WithVersionIntensity(42),
|
|
},
|
|
|
|
expectedPanic: "value given to nmap.WithVersionIntensity() should be between 0 and 9",
|
|
},
|
|
{
|
|
description: "service detection light intensity",
|
|
|
|
options: []func(*Scanner){
|
|
WithVersionLight(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--version-light",
|
|
},
|
|
},
|
|
{
|
|
description: "service detection highest intensity",
|
|
|
|
options: []func(*Scanner){
|
|
WithVersionAll(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--version-all",
|
|
},
|
|
},
|
|
{
|
|
description: "service detection enable trace",
|
|
|
|
options: []func(*Scanner){
|
|
WithVersionTrace(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--version-trace",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.description, func(t *testing.T) {
|
|
if test.expectedPanic != "" {
|
|
defer func() {
|
|
recoveredMessage := recover()
|
|
|
|
if recoveredMessage != test.expectedPanic {
|
|
t.Errorf("expected panic message to be %q but got %q", test.expectedPanic, recoveredMessage)
|
|
}
|
|
}()
|
|
}
|
|
|
|
s, err := NewScanner(test.options...)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(s.args, test.expectedArgs) {
|
|
t.Errorf("unexpected arguments, expected %s got %s", test.expectedArgs, s.args)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestScriptScan(t *testing.T) {
|
|
tests := []struct {
|
|
description string
|
|
|
|
targets []string
|
|
options []func(*Scanner)
|
|
unorderedArgs bool
|
|
|
|
expectedArgs []string
|
|
}{
|
|
{
|
|
description: "default script scan",
|
|
|
|
options: []func(*Scanner){
|
|
WithDefaultScript(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-sC",
|
|
},
|
|
},
|
|
{
|
|
description: "custom script list",
|
|
|
|
options: []func(*Scanner){
|
|
WithScripts("./scripts/", "/etc/nmap/nse/scripts"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--script=./scripts/,/etc/nmap/nse/scripts",
|
|
},
|
|
},
|
|
{
|
|
description: "script arguments",
|
|
|
|
options: []func(*Scanner){
|
|
WithScriptArguments(map[string]string{
|
|
"user": "foo",
|
|
"pass": "\",{}=bar\"",
|
|
"whois": "{whodb=nofollow+ripe}",
|
|
"xmpp-info.server_name": "localhost",
|
|
}),
|
|
},
|
|
|
|
unorderedArgs: true,
|
|
|
|
expectedArgs: []string{
|
|
"--script-args=",
|
|
"user=foo",
|
|
"pass=\",{}=bar\"",
|
|
"whois={whodb=nofollow+ripe}",
|
|
"xmpp-info.server_name=localhost",
|
|
},
|
|
},
|
|
{
|
|
description: "script arguments file",
|
|
|
|
options: []func(*Scanner){
|
|
WithScriptArgumentsFile("/script_args.txt"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--script-args-file=/script_args.txt",
|
|
},
|
|
},
|
|
{
|
|
description: "enable script trace",
|
|
|
|
options: []func(*Scanner){
|
|
WithScriptTrace(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--script-trace",
|
|
},
|
|
},
|
|
{
|
|
description: "update script database",
|
|
|
|
options: []func(*Scanner){
|
|
WithScriptUpdateDB(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--script-updatedb",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.description, func(t *testing.T) {
|
|
s, err := NewScanner(test.options...)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if test.unorderedArgs {
|
|
for _, expectedArg := range test.expectedArgs {
|
|
if !strings.Contains(s.args[0], expectedArg) {
|
|
t.Errorf("missing argument %s in %v", expectedArg, s.args)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
if !reflect.DeepEqual(s.args, test.expectedArgs) {
|
|
t.Errorf("unexpected arguments, expected %s got %s", test.expectedArgs, s.args)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOSDetection(t *testing.T) {
|
|
tests := []struct {
|
|
description string
|
|
|
|
options []func(*Scanner)
|
|
|
|
expectedArgs []string
|
|
}{
|
|
{
|
|
description: "enable OS detection",
|
|
|
|
options: []func(*Scanner){
|
|
WithOSDetection(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-O",
|
|
},
|
|
},
|
|
{
|
|
description: "enable OS scan limit",
|
|
|
|
options: []func(*Scanner){
|
|
WithOSScanLimit(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--osscan-limit",
|
|
},
|
|
},
|
|
{
|
|
description: "enable OS scan guess",
|
|
|
|
options: []func(*Scanner){
|
|
WithOSScanGuess(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--osscan-guess",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.description, func(t *testing.T) {
|
|
s, err := NewScanner(test.options...)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(s.args, test.expectedArgs) {
|
|
t.Errorf("unexpected arguments, expected %s got %s", test.expectedArgs, s.args)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTimingAndPerformance(t *testing.T) {
|
|
tests := []struct {
|
|
description string
|
|
|
|
options []func(*Scanner)
|
|
|
|
expectedArgs []string
|
|
}{
|
|
{
|
|
description: "set timing template",
|
|
|
|
options: []func(*Scanner){
|
|
WithTimingTemplate(TimingAggressive),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-T4",
|
|
},
|
|
},
|
|
{
|
|
description: "set min hostgroup",
|
|
|
|
options: []func(*Scanner){
|
|
WithMinHostgroup(42),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--min-hostgroup",
|
|
"42",
|
|
},
|
|
},
|
|
{
|
|
description: "set max hostgroup",
|
|
|
|
options: []func(*Scanner){
|
|
WithMaxHostgroup(42),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--max-hostgroup",
|
|
"42",
|
|
},
|
|
},
|
|
{
|
|
description: "set min parallelism",
|
|
|
|
options: []func(*Scanner){
|
|
WithMinParallelism(42),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--min-parallelism",
|
|
"42",
|
|
},
|
|
},
|
|
{
|
|
description: "set max parallelism",
|
|
|
|
options: []func(*Scanner){
|
|
WithMaxParallelism(42),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--max-parallelism",
|
|
"42",
|
|
},
|
|
},
|
|
{
|
|
description: "set min rtt-timeout",
|
|
|
|
options: []func(*Scanner){
|
|
WithMinRTTTimeout(2 * time.Minute),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--min-rtt-timeout",
|
|
"120000ms",
|
|
},
|
|
},
|
|
{
|
|
description: "set max rtt-timeout",
|
|
|
|
options: []func(*Scanner){
|
|
WithMaxRTTTimeout(8 * time.Hour),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--max-rtt-timeout",
|
|
"28800000ms",
|
|
},
|
|
},
|
|
{
|
|
description: "set initial rtt-timeout",
|
|
|
|
options: []func(*Scanner){
|
|
WithInitialRTTTimeout(8 * time.Hour),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--initial-rtt-timeout",
|
|
"28800000ms",
|
|
},
|
|
},
|
|
{
|
|
description: "set max retries",
|
|
|
|
options: []func(*Scanner){
|
|
WithMaxRetries(42),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--max-retries",
|
|
"42",
|
|
},
|
|
},
|
|
{
|
|
description: "set host timeout",
|
|
|
|
options: []func(*Scanner){
|
|
WithHostTimeout(42 * time.Second),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--host-timeout",
|
|
"42000ms",
|
|
},
|
|
},
|
|
{
|
|
description: "set scan delay",
|
|
|
|
options: []func(*Scanner){
|
|
WithScanDelay(42 * time.Millisecond),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--scan-delay",
|
|
"42ms",
|
|
},
|
|
},
|
|
{
|
|
description: "set max scan delay",
|
|
|
|
options: []func(*Scanner){
|
|
WithMaxScanDelay(42 * time.Millisecond),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--max-scan-delay",
|
|
"42ms",
|
|
},
|
|
},
|
|
{
|
|
description: "set min rate",
|
|
|
|
options: []func(*Scanner){
|
|
WithMinRate(42),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--min-rate",
|
|
"42",
|
|
},
|
|
},
|
|
{
|
|
description: "set max rate",
|
|
|
|
options: []func(*Scanner){
|
|
WithMaxRate(42),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--max-rate",
|
|
"42",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.description, func(t *testing.T) {
|
|
s, err := NewScanner(test.options...)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(s.args, test.expectedArgs) {
|
|
t.Errorf("unexpected arguments, expected %s got %s", test.expectedArgs, s.args)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFirewallAndIDSEvasionAndSpoofing(t *testing.T) {
|
|
tests := []struct {
|
|
description string
|
|
|
|
options []func(*Scanner)
|
|
|
|
expectedPanic string
|
|
expectedArgs []string
|
|
}{
|
|
{
|
|
description: "fragment packets",
|
|
|
|
options: []func(*Scanner){
|
|
WithFragmentPackets(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-f",
|
|
},
|
|
},
|
|
{
|
|
description: "custom fragment packet size",
|
|
|
|
options: []func(*Scanner){
|
|
WithMTU(42),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--mtu",
|
|
"42",
|
|
},
|
|
},
|
|
{
|
|
description: "enable decoys",
|
|
|
|
options: []func(*Scanner){
|
|
WithDecoys(
|
|
"192.168.1.1",
|
|
"192.168.1.2",
|
|
"192.168.1.3",
|
|
"192.168.1.4",
|
|
"192.168.1.5",
|
|
"192.168.1.6",
|
|
"ME",
|
|
"192.168.1.8",
|
|
),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-D",
|
|
"192.168.1.1,192.168.1.2,192.168.1.3,192.168.1.4,192.168.1.5,192.168.1.6,ME,192.168.1.8",
|
|
},
|
|
},
|
|
{
|
|
description: "spoof IP address",
|
|
|
|
options: []func(*Scanner){
|
|
WithSpoofIPAddress("192.168.1.1"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-S",
|
|
"192.168.1.1",
|
|
},
|
|
},
|
|
{
|
|
description: "set interface",
|
|
|
|
options: []func(*Scanner){
|
|
WithInterface("eth0"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-e",
|
|
"eth0",
|
|
},
|
|
},
|
|
{
|
|
description: "set source port",
|
|
|
|
options: []func(*Scanner){
|
|
WithSourcePort(4242),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--source-port",
|
|
"4242",
|
|
},
|
|
},
|
|
{
|
|
description: "set proxies",
|
|
|
|
options: []func(*Scanner){
|
|
WithProxies("4242", "8484"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--proxies",
|
|
"4242,8484",
|
|
},
|
|
},
|
|
{
|
|
description: "set custom hex payload",
|
|
|
|
options: []func(*Scanner){
|
|
WithHexData("0x8b6c42"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--data",
|
|
"0x8b6c42",
|
|
},
|
|
},
|
|
{
|
|
description: "set custom ascii payload",
|
|
|
|
options: []func(*Scanner){
|
|
WithASCIIData("pale brownish"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--data-string",
|
|
"pale brownish",
|
|
},
|
|
},
|
|
{
|
|
description: "set custom random payload length",
|
|
|
|
options: []func(*Scanner){
|
|
WithDataLength(42),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--data-length",
|
|
"42",
|
|
},
|
|
},
|
|
{
|
|
description: "set custom IP options",
|
|
|
|
options: []func(*Scanner){
|
|
WithIPOptions("S 192.168.1.1 10.0.0.3"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--ip-options",
|
|
"S 192.168.1.1 10.0.0.3",
|
|
},
|
|
},
|
|
{
|
|
description: "set custom TTL",
|
|
|
|
options: []func(*Scanner){
|
|
WithIPTimeToLive(254),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--ttl",
|
|
"254",
|
|
},
|
|
},
|
|
{
|
|
description: "set custom TTL - invalid value should panic",
|
|
|
|
options: []func(*Scanner){
|
|
WithIPTimeToLive(-254),
|
|
},
|
|
|
|
expectedPanic: "value given to nmap.WithIPTimeToLive() should be between 0 and 255",
|
|
},
|
|
{
|
|
description: "spoof mac address",
|
|
|
|
options: []func(*Scanner){
|
|
WithSpoofMAC("08:67:47:0A:78:E4"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--spoof-mac",
|
|
"08:67:47:0A:78:E4",
|
|
},
|
|
},
|
|
{
|
|
description: "send packets with bad checksum",
|
|
|
|
options: []func(*Scanner){
|
|
WithBadSum(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--badsum",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.description, func(t *testing.T) {
|
|
if test.expectedPanic != "" {
|
|
defer func() {
|
|
recoveredMessage := recover()
|
|
|
|
if recoveredMessage != test.expectedPanic {
|
|
t.Errorf("expected panic message to be %q but got %q", test.expectedPanic, recoveredMessage)
|
|
}
|
|
}()
|
|
}
|
|
|
|
s, err := NewScanner(test.options...)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(s.args, test.expectedArgs) {
|
|
t.Errorf("unexpected arguments, expected %s got %s", test.expectedArgs, s.args)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOutput(t *testing.T) {
|
|
tests := []struct {
|
|
description string
|
|
|
|
options []func(*Scanner)
|
|
|
|
expectedArgs []string
|
|
}{
|
|
{
|
|
description: "display reason",
|
|
|
|
options: []func(*Scanner){
|
|
WithReason(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--reason",
|
|
},
|
|
},
|
|
{
|
|
description: "show only open ports",
|
|
|
|
options: []func(*Scanner){
|
|
WithOpenOnly(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--open",
|
|
},
|
|
},
|
|
{
|
|
description: "enable packet trace",
|
|
|
|
options: []func(*Scanner){
|
|
WithPacketTrace(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--packet-trace",
|
|
},
|
|
},
|
|
{
|
|
description: "enable interface listing",
|
|
|
|
options: []func(*Scanner){
|
|
WithInterfaceList(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--iflist",
|
|
},
|
|
},
|
|
{
|
|
description: "enable interface listing",
|
|
|
|
options: []func(*Scanner){
|
|
WithInterfaceList(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--iflist",
|
|
},
|
|
},
|
|
{
|
|
description: "enable appending output",
|
|
|
|
options: []func(*Scanner){
|
|
WithAppendOutput(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--append-output",
|
|
},
|
|
},
|
|
{
|
|
description: "resume scan from file",
|
|
|
|
options: []func(*Scanner){
|
|
WithResumePreviousScan("/nmap_scan.xml"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--resume",
|
|
"/nmap_scan.xml",
|
|
},
|
|
},
|
|
{
|
|
description: "use stylesheet from file",
|
|
|
|
options: []func(*Scanner){
|
|
WithStylesheet("/nmap_stylesheet.xsl"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--stylesheet",
|
|
"/nmap_stylesheet.xsl",
|
|
},
|
|
},
|
|
{
|
|
description: "use stylesheet from file",
|
|
|
|
options: []func(*Scanner){
|
|
WithStylesheet("/nmap_stylesheet.xsl"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--stylesheet",
|
|
"/nmap_stylesheet.xsl",
|
|
},
|
|
},
|
|
{
|
|
description: "use default nmap stylesheet",
|
|
|
|
options: []func(*Scanner){
|
|
WithWebXML(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--webxml",
|
|
},
|
|
},
|
|
{
|
|
description: "disable stylesheets",
|
|
|
|
options: []func(*Scanner){
|
|
WithNoStylesheet(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--no-stylesheet",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.description, func(t *testing.T) {
|
|
s, err := NewScanner(test.options...)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(s.args, test.expectedArgs) {
|
|
t.Errorf("unexpected arguments, expected %s got %s", test.expectedArgs, s.args)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMiscellaneous(t *testing.T) {
|
|
tests := []struct {
|
|
description string
|
|
|
|
options []func(*Scanner)
|
|
|
|
expectedArgs []string
|
|
}{
|
|
{
|
|
description: "enable ipv6 scanning",
|
|
|
|
options: []func(*Scanner){
|
|
WithIPv6Scanning(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-6",
|
|
},
|
|
},
|
|
{
|
|
description: "enable aggressive scanning",
|
|
|
|
options: []func(*Scanner){
|
|
WithAggressiveScan(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"-A",
|
|
},
|
|
},
|
|
{
|
|
description: "set data dir",
|
|
|
|
options: []func(*Scanner){
|
|
WithDataDir("/etc/nmap/data"),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--datadir",
|
|
"/etc/nmap/data",
|
|
},
|
|
},
|
|
{
|
|
description: "send packets over ethernet",
|
|
|
|
options: []func(*Scanner){
|
|
WithSendEthernet(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--send-eth",
|
|
},
|
|
},
|
|
{
|
|
description: "send packets over IP",
|
|
|
|
options: []func(*Scanner){
|
|
WithSendIP(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--send-ip",
|
|
},
|
|
},
|
|
{
|
|
description: "assume user is privileged",
|
|
|
|
options: []func(*Scanner){
|
|
WithPrivileged(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--privileged",
|
|
},
|
|
},
|
|
{
|
|
description: "assume user is unprivileged",
|
|
|
|
options: []func(*Scanner){
|
|
WithUnprivileged(),
|
|
},
|
|
|
|
expectedArgs: []string{
|
|
"--unprivileged",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.description, func(t *testing.T) {
|
|
s, err := NewScanner(test.options...)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(s.args, test.expectedArgs) {
|
|
t.Errorf("unexpected arguments, expected %s got %s", test.expectedArgs, s.args)
|
|
}
|
|
})
|
|
}
|
|
}
|