diff --git a/examples_test.go b/examples_test.go index 701688e..3d03d4c 100644 --- a/examples_test.go +++ b/examples_test.go @@ -5,7 +5,7 @@ import ( "log" ) -// A scanner can be instanciated with options to set the arguments +// A scanner can be instantiated with options to set the arguments // that are given to nmap. func ExampleScanner_simple() { s, err := NewScanner( diff --git a/nmap.go b/nmap.go index 09324bc..e32e220 100644 --- a/nmap.go +++ b/nmap.go @@ -84,7 +84,7 @@ func (s *Scanner) Run() (*Run, error) { case <-s.ctx.Done(): // Context was done before the scan was finished. // The process is killed and a timeout error is returned. - cmd.Process.Kill() + _ = cmd.Process.Kill() return nil, ErrScanTimeout case <-done: diff --git a/nmap_test.go b/nmap_test.go index 4b044a7..1ead35e 100644 --- a/nmap_test.go +++ b/nmap_test.go @@ -17,7 +17,7 @@ import ( func TestNmapNotInstalled(t *testing.T) { oldPath := os.Getenv("PATH") - os.Setenv("PATH", "") + _ = os.Setenv("PATH", "") s, err := NewScanner() if err == nil { @@ -28,7 +28,7 @@ func TestNmapNotInstalled(t *testing.T) { t.Error("expected NewScanner to return a nil scanner if nmap is not found in $PATH") } - os.Setenv("PATH", oldPath) + _ = os.Setenv("PATH", oldPath) } func TestRun(t *testing.T) { @@ -225,6 +225,133 @@ func TestRun(t *testing.T) { } } +func TestRunAsync(t *testing.T) { + tests := []struct { + description string + + options []func(*Scanner) + + testTimeout bool + compareWholeRun bool + + expectedResult *Run + expectedRunAsyncErr error + expectedWaitErr bool + expectedParseErr error + expectedNmapErr string + }{ + { + description: "invalid binary path", + + options: []func(*Scanner){ + WithTargets("0.0.0.0"), + WithBinaryPath("/invalid"), + }, + + expectedResult: nil, + expectedRunAsyncErr: errors.New("unable to execute asynchronous nmap run: fork/exec /invalid: no such file or directory"), + }, + { + description: "output can't be parsed", + + options: []func(*Scanner){ + WithTargets("0.0.0.0"), + WithBinaryPath("echo"), + }, + + expectedResult: nil, + expectedParseErr: errors.New("EOF"), + }, + { + description: "context timeout", + + options: []func(*Scanner){ + WithTargets("0.0.0.0/16"), + }, + + testTimeout: true, + + expectedResult: nil, + expectedWaitErr: true, + }, + } + + 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. + } + + err = s.RunAsync() + assert.Equal(t, test.expectedRunAsyncErr, err) + if err != nil { + return + } + + stdout := s.GetStdout() + var content []byte + go func() { + for stdout.Scan() { + content = stdout.Bytes() + } + }() + + err = s.Wait() + if test.expectedWaitErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + if err != nil { + return + } + + result, err := Parse(content) + assert.Equal(t, test.expectedParseErr, err) + + if test.expectedNmapErr != "" { + assert.Contains(t, result.NmapErrors, test.expectedNmapErr) + } + + 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 test.expectedResult == nil { + return + } + + if test.compareWholeRun { + result.rawXML = nil + if !reflect.DeepEqual(test.expectedResult, result) { + t.Errorf("expected result to be %+v, got %+v", test.expectedResult, result) + } + } else { + if result.Args != test.expectedResult.Args { + t.Errorf("expected args %q got %q", test.expectedResult.Args, result.Args) + } + + if result.Scanner != test.expectedResult.Scanner { + t.Errorf("expected scanner %q got %q", test.expectedResult.Scanner, result.Scanner) + } + } + }) + } +} + func TestTargetSpecification(t *testing.T) { tests := []struct { description string