Files
osmedeus/internal/functions/nmap_functions_test.go
T
j3ssie 459d5939fb feat: add cloud infrastructure and nmap/tmux function support
- Implement cloud provider infrastructure (DigitalOcean, AWS, GCP, Linode, Azure) with Pulumi integration for distributed scanning
- Add nmap and tmux utility functions for port scanning results processing and long-running background session management
- Introduce webhook-triggered run execution with unique UUID and authentication key support for external integrations
2026-02-16 15:23:24 +07:00

283 lines
6.8 KiB
Go

package functions
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/j3ssie/osmedeus/v5/internal/json"
)
func TestParseNmapXML(t *testing.T) {
testdataPath := filepath.Join("..", "..", "test", "testdata", "sample-jsonl-output", "sample-nmap-result-1.xml")
// Check if test file exists
if _, err := os.Stat(testdataPath); os.IsNotExist(err) {
t.Skipf("Test file not found: %s", testdataPath)
return
}
results, err := parseNmapXML(testdataPath)
if err != nil {
t.Fatalf("Failed to parse XML: %v", err)
}
if len(results) == 0 {
t.Error("Expected at least 1 host, got 0")
return
}
// Verify first host
host := results[0]
if host.HostIP == "" {
t.Error("Expected non-empty host IP")
}
if host.AssetType != "ip" {
t.Errorf("Expected asset type 'ip', got '%s'", host.AssetType)
}
if len(host.Ports) == 0 {
t.Error("Expected ports, got none")
}
// Verify JSONL serialization
data, err := json.Marshal(host)
if err != nil {
t.Errorf("Failed to marshal to JSON: %v", err)
}
if len(data) == 0 {
t.Error("Expected non-empty JSON output")
}
// Verify it's valid JSON
var unmarshal AssetOutput
if err := json.Unmarshal(data, &unmarshal); err != nil {
t.Errorf("Failed to unmarshal JSON: %v", err)
}
}
func TestParseNmapGrepable(t *testing.T) {
testdataPath := filepath.Join("..", "..", "test", "testdata", "sample-jsonl-output", "sample-nmap-result-1.gnmap")
// Check if test file exists
if _, err := os.Stat(testdataPath); os.IsNotExist(err) {
t.Skipf("Test file not found: %s", testdataPath)
return
}
results, err := parseNmapGrepable(testdataPath)
if err != nil {
t.Fatalf("Failed to parse grepable: %v", err)
}
if len(results) == 0 {
t.Error("Expected at least 1 host, got 0")
return
}
// Verify first host
host := results[0]
if host.HostIP == "" {
t.Error("Expected non-empty host IP")
}
if host.AssetType != "ip" {
t.Errorf("Expected asset type 'ip', got '%s'", host.AssetType)
}
if len(host.Ports) == 0 {
t.Error("Expected ports, got none")
}
}
func TestParseNmapXMLMultipleHosts(t *testing.T) {
testdataPath := filepath.Join("..", "..", "test", "testdata", "sample-jsonl-output", "sample-nmap-result-2.xml")
// Check if test file exists
if _, err := os.Stat(testdataPath); os.IsNotExist(err) {
t.Skipf("Test file not found: %s", testdataPath)
return
}
results, err := parseNmapXML(testdataPath)
if err != nil {
t.Fatalf("Failed to parse XML: %v", err)
}
if len(results) == 0 {
t.Error("Expected at least 1 host, got 0")
}
// Verify all hosts have required fields
for i, host := range results {
if host.HostIP == "" {
t.Errorf("Host %d: Expected non-empty host IP", i)
}
if host.AssetValue == "" {
t.Errorf("Host %d: Expected non-empty asset value", i)
}
if host.AssetType != "ip" {
t.Errorf("Host %d: Expected asset type 'ip', got '%s'", i, host.AssetType)
}
}
}
func TestNmapToJSONLIntegration(t *testing.T) {
testdataPath := filepath.Join("..", "..", "test", "testdata", "sample-jsonl-output", "sample-nmap-result-1.xml")
// Check if test file exists
if _, err := os.Stat(testdataPath); os.IsNotExist(err) {
t.Skipf("Test file not found: %s", testdataPath)
return
}
// Create temp output file
tmpDir := t.TempDir()
outputPath := filepath.Join(tmpDir, "output.jsonl")
// Parse and write
results, err := parseNmapXML(testdataPath)
if err != nil {
t.Fatalf("Failed to parse XML: %v", err)
}
// Write JSONL
outFile, err := os.Create(outputPath)
if err != nil {
t.Fatalf("Failed to create output file: %v", err)
}
for _, asset := range results {
data, err := json.Marshal(asset)
if err != nil {
t.Fatalf("Failed to marshal asset: %v", err)
}
if _, err := outFile.Write(data); err != nil {
t.Fatalf("Failed to write data: %v", err)
}
if _, err := outFile.WriteString("\n"); err != nil {
t.Fatalf("Failed to write newline: %v", err)
}
}
if err := outFile.Close(); err != nil {
t.Fatalf("Failed to close file: %v", err)
}
// Verify output file exists and is not empty
stat, err := os.Stat(outputPath)
if err != nil {
t.Fatalf("Output file not found: %v", err)
}
if stat.Size() == 0 {
t.Error("Output file is empty")
}
// Read and verify JSONL format
content, err := os.ReadFile(outputPath)
if err != nil {
t.Fatalf("Failed to read output file: %v", err)
}
lines := strings.Split(strings.TrimSpace(string(content)), "\n")
if len(lines) == 0 {
t.Error("Expected at least one line in JSONL output")
}
// Verify each line is valid JSON
for i, line := range lines {
if line == "" {
continue
}
var asset AssetOutput
if err := json.Unmarshal([]byte(line), &asset); err != nil {
t.Errorf("Line %d is not valid JSON: %v", i+1, err)
}
// Verify required fields
if asset.HostIP == "" {
t.Errorf("Line %d: Missing host_ip", i+1)
}
if asset.AssetValue == "" {
t.Errorf("Line %d: Missing asset_value", i+1)
}
if asset.AssetType == "" {
t.Errorf("Line %d: Missing asset_type", i+1)
}
}
}
func TestParseNmapXMLEmptyFile(t *testing.T) {
tmpDir := t.TempDir()
emptyFile := filepath.Join(tmpDir, "empty.xml")
if err := os.WriteFile(emptyFile, []byte(""), 0644); err != nil {
t.Fatalf("Failed to create empty file: %v", err)
}
_, err := parseNmapXML(emptyFile)
if err == nil {
t.Error("Expected error for empty file, got nil")
}
}
func TestParseNmapXMLInvalidXML(t *testing.T) {
tmpDir := t.TempDir()
invalidFile := filepath.Join(tmpDir, "invalid.xml")
if err := os.WriteFile(invalidFile, []byte("not valid xml"), 0644); err != nil {
t.Fatalf("Failed to create invalid file: %v", err)
}
_, err := parseNmapXML(invalidFile)
if err == nil {
t.Error("Expected error for invalid XML, got nil")
}
}
func TestParseNmapGrepableInvalidFile(t *testing.T) {
tmpDir := t.TempDir()
invalidFile := filepath.Join(tmpDir, "invalid.gnmap")
if err := os.WriteFile(invalidFile, []byte("not a grepable format"), 0644); err != nil {
t.Fatalf("Failed to create invalid file: %v", err)
}
results, err := parseNmapGrepable(invalidFile)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
// Should return empty results for invalid format
if len(results) != 0 {
t.Errorf("Expected 0 results for invalid format, got %d", len(results))
}
}
func TestSanitizeTargetForPath(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"192.168.1.0/24", "192-168-1-0-24"},
{"10.0.0.1", "10-0-0-1"},
{"example.com", "example-com"},
{"192.168.1.1:8080", "192-168-1-1-8080"},
{"targets.txt", "targets"},
{"scan.xml", "scan"},
{"http://example.com", "http---example-com"},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
result := sanitizeTargetForPath(tt.input)
if result != tt.expected {
t.Errorf("sanitizeTargetForPath(%q) = %q, expected %q", tt.input, result, tt.expected)
}
})
}
}