Skip to content

Commit

Permalink
Nmap error handling (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
TerminalFi authored and Ullaakut committed Aug 26, 2019
1 parent 89ec1c8 commit fb38fc9
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 13 deletions.
16 changes: 16 additions & 0 deletions internal/slices/unique_string.go
@@ -0,0 +1,16 @@
package slices

// Adapted from
// https://siongui.github.io/2018/04/14/go-remove-duplicates-from-slice-or-array/
// RemoveDuplicatesFromStringSlice
func RemoveDuplicatesFromStringSlice(s []string) []string {
m := make(map[string]struct{})
var result []string
for _, item := range s {
if _, ok := m[item]; !ok {
m[item] = struct{}{}
result = append(result, item)
}
}
return result
}
10 changes: 8 additions & 2 deletions nmap.go
Expand Up @@ -4,11 +4,12 @@ package nmap
import (
"bytes"
"context"
"errors"
"fmt"
"os/exec"
"strings"
"time"

. "github.com/Ullaakut/nmap/internal/slices"
)

// ScanRunner represents something that can run a scan.
Expand Down Expand Up @@ -83,15 +84,20 @@ func (s *Scanner) Run() (*Run, error) {
return nil, ErrScanTimeout
case <-done:
// Scan finished before timeout.
var nmapErrors []string
if stderr.Len() > 0 {
return nil, errors.New(strings.Trim(stderr.String(), ".\n"))
// List all unique errors returned by nmap.
nmapErrors = strings.Split(strings.Trim(stderr.String(), "\n"), "\n")
nmapErrors = RemoveDuplicatesFromStringSlice(nmapErrors)
}

result, err := Parse(stdout.Bytes())
if err != nil {
return nil, fmt.Errorf("unable to parse nmap output: %v", err)
}

result.NmapErrors = nmapErrors

// Call filters if they are set.
if s.portFilter != nil {
result = choosePorts(result, s.portFilter)
Expand Down
19 changes: 18 additions & 1 deletion nmap_test.go
Expand Up @@ -10,6 +10,9 @@ import (
"strings"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestNmapNotInstalled(t *testing.T) {
Expand Down Expand Up @@ -44,6 +47,7 @@ func TestRun(t *testing.T) {

expectedResult *Run
expectedErr error
expectedNmapErr string
}{
{
description: "invalid binary path",
Expand Down Expand Up @@ -99,7 +103,11 @@ func TestRun(t *testing.T) {
WithTimingTemplate(TimingFastest),
},

expectedErr: errors.New("WARNING: No targets were specified, so 0 hosts scanned"),
expectedNmapErr: "WARNING: No targets were specified, so 0 hosts scanned.",
expectedResult: &Run{
Scanner: "nmap",
Args: "/usr/local/bin/nmap -T5 -oX -",
},
},
{
description: "scan localhost with filters",
Expand Down Expand Up @@ -174,12 +182,21 @@ func TestRun(t *testing.T) {
}

result, err := s.Run()

if err != test.expectedErr {
require.NotNil(t, err)

if err.Error() != test.expectedErr.Error() {
t.Errorf("expected error %q got %q", test.expectedErr, err)
}
}

if test.expectedNmapErr != "" {
require.NotNil(t, result)

assert.Contains(t, result.NmapErrors, test.expectedNmapErr)
}

if result == nil && test.expectedResult == nil {
return
} else if result == nil && test.expectedResult != nil {
Expand Down
5 changes: 3 additions & 2 deletions xml.go
Expand Up @@ -32,7 +32,8 @@ type Run struct {
TaskProgress []TaskProgress `xml:"taskprogress" json:"task_progress"`
TaskEnd []Task `xml:"taskend" json:"task_end"`

rawXML []byte
NmapErrors []string
rawXML []byte
}

// ToFile writes a Run as XML into the specified file path.
Expand Down Expand Up @@ -420,7 +421,7 @@ func (t *Timestamp) UnmarshalXMLAttr(attr xml.Attr) (err error) {
// Run struct.
func Parse(content []byte) (*Run, error) {
r := &Run{
rawXML: content,
rawXML: content,
}

err := xml.Unmarshal(content, r)
Expand Down
11 changes: 3 additions & 8 deletions xml_test.go
@@ -1,7 +1,6 @@
package nmap

import (
"bufio"
"bytes"
"encoding/json"
"encoding/xml"
Expand Down Expand Up @@ -173,12 +172,6 @@ func TestParseTableXML(t *testing.T) {
}
}

type mockWriter struct {
wr *bufio.Writer
writeErr error
flushErr error
}

func TestFormatTableXML(t *testing.T) {
table := Table{
Key: "key123",
Expand Down Expand Up @@ -1069,7 +1062,9 @@ func TestParseRunXML(t *testing.T) {
result, err := Parse(rawXML)

// Remove rawXML before comparing
result.rawXML = []byte{}
if result != nil {
result.rawXML = []byte{}
}

compareResults(t, test.expectedResult, result)

Expand Down

0 comments on commit fb38fc9

Please sign in to comment.