mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
733 lines
19 KiB
Go
733 lines
19 KiB
Go
package fscan
|
|
|
|
import (
|
|
"encoding/json"
|
|
"math"
|
|
"testing"
|
|
|
|
"github.com/shadow1ng/fscan/common/output"
|
|
)
|
|
|
|
func TestResultHelpers(t *testing.T) {
|
|
result := Result{
|
|
Type: ResultTypeService,
|
|
Target: "127.0.0.1:8080",
|
|
Status: "identified",
|
|
Details: map[string]interface{}{
|
|
"port": float64(8080),
|
|
"service": "http",
|
|
"plugin": "webtitle",
|
|
"banner": "nginx",
|
|
"is_web": "true",
|
|
"protocol": "http",
|
|
},
|
|
}
|
|
|
|
if !result.IsService() || result.IsPort() {
|
|
t.Fatalf("unexpected type helpers for %q", result.Type)
|
|
}
|
|
if port, ok := result.Port(); !ok || port != 8080 {
|
|
t.Fatalf("Port = %d/%v, want 8080/true", port, ok)
|
|
}
|
|
if service, ok := result.Service(); !ok || service != "http" {
|
|
t.Fatalf("Service = %q/%v, want http/true", service, ok)
|
|
}
|
|
if plugin, ok := result.Plugin(); !ok || plugin != "webtitle" {
|
|
t.Fatalf("Plugin = %q/%v, want webtitle/true", plugin, ok)
|
|
}
|
|
if banner, ok := result.Banner(); !ok || banner != "nginx" {
|
|
t.Fatalf("Banner = %q/%v, want nginx/true", banner, ok)
|
|
}
|
|
if !result.IsWeb() {
|
|
t.Fatal("expected web result")
|
|
}
|
|
}
|
|
|
|
func TestResultPortFallback(t *testing.T) {
|
|
result := Result{Target: "[::1]:22"}
|
|
|
|
port, ok := result.Port()
|
|
if !ok || port != 22 {
|
|
t.Fatalf("Port = %d/%v, want 22/true", port, ok)
|
|
}
|
|
}
|
|
|
|
func TestResultPortDoesNotParseBareIPv6(t *testing.T) {
|
|
result := Result{Target: "2001:db8::1"}
|
|
|
|
if port, ok := result.Port(); ok {
|
|
t.Fatalf("Port = %d/true, want false", port)
|
|
}
|
|
}
|
|
|
|
func TestResultPortFromSimpleTarget(t *testing.T) {
|
|
result := Result{Target: "10.0.0.1:3306"}
|
|
|
|
port, ok := result.Port()
|
|
if !ok || port != 3306 {
|
|
t.Fatalf("Port = %d/%v, want 3306/true", port, ok)
|
|
}
|
|
}
|
|
|
|
func TestResultPortNoPort(t *testing.T) {
|
|
result := Result{Target: "10.0.0.1"}
|
|
if _, ok := result.Port(); ok {
|
|
t.Fatal("expected no port")
|
|
}
|
|
}
|
|
|
|
func TestResultPortRejectsOutOfRangePorts(t *testing.T) {
|
|
tests := []Result{
|
|
{Target: "10.0.0.1:70000"},
|
|
{Target: "[::1]:0"},
|
|
{Details: map[string]interface{}{"port": 70000}},
|
|
{Details: map[string]interface{}{"port": 0}},
|
|
}
|
|
|
|
for _, result := range tests {
|
|
if port, ok := result.Port(); ok {
|
|
t.Fatalf("Port(%#v) = %d/true, want false", result, port)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResultCredentialHelpers(t *testing.T) {
|
|
result := Result{
|
|
Type: ResultTypeVuln,
|
|
Target: "127.0.0.1:22",
|
|
Details: map[string]interface{}{
|
|
"type": "weak_credential",
|
|
"service": "ssh",
|
|
"username": "root",
|
|
"password": "toor",
|
|
},
|
|
}
|
|
|
|
if !result.IsVuln() {
|
|
t.Fatal("expected vuln result")
|
|
}
|
|
if !result.IsCredential() {
|
|
t.Fatal("expected credential result")
|
|
}
|
|
if username, ok := result.Username(); !ok || username != "root" {
|
|
t.Fatalf("Username = %q/%v, want root/true", username, ok)
|
|
}
|
|
if password, ok := result.Password(); !ok || password != "toor" {
|
|
t.Fatalf("Password = %q/%v, want toor/true", password, ok)
|
|
}
|
|
}
|
|
|
|
func TestResultCredentialViaStatusPrefix(t *testing.T) {
|
|
result := Result{
|
|
Type: ResultTypeVuln,
|
|
Target: "127.0.0.1:22",
|
|
Status: "weak_credential: root:pass",
|
|
}
|
|
if !result.IsCredential() {
|
|
t.Fatal("expected credential via status prefix")
|
|
}
|
|
}
|
|
|
|
func TestResultNotCredentialWithoutMarker(t *testing.T) {
|
|
result := Result{
|
|
Type: ResultTypeVuln,
|
|
Target: "127.0.0.1:445",
|
|
Status: "MS17-010",
|
|
Details: map[string]interface{}{
|
|
"vulnerability": "MS17-010",
|
|
},
|
|
}
|
|
if result.IsCredential() {
|
|
t.Fatal("vuln without credential marker should not be credential")
|
|
}
|
|
}
|
|
|
|
func TestTypedResultViews(t *testing.T) {
|
|
portResult, ok := (Result{
|
|
Type: ResultTypePort,
|
|
Target: "127.0.0.1",
|
|
Details: map[string]interface{}{"port": 22},
|
|
}).AsPort()
|
|
if !ok || portResult.Port != 22 || portResult.Target != "127.0.0.1" {
|
|
t.Fatalf("AsPort = %#v/%v, want port 22", portResult, ok)
|
|
}
|
|
|
|
serviceResult, ok := (Result{
|
|
Type: ResultTypeService,
|
|
Target: "127.0.0.1:80",
|
|
Details: map[string]interface{}{
|
|
"port": 80,
|
|
"service": "http",
|
|
"banner": "nginx",
|
|
"product": "nginx",
|
|
"version": "1.25",
|
|
"is_web": true,
|
|
"protocol": "http",
|
|
"url": "http://127.0.0.1:80",
|
|
},
|
|
}).AsService()
|
|
if !ok || serviceResult.Service != "http" || serviceResult.Port != 80 || !serviceResult.IsWeb {
|
|
t.Fatalf("AsService = %#v/%v, want http web service", serviceResult, ok)
|
|
}
|
|
|
|
credentialResult, ok := (Result{
|
|
Type: ResultTypeVuln,
|
|
Target: "127.0.0.1:22",
|
|
Details: map[string]interface{}{
|
|
"type": "weak_credential",
|
|
"service": "ssh",
|
|
"username": "root",
|
|
"password": "toor",
|
|
},
|
|
}).AsCredential()
|
|
if !ok || credentialResult.Username != "root" || credentialResult.Password != "toor" {
|
|
t.Fatalf("AsCredential = %#v/%v, want root/toor", credentialResult, ok)
|
|
}
|
|
|
|
vulnResult, ok := (Result{
|
|
Type: ResultTypeVuln,
|
|
Target: "127.0.0.1:25",
|
|
Details: map[string]interface{}{
|
|
"service": "smtp",
|
|
"vulnerability": "open relay",
|
|
},
|
|
}).AsVulnerability()
|
|
if !ok || vulnResult.Service != "smtp" || vulnResult.Vulnerability != "open relay" {
|
|
t.Fatalf("AsVulnerability = %#v/%v, want smtp/open relay", vulnResult, ok)
|
|
}
|
|
}
|
|
|
|
func TestAsPortNonPortResult(t *testing.T) {
|
|
_, ok := (Result{Type: ResultTypeHost, Target: "10.0.0.1"}).AsPort()
|
|
if ok {
|
|
t.Fatal("AsPort should return false for non-port result")
|
|
}
|
|
}
|
|
|
|
func TestAsPortNoPortValue(t *testing.T) {
|
|
_, ok := (Result{Type: ResultTypePort, Target: "10.0.0.1"}).AsPort()
|
|
if ok {
|
|
t.Fatal("AsPort should return false when no port available")
|
|
}
|
|
}
|
|
|
|
func TestAsServiceNonServiceResult(t *testing.T) {
|
|
_, ok := (Result{Type: ResultTypePort, Target: "10.0.0.1"}).AsService()
|
|
if ok {
|
|
t.Fatal("AsService should return false for non-service result")
|
|
}
|
|
}
|
|
|
|
func TestAsServiceNoUsefulFields(t *testing.T) {
|
|
_, ok := (Result{Type: ResultTypeService, Target: "10.0.0.1"}).AsService()
|
|
if ok {
|
|
t.Fatal("AsService should return false when no useful service fields")
|
|
}
|
|
}
|
|
|
|
func TestAsCredentialNonCredential(t *testing.T) {
|
|
_, ok := (Result{
|
|
Type: ResultTypeVuln,
|
|
Target: "10.0.0.1:445",
|
|
Details: map[string]interface{}{"vulnerability": "MS17-010"},
|
|
}).AsCredential()
|
|
if ok {
|
|
t.Fatal("AsCredential should return false for non-credential vuln")
|
|
}
|
|
}
|
|
|
|
func TestAsCredentialNoUsernamePassword(t *testing.T) {
|
|
_, ok := (Result{
|
|
Type: ResultTypeVuln,
|
|
Target: "10.0.0.1:22",
|
|
Status: "weak_credential: ???",
|
|
Details: map[string]interface{}{},
|
|
}).AsCredential()
|
|
if ok {
|
|
t.Fatal("AsCredential should return false without username/password")
|
|
}
|
|
}
|
|
|
|
func TestAsVulnerabilityCredentialExcluded(t *testing.T) {
|
|
_, ok := (Result{
|
|
Type: ResultTypeVuln,
|
|
Target: "10.0.0.1:22",
|
|
Details: map[string]interface{}{
|
|
"type": "weak_credential",
|
|
"vulnerability": "ssh weak password",
|
|
"username": "root",
|
|
"password": "toor",
|
|
},
|
|
}).AsVulnerability()
|
|
if ok {
|
|
t.Fatal("AsVulnerability should exclude credential results")
|
|
}
|
|
}
|
|
|
|
func TestAsVulnerabilityEmptyVulnField(t *testing.T) {
|
|
_, ok := (Result{
|
|
Type: ResultTypeVuln,
|
|
Target: "10.0.0.1:445",
|
|
Details: map[string]interface{}{},
|
|
}).AsVulnerability()
|
|
if ok {
|
|
t.Fatal("AsVulnerability should return false without vulnerability field")
|
|
}
|
|
}
|
|
|
|
func TestSummarizeResults(t *testing.T) {
|
|
results := []Result{
|
|
{Type: ResultTypeHost, Target: "127.0.0.1"},
|
|
{Type: ResultTypePort, Target: "127.0.0.1", Details: map[string]interface{}{"port": 80}},
|
|
{Type: ResultTypeService, Target: "127.0.0.1:80", Details: map[string]interface{}{"service": "http"}},
|
|
{Type: ResultTypeVuln, Target: "127.0.0.1:22", Status: "weak_credential: root:toor"},
|
|
}
|
|
|
|
summary := SummarizeResults(results)
|
|
if summary.Total != 4 {
|
|
t.Fatalf("Total = %d, want 4", summary.Total)
|
|
}
|
|
if summary.Hosts != 1 || summary.Ports != 1 || summary.Services != 1 || summary.Vulns != 1 {
|
|
t.Fatalf("summary categories = %#v, want one each", summary)
|
|
}
|
|
if summary.Web != 1 {
|
|
t.Fatalf("Web = %d, want 1", summary.Web)
|
|
}
|
|
if summary.Credentials != 1 {
|
|
t.Fatalf("Credentials = %d, want 1", summary.Credentials)
|
|
}
|
|
}
|
|
|
|
func TestSummarizeEmpty(t *testing.T) {
|
|
summary := SummarizeResults(nil)
|
|
if summary.Total != 0 {
|
|
t.Fatalf("Total = %d, want 0", summary.Total)
|
|
}
|
|
}
|
|
|
|
func TestResultDetailIntRejectsFraction(t *testing.T) {
|
|
result := Result{Details: map[string]interface{}{"port": 22.5}}
|
|
|
|
if port, ok := result.DetailInt("port"); ok {
|
|
t.Fatalf("DetailInt = %d/true, want false", port)
|
|
}
|
|
}
|
|
|
|
func TestResultDetailIntParsesJSONNumber(t *testing.T) {
|
|
result := Result{Details: map[string]interface{}{"port": json.Number("443")}}
|
|
|
|
port, ok := result.DetailInt("port")
|
|
if !ok || port != 443 {
|
|
t.Fatalf("DetailInt = %d/%v, want 443/true", port, ok)
|
|
}
|
|
}
|
|
|
|
func TestDetailIntTypes(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value interface{}
|
|
want int
|
|
ok bool
|
|
}{
|
|
{"int", int(42), 42, true},
|
|
{"int8", int8(8), 8, true},
|
|
{"int16", int16(16), 16, true},
|
|
{"int32", int32(32), 32, true},
|
|
{"int64", int64(64), 64, true},
|
|
{"uint", uint(10), 10, true},
|
|
{"uint8", uint8(8), 8, true},
|
|
{"uint16", uint16(16), 16, true},
|
|
{"uint32", uint32(32), 32, true},
|
|
{"uint64", uint64(64), 64, true},
|
|
{"float32", float32(80), 80, true},
|
|
{"float64", float64(443), 443, true},
|
|
{"string", "8080", 8080, true},
|
|
{"string-spaces", " 22 ", 22, true},
|
|
{"string-invalid", "abc", 0, false},
|
|
{"nil", nil, 0, false},
|
|
{"bool", true, 0, false},
|
|
{"missing", nil, 0, false},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{"v": tt.value}}
|
|
if tt.name == "missing" {
|
|
r = Result{Details: map[string]interface{}{}}
|
|
}
|
|
got, ok := r.DetailInt("v")
|
|
if ok != tt.ok || got != tt.want {
|
|
t.Fatalf("DetailInt = %d/%v, want %d/%v", got, ok, tt.want, tt.ok)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDetailIntOverflow(t *testing.T) {
|
|
if _, ok := (Result{Details: map[string]interface{}{"v": uint64(math.MaxUint64)}}).DetailInt("v"); ok {
|
|
t.Fatal("uint64 max should overflow")
|
|
}
|
|
if _, ok := (Result{Details: map[string]interface{}{"v": float64(1.5)}}).DetailInt("v"); ok {
|
|
t.Fatal("non-integer float should fail")
|
|
}
|
|
}
|
|
|
|
type testStringer struct{ s string }
|
|
|
|
func (ts testStringer) String() string { return ts.s }
|
|
|
|
func TestDetailStringStringer(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{"k": testStringer{"hello"}}}
|
|
v, ok := r.DetailString("k")
|
|
if !ok || v != "hello" {
|
|
t.Fatalf("DetailString(Stringer) = %q/%v, want hello/true", v, ok)
|
|
}
|
|
}
|
|
|
|
func TestDetailStringFallback(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{"k": 42}}
|
|
v, ok := r.DetailString("k")
|
|
if !ok || v != "42" {
|
|
t.Fatalf("DetailString(int) = %q/%v, want 42/true", v, ok)
|
|
}
|
|
}
|
|
|
|
func TestDetailStringNil(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{"k": nil}}
|
|
_, ok := r.DetailString("k")
|
|
if ok {
|
|
t.Fatal("DetailString(nil) should return false")
|
|
}
|
|
}
|
|
|
|
func TestDetailStringMissing(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{}}
|
|
_, ok := r.DetailString("missing")
|
|
if ok {
|
|
t.Fatal("DetailString(missing) should return false")
|
|
}
|
|
}
|
|
|
|
func TestDetailIntStringer(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{"v": testStringer{"99"}}}
|
|
got, ok := r.DetailInt("v")
|
|
if !ok || got != 99 {
|
|
t.Fatalf("DetailInt(Stringer) = %d/%v, want 99/true", got, ok)
|
|
}
|
|
}
|
|
|
|
func TestDetailBool(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
val interface{}
|
|
want bool
|
|
ok bool
|
|
}{
|
|
{"true", true, true, true},
|
|
{"false", false, false, true},
|
|
{"string-true", "true", true, true},
|
|
{"string-false", "false", false, true},
|
|
{"string-1", "1", true, true},
|
|
{"string-0", "0", false, true},
|
|
{"string-invalid", "maybe", false, false},
|
|
{"nil", nil, false, false},
|
|
{"int", 1, false, false},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{"b": tt.val}}
|
|
got, ok := r.DetailBool("b")
|
|
if ok != tt.ok || got != tt.want {
|
|
t.Fatalf("DetailBool = %v/%v, want %v/%v", got, ok, tt.want, tt.ok)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDetailBoolMissing(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{}}
|
|
_, ok := r.DetailBool("missing")
|
|
if ok {
|
|
t.Fatal("DetailBool(missing) should return false")
|
|
}
|
|
}
|
|
|
|
func TestIsWebViaProtocol(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{"protocol": "https"}}
|
|
if !r.IsWeb() {
|
|
t.Fatal("expected web via protocol=https")
|
|
}
|
|
}
|
|
|
|
func TestIsWebViaService(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{"service": "HTTP"}}
|
|
if !r.IsWeb() {
|
|
t.Fatal("expected web via service=HTTP (case-insensitive)")
|
|
}
|
|
}
|
|
|
|
func TestIsWebFalse(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{"service": "ssh"}}
|
|
if r.IsWeb() {
|
|
t.Fatal("ssh should not be web")
|
|
}
|
|
}
|
|
|
|
func TestIsWebNoDetails(t *testing.T) {
|
|
r := Result{}
|
|
if r.IsWeb() {
|
|
t.Fatal("empty result should not be web")
|
|
}
|
|
}
|
|
|
|
func TestResultTypeHelpers(t *testing.T) {
|
|
if !(Result{Type: ResultTypeHost}).IsHost() {
|
|
t.Fatal("IsHost")
|
|
}
|
|
if !(Result{Type: ResultTypePort}).IsPort() {
|
|
t.Fatal("IsPort")
|
|
}
|
|
if !(Result{Type: ResultTypeService}).IsService() {
|
|
t.Fatal("IsService")
|
|
}
|
|
if !(Result{Type: ResultTypeVuln}).IsVuln() {
|
|
t.Fatal("IsVuln")
|
|
}
|
|
if (Result{Type: ResultTypeHost}).IsPort() {
|
|
t.Fatal("host should not be port")
|
|
}
|
|
}
|
|
|
|
func TestResultURLAndVulnerability(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{
|
|
"url": "http://example.com",
|
|
"vulnerability": "CVE-2021-1234",
|
|
}}
|
|
if u, ok := r.URL(); !ok || u != "http://example.com" {
|
|
t.Fatalf("URL = %q/%v", u, ok)
|
|
}
|
|
if v, ok := r.Vulnerability(); !ok || v != "CVE-2021-1234" {
|
|
t.Fatalf("Vulnerability = %q/%v", v, ok)
|
|
}
|
|
}
|
|
|
|
func TestResultProtocol(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{"protocol": "tcp"}}
|
|
if p, ok := r.Protocol(); !ok || p != "tcp" {
|
|
t.Fatalf("Protocol = %q/%v", p, ok)
|
|
}
|
|
}
|
|
|
|
func TestDetailIntNilDetails(t *testing.T) {
|
|
r := Result{}
|
|
_, ok := r.DetailInt("port")
|
|
if ok {
|
|
t.Fatal("DetailInt on nil details should return false")
|
|
}
|
|
}
|
|
|
|
func TestDetailStringNilDetails(t *testing.T) {
|
|
r := Result{}
|
|
_, ok := r.DetailString("service")
|
|
if ok {
|
|
t.Fatal("DetailString on nil details should return false")
|
|
}
|
|
}
|
|
|
|
func TestDetailBoolNilDetails(t *testing.T) {
|
|
r := Result{}
|
|
_, ok := r.DetailBool("is_web")
|
|
if ok {
|
|
t.Fatal("DetailBool on nil details should return false")
|
|
}
|
|
}
|
|
|
|
func TestIsWebExplicitBoolDetail(t *testing.T) {
|
|
r := Result{Details: map[string]interface{}{"is_web": true}}
|
|
if !r.IsWeb() {
|
|
t.Fatal("explicit is_web=true should mark as web")
|
|
}
|
|
|
|
r2 := Result{Details: map[string]interface{}{"is_web": false, "service": "http"}}
|
|
if r2.IsWeb() {
|
|
t.Fatal("explicit is_web=false should override service heuristic")
|
|
}
|
|
}
|
|
|
|
func TestIntFromInt64Overflow(t *testing.T) {
|
|
if _, ok := intFromInt64(math.MaxInt64); !ok {
|
|
t.Fatal("max int64 should fit on 64-bit")
|
|
}
|
|
}
|
|
|
|
func TestIntFromUint64Overflow(t *testing.T) {
|
|
if _, ok := intFromUint64(math.MaxUint64); ok {
|
|
t.Fatal("max uint64 should overflow int")
|
|
}
|
|
if v, ok := intFromUint64(0); !ok || v != 0 {
|
|
t.Fatalf("intFromUint64(0) = %d/%v", v, ok)
|
|
}
|
|
}
|
|
|
|
func TestIntFromFloat64NonInteger(t *testing.T) {
|
|
if _, ok := intFromFloat64(3.14); ok {
|
|
t.Fatal("non-integer float should fail")
|
|
}
|
|
if v, ok := intFromFloat64(100.0); !ok || v != 100 {
|
|
t.Fatalf("intFromFloat64(100.0) = %d/%v", v, ok)
|
|
}
|
|
}
|
|
|
|
func TestResultSummaryAddWebCredential(t *testing.T) {
|
|
var s ResultSummary
|
|
s.Add(Result{
|
|
Type: ResultTypeVuln,
|
|
Target: "10.0.0.1:80",
|
|
Status: "weak_credential: admin:admin",
|
|
Details: map[string]interface{}{
|
|
"service": "http",
|
|
"is_web": true,
|
|
"username": "admin",
|
|
"password": "admin",
|
|
},
|
|
})
|
|
if s.Vulns != 1 || s.Web != 1 || s.Credentials != 1 {
|
|
t.Fatalf("summary = %+v, want vulns=1 web=1 credentials=1", s)
|
|
}
|
|
}
|
|
|
|
func TestAsServiceWithPortOnly(t *testing.T) {
|
|
sr, ok := (Result{
|
|
Type: ResultTypeService,
|
|
Target: "10.0.0.1:3306",
|
|
Details: map[string]interface{}{"port": 3306},
|
|
}).AsService()
|
|
if !ok || sr.Port != 3306 {
|
|
t.Fatalf("AsService with port only = %#v/%v", sr, ok)
|
|
}
|
|
}
|
|
|
|
func TestConvertOutputResultNil(t *testing.T) {
|
|
_, ok := convertOutputResult(nil)
|
|
if ok {
|
|
t.Fatal("convertOutputResult(nil) should return false")
|
|
}
|
|
}
|
|
|
|
func TestConvertOutputResultEmpty(t *testing.T) {
|
|
_, ok := convertOutputResult(&output.ScanResult{})
|
|
if ok {
|
|
t.Fatal("empty output result should return false")
|
|
}
|
|
}
|
|
|
|
func TestConvertOutputResultValid(t *testing.T) {
|
|
raw := &output.ScanResult{
|
|
Type: output.ResultType(ResultTypePort),
|
|
Target: "10.0.0.1",
|
|
Status: "open",
|
|
Details: map[string]interface{}{
|
|
"port": 22,
|
|
},
|
|
}
|
|
r, ok := convertOutputResult(raw)
|
|
if !ok {
|
|
t.Fatal("expected valid conversion")
|
|
}
|
|
if r.Type != ResultTypePort || r.Target != "10.0.0.1" {
|
|
t.Fatalf("converted = %#v", r)
|
|
}
|
|
}
|
|
|
|
func TestResultJSON(t *testing.T) {
|
|
r := Result{
|
|
Type: ResultTypePort,
|
|
Target: "10.0.0.1",
|
|
Status: "open",
|
|
Details: map[string]interface{}{
|
|
"port": 22,
|
|
},
|
|
}
|
|
data, err := json.Marshal(r)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var decoded Result
|
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if decoded.Type != ResultTypePort || decoded.Target != "10.0.0.1" {
|
|
t.Fatalf("round-trip failed: %#v", decoded)
|
|
}
|
|
}
|
|
|
|
func TestPortResultJSON(t *testing.T) {
|
|
pr := PortResult{Target: "10.0.0.1", Port: 80}
|
|
data, err := json.Marshal(pr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := string(data); got != `{"target":"10.0.0.1","port":80}` {
|
|
t.Fatalf("PortResult JSON = %s", got)
|
|
}
|
|
}
|
|
|
|
func TestServiceResultJSON(t *testing.T) {
|
|
sr := ServiceResult{Target: "10.0.0.1:80", Port: 80, Service: "http", IsWeb: true}
|
|
data, err := json.Marshal(sr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var decoded ServiceResult
|
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if decoded.Service != "http" || !decoded.IsWeb {
|
|
t.Fatalf("round-trip failed: %#v", decoded)
|
|
}
|
|
}
|
|
|
|
func TestCredentialResultJSON(t *testing.T) {
|
|
cr := CredentialResult{Target: "10.0.0.1:22", Service: "ssh", Username: "root", Password: "toor"}
|
|
data, err := json.Marshal(cr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var decoded CredentialResult
|
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if decoded.Username != "root" || decoded.Password != "toor" {
|
|
t.Fatalf("round-trip failed: %#v", decoded)
|
|
}
|
|
}
|
|
|
|
func TestVulnerabilityResultJSON(t *testing.T) {
|
|
vr := VulnerabilityResult{Target: "10.0.0.1:445", Service: "smb", Vulnerability: "MS17-010"}
|
|
data, err := json.Marshal(vr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var decoded VulnerabilityResult
|
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if decoded.Vulnerability != "MS17-010" {
|
|
t.Fatalf("round-trip failed: %#v", decoded)
|
|
}
|
|
}
|
|
|
|
func TestResultSummaryJSON(t *testing.T) {
|
|
summary := ResultSummary{Total: 10, Hosts: 2, Ports: 3, Services: 3, Vulns: 2}
|
|
data, err := json.Marshal(summary)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var decoded ResultSummary
|
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if decoded.Total != 10 || decoded.Hosts != 2 {
|
|
t.Fatalf("round-trip failed: %#v", decoded)
|
|
}
|
|
}
|