8531c006d4
* enhancement: supporting http tunneled rtsp * refactor: simplify HTTP tunnel support per review feedback - Extract streamCandidate() for nmap port classification - Add isCommonHTTPPort() for masscan and nmap fallback - Move URL building to Stream.String() and Stream.URL() - Pass Stream directly to attack methods instead of individual args - Add TLS config for HTTPS tunnel support - Make auth detection non-fatal for tunneled streams - Rename HTTPTunnel to UseHTTPTunnel * - Testing the auth workflow for the tunneled streams is not blocking the rest of the pipeline since I changed the return values to Auth unknown and nil - added extra ports in the test according to suggestions * fixing some lint errors * removing the unused buildrtspurl * delayed the urlstream call for clarity removed error messages refactored the test that used the deprecated buildTRSPurl to use stream.URL and stream.String() methods * extracting iscommonHTTP port to pkg/ports (package ports) switching on u.scheme to create proper schemes for http and https * refactor: replace HTTP tunnel bool with scheme-based detection; enable TLS only for HTTPS-tunneled streams * chore: simnplify InferTunnelScheme and newRTSPClient * fix: remove rendundant check in streamCandidate * fix: typo in parseScheme * tests: coverage for new schemes * fix: use RTSP and not RTSPS for HTTPS URLs * fix: tunneled RTSP scheme handling and auth detection fallback * ui: render empty credentials as none in summary and TUI * chore: ignore duplicate-string warning for none literal * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix: rtsps probe headers --------- Co-authored-by: Brendan Le Glaunec <brendan@glaulabs.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
159 lines
4.3 KiB
Go
159 lines
4.3 KiB
Go
package masscan
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/netip"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/Ullaakut/cameradar/v6"
|
|
masscanlib "github.com/Ullaakut/masscan"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestRunScan(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
result *masscanlib.Run
|
|
err error
|
|
wantStreams []cameradar.Stream
|
|
wantDebug []string
|
|
wantProgress []string
|
|
wantErrContains string
|
|
}{
|
|
{
|
|
name: "filters invalid addresses, closed and invalid ports",
|
|
result: &masscanlib.Run{
|
|
Hosts: []masscanlib.Host{
|
|
{
|
|
Address: "192.0.2.10",
|
|
Ports: []masscanlib.Port{
|
|
{Number: 554, Status: "open"},
|
|
{Number: 8554, Status: "closed"},
|
|
{Number: 0, Status: "open"},
|
|
},
|
|
},
|
|
{Address: "not-an-ip", Ports: []masscanlib.Port{{Number: 8554, Status: "open"}}},
|
|
{Address: "", Ports: []masscanlib.Port{{Number: 8554, Status: "open"}}},
|
|
},
|
|
},
|
|
wantStreams: []cameradar.Stream{
|
|
{Address: netip.MustParseAddr("192.0.2.10"), Port: 554},
|
|
},
|
|
wantProgress: []string{
|
|
"Skipping invalid port 0 on 192.0.2.10",
|
|
"Skipping invalid address \"not-an-ip\": ParseAddr(\"not-an-ip\"): unable to parse IP",
|
|
"Skipping host with empty address",
|
|
"Found 1 RTSP streams",
|
|
},
|
|
},
|
|
{
|
|
name: "collects streams from multiple hosts",
|
|
result: &masscanlib.Run{
|
|
Hosts: []masscanlib.Host{
|
|
{Address: "192.0.2.10", Ports: []masscanlib.Port{{Number: 8554, Status: "open"}}},
|
|
{Address: "198.51.100.9", Ports: []masscanlib.Port{{Number: 554, Status: "open"}}},
|
|
},
|
|
},
|
|
wantStreams: []cameradar.Stream{
|
|
{Address: netip.MustParseAddr("192.0.2.10"), Port: 8554},
|
|
{Address: netip.MustParseAddr("198.51.100.9"), Port: 554},
|
|
},
|
|
wantProgress: []string{"Found 2 RTSP streams"},
|
|
},
|
|
{
|
|
name: "sets scheme for common HTTP ports",
|
|
result: &masscanlib.Run{
|
|
Hosts: []masscanlib.Host{
|
|
{
|
|
Address: "192.0.2.10",
|
|
Ports: []masscanlib.Port{
|
|
{Number: 554, Status: "open"},
|
|
{Number: 80, Status: "open"},
|
|
{Number: 443, Status: "open"},
|
|
{Number: 8080, Status: "open"},
|
|
{Number: 8443, Status: "open"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantStreams: []cameradar.Stream{
|
|
{Address: netip.MustParseAddr("192.0.2.10"), Port: 554},
|
|
{Address: netip.MustParseAddr("192.0.2.10"), Port: 80, Scheme: "http"},
|
|
{Address: netip.MustParseAddr("192.0.2.10"), Port: 443, Scheme: "https"},
|
|
{Address: netip.MustParseAddr("192.0.2.10"), Port: 8080, Scheme: "http"},
|
|
{Address: netip.MustParseAddr("192.0.2.10"), Port: 8443, Scheme: "https"},
|
|
},
|
|
wantProgress: []string{"Found 5 RTSP streams"},
|
|
},
|
|
{
|
|
name: "returns error when scan fails",
|
|
err: errors.New("scan failed"),
|
|
wantErrContains: "scanning network",
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
reporter := &recordingReporter{}
|
|
|
|
streams, err := runScan(t.Context(), fakeRunner{result: test.result, err: test.err}, reporter)
|
|
|
|
if test.wantErrContains != "" {
|
|
require.Error(t, err)
|
|
assert.ErrorContains(t, err, test.wantErrContains)
|
|
assert.Empty(t, streams)
|
|
assert.Empty(t, reporter.progress)
|
|
assert.Equal(t, test.wantDebug, reporter.debug)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, test.wantStreams, streams)
|
|
assert.Equal(t, test.wantDebug, reporter.debug)
|
|
for _, progress := range test.wantProgress {
|
|
assert.Contains(t, reporter.progress, progress)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type fakeRunner struct {
|
|
result *masscanlib.Run
|
|
err error
|
|
}
|
|
|
|
func (f fakeRunner) Run(context.Context) (*masscanlib.Run, error) {
|
|
return f.result, f.err
|
|
}
|
|
|
|
type recordingReporter struct {
|
|
mu sync.Mutex
|
|
debug []string
|
|
progress []string
|
|
}
|
|
|
|
func (r *recordingReporter) Start(cameradar.Step, string) {}
|
|
|
|
func (r *recordingReporter) Done(cameradar.Step, string) {}
|
|
|
|
func (r *recordingReporter) Progress(_ cameradar.Step, message string) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
r.progress = append(r.progress, message)
|
|
}
|
|
|
|
func (r *recordingReporter) Debug(_ cameradar.Step, message string) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
r.debug = append(r.debug, message)
|
|
}
|
|
|
|
func (r *recordingReporter) Error(cameradar.Step, error) {}
|
|
|
|
func (r *recordingReporter) Summary([]cameradar.Stream, error) {}
|
|
|
|
func (r *recordingReporter) Close() {}
|