Increase test coverage, mock libcurl & uniformize error messages
This commit is contained in:
committed by
Brendan Le Glaunec
parent
c1ea6b167c
commit
74672f6625
@@ -14,120 +14,110 @@ func doNotWrite([]uint8, interface{}) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func routeAttack(camera Stream, route string, timeout time.Duration, enableLogs bool) bool {
|
func routeAttack(c Curler, camera Stream, route string, timeout time.Duration, enableLogs bool) bool {
|
||||||
easy := curl.EasyInit()
|
attackURL := fmt.Sprintf(
|
||||||
defer easy.Cleanup()
|
"rtsp://%s:%s@%s:%d/%s",
|
||||||
|
camera.Username,
|
||||||
|
camera.Password,
|
||||||
|
camera.Address,
|
||||||
|
camera.Port,
|
||||||
|
route,
|
||||||
|
)
|
||||||
|
|
||||||
if easy != nil {
|
if enableLogs {
|
||||||
attackURL := fmt.Sprintf(
|
// Debug logs when logs are enabled
|
||||||
"rtsp://%s:%s@%s:%d/%s",
|
c.Setopt(curl.OPT_VERBOSE, 1)
|
||||||
camera.Username,
|
} else {
|
||||||
camera.Password,
|
// Do not write sdp in stdout
|
||||||
camera.Address,
|
c.Setopt(curl.OPT_WRITEFUNCTION, doNotWrite)
|
||||||
camera.Port,
|
}
|
||||||
route,
|
|
||||||
)
|
|
||||||
|
|
||||||
if enableLogs {
|
// Do not send a body in the describe request
|
||||||
// Debug logs when logs are enabled
|
c.Setopt(curl.OPT_NOBODY, 1)
|
||||||
easy.Setopt(curl.OPT_VERBOSE, 1)
|
// Send a request to the URL of the camera we want to attack
|
||||||
} else {
|
c.Setopt(curl.OPT_URL, attackURL)
|
||||||
// Do not write sdp in stdout
|
// Set the RTSP STREAM URI as the camera URL
|
||||||
easy.Setopt(curl.OPT_WRITEFUNCTION, doNotWrite)
|
c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
|
||||||
}
|
// 2 is CURL_RTSPREQ_DESCRIBE
|
||||||
|
c.Setopt(curl.OPT_RTSP_REQUEST, 2)
|
||||||
|
// Set custom timeout
|
||||||
|
c.Setopt(curl.OPT_TIMEOUT_MS, int(timeout/time.Millisecond))
|
||||||
|
|
||||||
// Do not send a body in the describe request
|
// Perform the request
|
||||||
easy.Setopt(curl.OPT_NOBODY, 1)
|
err := c.Perform()
|
||||||
// Send a request to the URL of the camera we want to attack
|
if err != nil {
|
||||||
easy.Setopt(curl.OPT_URL, attackURL)
|
fmt.Printf("\nERROR: curl timeout on camera '%s' reached after %s.\nconsider increasing the timeout (-T, --timeout parameter) to at least 5000ms if scanning an unstable network.\n", camera.Address, timeout.String())
|
||||||
// Set the RTSP STREAM URI as the camera URL
|
return false
|
||||||
easy.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
|
}
|
||||||
// 2 is CURL_RTSPREQ_DESCRIBE
|
|
||||||
easy.Setopt(curl.OPT_RTSP_REQUEST, 2)
|
|
||||||
// Set custom timeout
|
|
||||||
easy.Setopt(curl.OPT_TIMEOUT_MS, int(timeout/time.Millisecond))
|
|
||||||
|
|
||||||
// Perform the request
|
// Get return code for the request
|
||||||
err := easy.Perform()
|
rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("\nERROR: curl timeout on camera '%s' reached after %s.\nconsider increasing the timeout (-T, --timeout parameter) to at least 5000ms if scanning an unstable network.\n", camera.Address, timeout.String())
|
return false
|
||||||
return false
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Get return code for the request
|
// If it's a 401 or 403, it means that the credentials are wrong but the route might be okay
|
||||||
rc, err := easy.Getinfo(curl.INFO_RESPONSE_CODE)
|
// If it's a 200, the camera is accessed successfully
|
||||||
if err != nil {
|
if rc == 200 || rc == 401 || rc == 403 {
|
||||||
return false
|
return true
|
||||||
}
|
|
||||||
|
|
||||||
// If it's a 401 or 403, it means that the credentials are wrong but the route might be okay
|
|
||||||
// If it's a 200, the camera is accessed successfully
|
|
||||||
if rc == 200 || rc == 401 || rc == 403 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func credAttack(camera Stream, username string, password string, timeout time.Duration, enableLogs bool) bool {
|
func credAttack(c Curler, camera Stream, username string, password string, timeout time.Duration, enableLogs bool) bool {
|
||||||
easy := curl.EasyInit()
|
attackURL := fmt.Sprintf(
|
||||||
defer easy.Cleanup()
|
"rtsp://%s:%s@%s:%d/%s",
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
camera.Address,
|
||||||
|
camera.Port,
|
||||||
|
camera.Route,
|
||||||
|
)
|
||||||
|
|
||||||
if easy != nil {
|
if enableLogs {
|
||||||
attackURL := fmt.Sprintf(
|
// Debug logs when logs are enabled
|
||||||
"rtsp://%s:%s@%s:%d/%s",
|
c.Setopt(curl.OPT_VERBOSE, 1)
|
||||||
username,
|
} else {
|
||||||
password,
|
// Do not write sdp in stdout
|
||||||
camera.Address,
|
c.Setopt(curl.OPT_WRITEFUNCTION, doNotWrite)
|
||||||
camera.Port,
|
}
|
||||||
camera.Route,
|
|
||||||
)
|
|
||||||
|
|
||||||
if enableLogs {
|
// Do not send a body in the describe request
|
||||||
// Debug logs when logs are enabled
|
c.Setopt(curl.OPT_NOBODY, 1)
|
||||||
easy.Setopt(curl.OPT_VERBOSE, 1)
|
// Send a request to the URL of the camera we want to attack
|
||||||
} else {
|
c.Setopt(curl.OPT_URL, attackURL)
|
||||||
// Do not write sdp in stdout
|
// Set the RTSP STREAM URI as the camera URL
|
||||||
easy.Setopt(curl.OPT_WRITEFUNCTION, doNotWrite)
|
c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
|
||||||
}
|
// 2 is CURL_RTSPREQ_DESCRIBE
|
||||||
|
c.Setopt(curl.OPT_RTSP_REQUEST, 2)
|
||||||
|
// Set custom timeout
|
||||||
|
c.Setopt(curl.OPT_TIMEOUT_MS, int(timeout/time.Millisecond))
|
||||||
|
|
||||||
// Do not send a body in the describe request
|
// Perform the request
|
||||||
easy.Setopt(curl.OPT_NOBODY, 1)
|
err := c.Perform()
|
||||||
// Send a request to the URL of the camera we want to attack
|
if err != nil {
|
||||||
easy.Setopt(curl.OPT_URL, attackURL)
|
fmt.Printf("\nERROR: curl timeout on camera '%s' reached after %s.\nconsider increasing the timeout (-T, --timeout parameter) to at least 5000ms if scanning an unstable network.\n", camera.Address, timeout.String())
|
||||||
// Set the RTSP STREAM URI as the camera URL
|
return false
|
||||||
easy.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL)
|
}
|
||||||
// 2 is CURL_RTSPREQ_DESCRIBE
|
|
||||||
easy.Setopt(curl.OPT_RTSP_REQUEST, 2)
|
|
||||||
// Set custom timeout
|
|
||||||
easy.Setopt(curl.OPT_TIMEOUT_MS, int(timeout/time.Millisecond))
|
|
||||||
|
|
||||||
// Perform the request
|
// Get return code for the request
|
||||||
err := easy.Perform()
|
rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("\nERROR: curl timeout on camera '%s' reached after %s.\nconsider increasing the timeout (-T, --timeout parameter) to at least 5000ms if scanning an unstable network.\n", camera.Address, timeout.String())
|
return false
|
||||||
return false
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Get return code for the request
|
// If it's a 404, it means that the route is incorrect but the credentials might be okay
|
||||||
rc, err := easy.Getinfo(curl.INFO_RESPONSE_CODE)
|
// If it's a 200, the camera is accessed successfully
|
||||||
if err != nil {
|
if rc == 200 || rc == 404 {
|
||||||
return false
|
return true
|
||||||
}
|
|
||||||
|
|
||||||
// If it's a 404, it means that the route is incorrect but the credentials might be okay
|
|
||||||
// If it's a 200, the camera is accessed successfully
|
|
||||||
if rc == 200 || rc == 404 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func attackCameraCredentials(target Stream, credentials Credentials, resultsChan chan<- Stream, timeout time.Duration, log bool) {
|
func attackCameraCredentials(c Curler, target Stream, credentials Credentials, resultsChan chan<- Stream, timeout time.Duration, log bool) {
|
||||||
for _, username := range credentials.Usernames {
|
for _, username := range credentials.Usernames {
|
||||||
for _, password := range credentials.Passwords {
|
for _, password := range credentials.Passwords {
|
||||||
ok := credAttack(target, username, password, timeout, log)
|
ok := credAttack(c, target, username, password, timeout, log)
|
||||||
if ok {
|
if ok {
|
||||||
target.CredentialsFound = true
|
target.CredentialsFound = true
|
||||||
target.Username = username
|
target.Username = username
|
||||||
@@ -141,9 +131,9 @@ func attackCameraCredentials(target Stream, credentials Credentials, resultsChan
|
|||||||
resultsChan <- target
|
resultsChan <- target
|
||||||
}
|
}
|
||||||
|
|
||||||
func attackCameraRoute(target Stream, routes Routes, resultsChan chan<- Stream, timeout time.Duration, log bool) {
|
func attackCameraRoute(c Curler, target Stream, routes Routes, resultsChan chan<- Stream, timeout time.Duration, log bool) {
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
ok := routeAttack(target, route, timeout, log)
|
ok := routeAttack(c, target, route, timeout, log)
|
||||||
if ok {
|
if ok {
|
||||||
target.RouteFound = true
|
target.RouteFound = true
|
||||||
target.Route = route
|
target.Route = route
|
||||||
@@ -157,12 +147,7 @@ func attackCameraRoute(target Stream, routes Routes, resultsChan chan<- Stream,
|
|||||||
|
|
||||||
// AttackCredentials attempts to guess the provided targets' credentials using the given
|
// AttackCredentials attempts to guess the provided targets' credentials using the given
|
||||||
// dictionary or the default dictionary if none was provided by the user.
|
// dictionary or the default dictionary if none was provided by the user.
|
||||||
func AttackCredentials(targets []Stream, credentials Credentials, timeout time.Duration, log bool) ([]Stream, error) {
|
func AttackCredentials(c Curler, targets []Stream, credentials Credentials, timeout time.Duration, log bool) ([]Stream, error) {
|
||||||
err := curl.GlobalInit(curl.GLOBAL_ALL)
|
|
||||||
if err != nil {
|
|
||||||
return targets, errors.Wrap(err, "could not initialize curl")
|
|
||||||
}
|
|
||||||
|
|
||||||
attacks := make(chan Stream)
|
attacks := make(chan Stream)
|
||||||
defer close(attacks)
|
defer close(attacks)
|
||||||
|
|
||||||
@@ -170,10 +155,10 @@ func AttackCredentials(targets []Stream, credentials Credentials, timeout time.D
|
|||||||
for _, target := range targets {
|
for _, target := range targets {
|
||||||
err := validate.Struct(target)
|
err := validate.Struct(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return targets, errors.Wrap(err, "invalid streams")
|
return targets, errors.Wrap(err, "invalid targets")
|
||||||
}
|
}
|
||||||
|
|
||||||
go attackCameraCredentials(target, credentials, attacks, timeout, log)
|
go attackCameraCredentials(c, target, credentials, attacks, timeout, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
attackResults := []Stream{}
|
attackResults := []Stream{}
|
||||||
@@ -197,12 +182,7 @@ func AttackCredentials(targets []Stream, credentials Credentials, timeout time.D
|
|||||||
|
|
||||||
// AttackRoute attempts to guess the provided targets' streaming routes using the given
|
// AttackRoute attempts to guess the provided targets' streaming routes using the given
|
||||||
// dictionary or the default dictionary if none was provided by the user.
|
// dictionary or the default dictionary if none was provided by the user.
|
||||||
func AttackRoute(targets []Stream, routes Routes, timeout time.Duration, log bool) ([]Stream, error) {
|
func AttackRoute(c Curler, targets []Stream, routes Routes, timeout time.Duration, log bool) ([]Stream, error) {
|
||||||
err := curl.GlobalInit(curl.GLOBAL_ALL)
|
|
||||||
if err != nil {
|
|
||||||
return targets, errors.Wrap(err, "could not initialize curl")
|
|
||||||
}
|
|
||||||
|
|
||||||
attacks := make(chan Stream)
|
attacks := make(chan Stream)
|
||||||
defer close(attacks)
|
defer close(attacks)
|
||||||
|
|
||||||
@@ -210,10 +190,10 @@ func AttackRoute(targets []Stream, routes Routes, timeout time.Duration, log boo
|
|||||||
for _, target := range targets {
|
for _, target := range targets {
|
||||||
err := validate.Struct(target)
|
err := validate.Struct(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return targets, errors.Wrap(err, "invalid streams")
|
return targets, errors.Wrap(err, "invalid targets")
|
||||||
}
|
}
|
||||||
|
|
||||||
go attackCameraRoute(target, routes, attacks, timeout, log)
|
go attackCameraRoute(c, target, routes, attacks, timeout, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
attackResults := []Stream{}
|
attackResults := []Stream{}
|
||||||
|
|||||||
+208
-25
@@ -1,16 +1,35 @@
|
|||||||
package cmrdr
|
package cmrdr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
curl "github.com/andelf/go-curl"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Again, since these tests use the curl library, I don't want to spend ages trying to mock
|
type CurlerMock struct {
|
||||||
// the lib right now.
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CurlerMock) Setopt(opt int, param interface{}) error {
|
||||||
|
args := m.Called(opt, param)
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CurlerMock) Perform() error {
|
||||||
|
args := m.Called()
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CurlerMock) Getinfo(info curl.CurlInfo) (interface{}, error) {
|
||||||
|
args := m.Called(info)
|
||||||
|
return args.Int(0), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAttackCredentials(t *testing.T) {
|
func TestAttackCredentials(t *testing.T) {
|
||||||
validStream1 := Stream{
|
validStream1 := Stream{
|
||||||
@@ -25,58 +44,135 @@ func TestAttackCredentials(t *testing.T) {
|
|||||||
Port: 1337,
|
Port: 1337,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invalidStream := Stream{
|
||||||
|
Device: "InvalidDevice",
|
||||||
|
}
|
||||||
|
|
||||||
fakeTargets := []Stream{validStream1, validStream2}
|
fakeTargets := []Stream{validStream1, validStream2}
|
||||||
|
invalidTargets := []Stream{invalidStream}
|
||||||
fakeCredentials := Credentials{
|
fakeCredentials := Credentials{
|
||||||
Usernames: []string{"admin", "root"},
|
Usernames: []string{"admin", "root"},
|
||||||
Passwords: []string{"12345", "root"},
|
Passwords: []string{"12345", "root"},
|
||||||
}
|
}
|
||||||
|
|
||||||
vectors := []struct {
|
testCases := []struct {
|
||||||
targets []Stream
|
targets []Stream
|
||||||
credentials Credentials
|
credentials Credentials
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
log bool
|
log bool
|
||||||
|
|
||||||
|
status int
|
||||||
|
|
||||||
|
performErr error
|
||||||
|
getInfoErr error
|
||||||
|
invalidTargets bool
|
||||||
|
|
||||||
expectedStreams []Stream
|
expectedStreams []Stream
|
||||||
expectedErrMsg string
|
expectedErrMsg string
|
||||||
}{
|
}{
|
||||||
// Valid baseline
|
// Credentials found
|
||||||
|
{
|
||||||
|
targets: fakeTargets,
|
||||||
|
credentials: fakeCredentials,
|
||||||
|
timeout: 1 * time.Millisecond,
|
||||||
|
|
||||||
|
status: 404,
|
||||||
|
|
||||||
|
expectedStreams: fakeTargets,
|
||||||
|
},
|
||||||
|
// Camera accessed
|
||||||
|
{
|
||||||
|
targets: fakeTargets,
|
||||||
|
credentials: fakeCredentials,
|
||||||
|
timeout: 1 * time.Millisecond,
|
||||||
|
|
||||||
|
status: 200,
|
||||||
|
|
||||||
|
expectedStreams: fakeTargets,
|
||||||
|
},
|
||||||
|
// Invalid targets
|
||||||
|
{
|
||||||
|
targets: invalidTargets,
|
||||||
|
credentials: fakeCredentials,
|
||||||
|
timeout: 1 * time.Millisecond,
|
||||||
|
|
||||||
|
invalidTargets: true,
|
||||||
|
|
||||||
|
expectedErrMsg: "invalid targets",
|
||||||
|
expectedStreams: invalidTargets,
|
||||||
|
},
|
||||||
|
// curl perform fails
|
||||||
|
{
|
||||||
|
targets: fakeTargets,
|
||||||
|
credentials: fakeCredentials,
|
||||||
|
timeout: 1 * time.Millisecond,
|
||||||
|
|
||||||
|
performErr: errors.New("dummy error"),
|
||||||
|
|
||||||
|
expectedStreams: fakeTargets,
|
||||||
|
expectedErrMsg: "no credentials found",
|
||||||
|
},
|
||||||
|
// curl getinfo fails
|
||||||
|
{
|
||||||
|
targets: fakeTargets,
|
||||||
|
credentials: fakeCredentials,
|
||||||
|
timeout: 1 * time.Millisecond,
|
||||||
|
|
||||||
|
getInfoErr: errors.New("dummy error"),
|
||||||
|
|
||||||
|
expectedStreams: fakeTargets,
|
||||||
|
expectedErrMsg: "no credentials found",
|
||||||
|
},
|
||||||
|
// Credentials not found
|
||||||
{
|
{
|
||||||
targets: fakeTargets,
|
targets: fakeTargets,
|
||||||
credentials: fakeCredentials,
|
credentials: fakeCredentials,
|
||||||
timeout: 1 * time.Millisecond,
|
timeout: 1 * time.Millisecond,
|
||||||
log: true,
|
log: true,
|
||||||
|
|
||||||
|
status: 403,
|
||||||
|
|
||||||
expectedStreams: fakeTargets,
|
expectedStreams: fakeTargets,
|
||||||
expectedErrMsg: "no credentials found",
|
expectedErrMsg: "no credentials found",
|
||||||
},
|
},
|
||||||
// Valid baseline without logs
|
// Logging disabled
|
||||||
{
|
{
|
||||||
targets: fakeTargets,
|
targets: fakeTargets,
|
||||||
credentials: fakeCredentials,
|
credentials: fakeCredentials,
|
||||||
timeout: 1 * time.Millisecond,
|
timeout: 1 * time.Millisecond,
|
||||||
log: false,
|
log: false,
|
||||||
|
|
||||||
|
status: 403,
|
||||||
|
|
||||||
expectedStreams: fakeTargets,
|
expectedStreams: fakeTargets,
|
||||||
expectedErrMsg: "no credentials found",
|
expectedErrMsg: "no credentials found",
|
||||||
},
|
},
|
||||||
// TODO: Refacto and make tests with all possible error cases
|
|
||||||
}
|
}
|
||||||
for i, vector := range vectors {
|
for i, test := range testCases {
|
||||||
results, err := AttackCredentials(vector.targets, vector.credentials, vector.timeout, vector.log)
|
curlerMock := &CurlerMock{}
|
||||||
|
|
||||||
if len(vector.expectedErrMsg) > 0 {
|
if !test.invalidTargets {
|
||||||
|
curlerMock.On("Setopt", mock.Anything, mock.Anything).Return(nil)
|
||||||
|
curlerMock.On("Perform").Return(test.performErr)
|
||||||
|
if test.performErr == nil {
|
||||||
|
curlerMock.On("Getinfo", mock.Anything).Return(test.status, test.getInfoErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := AttackCredentials(curlerMock, test.targets, test.credentials, test.timeout, test.log)
|
||||||
|
|
||||||
|
if len(test.expectedErrMsg) > 0 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fmt.Printf("unexpected success in AttackCredentials test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg)
|
fmt.Printf("unexpected success in AttackCredentials test, iteration %d. expected error: %s\n", i, test.expectedErrMsg)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("unexpected error in AttackCredentials test, iteration %d: %v\n", i, err)
|
fmt.Printf("unexpected error in AttackCredentials test, iteration %d: %v\n", i, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
for _, stream := range vector.expectedStreams {
|
for _, stream := range test.expectedStreams {
|
||||||
foundStream := false
|
foundStream := false
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
||||||
@@ -86,8 +182,8 @@ func TestAttackCredentials(t *testing.T) {
|
|||||||
assert.Equal(t, true, foundStream, "wrong streams parsed")
|
assert.Equal(t, true, foundStream, "wrong streams parsed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(vector.expectedStreams), len(results), "wrong streams parsed")
|
assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed")
|
||||||
|
curlerMock.AssertExpectations(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,19 +200,92 @@ func TestAttackRoute(t *testing.T) {
|
|||||||
Port: 1337,
|
Port: 1337,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invalidStream := Stream{
|
||||||
|
Device: "InvalidDevice",
|
||||||
|
}
|
||||||
|
|
||||||
fakeTargets := []Stream{validStream1, validStream2}
|
fakeTargets := []Stream{validStream1, validStream2}
|
||||||
fakeRoutes := Routes{"live.sdp", "media.amp"}
|
fakeRoutes := Routes{"live.sdp", "media.amp"}
|
||||||
|
invalidTargets := []Stream{invalidStream}
|
||||||
|
|
||||||
vectors := []struct {
|
testCases := []struct {
|
||||||
targets []Stream
|
targets []Stream
|
||||||
routes Routes
|
routes Routes
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
log bool
|
log bool
|
||||||
|
|
||||||
|
status int
|
||||||
|
|
||||||
|
performErr error
|
||||||
|
getInfoErr error
|
||||||
|
invalidTargets bool
|
||||||
|
|
||||||
expectedStreams []Stream
|
expectedStreams []Stream
|
||||||
expectedErrMsg string
|
expectedErrMsg string
|
||||||
}{
|
}{
|
||||||
// Valid baseline
|
// Route found
|
||||||
|
{
|
||||||
|
targets: fakeTargets,
|
||||||
|
routes: fakeRoutes,
|
||||||
|
timeout: 1 * time.Millisecond,
|
||||||
|
|
||||||
|
status: 403,
|
||||||
|
|
||||||
|
expectedStreams: fakeTargets,
|
||||||
|
},
|
||||||
|
// Route found
|
||||||
|
{
|
||||||
|
targets: fakeTargets,
|
||||||
|
routes: fakeRoutes,
|
||||||
|
timeout: 1 * time.Millisecond,
|
||||||
|
|
||||||
|
status: 401,
|
||||||
|
|
||||||
|
expectedStreams: fakeTargets,
|
||||||
|
},
|
||||||
|
// Camera accessed
|
||||||
|
{
|
||||||
|
targets: fakeTargets,
|
||||||
|
routes: fakeRoutes,
|
||||||
|
timeout: 1 * time.Millisecond,
|
||||||
|
|
||||||
|
status: 200,
|
||||||
|
|
||||||
|
expectedStreams: fakeTargets,
|
||||||
|
},
|
||||||
|
// Invalid targets
|
||||||
|
{
|
||||||
|
targets: invalidTargets,
|
||||||
|
routes: fakeRoutes,
|
||||||
|
timeout: 1 * time.Millisecond,
|
||||||
|
invalidTargets: true,
|
||||||
|
|
||||||
|
expectedErrMsg: "invalid targets",
|
||||||
|
expectedStreams: invalidTargets,
|
||||||
|
},
|
||||||
|
// curl perform fails
|
||||||
|
{
|
||||||
|
targets: fakeTargets,
|
||||||
|
routes: fakeRoutes,
|
||||||
|
timeout: 1 * time.Millisecond,
|
||||||
|
|
||||||
|
performErr: errors.New("dummy error"),
|
||||||
|
|
||||||
|
expectedStreams: fakeTargets,
|
||||||
|
expectedErrMsg: "no routes found",
|
||||||
|
},
|
||||||
|
// curl getinfo fails
|
||||||
|
{
|
||||||
|
targets: fakeTargets,
|
||||||
|
routes: fakeRoutes,
|
||||||
|
timeout: 1 * time.Millisecond,
|
||||||
|
|
||||||
|
getInfoErr: errors.New("dummy error"),
|
||||||
|
|
||||||
|
expectedStreams: fakeTargets,
|
||||||
|
expectedErrMsg: "no routes found",
|
||||||
|
},
|
||||||
|
// Routes not found
|
||||||
{
|
{
|
||||||
targets: fakeTargets,
|
targets: fakeTargets,
|
||||||
routes: fakeRoutes,
|
routes: fakeRoutes,
|
||||||
@@ -126,7 +295,7 @@ func TestAttackRoute(t *testing.T) {
|
|||||||
expectedStreams: fakeTargets,
|
expectedStreams: fakeTargets,
|
||||||
expectedErrMsg: "no routes found",
|
expectedErrMsg: "no routes found",
|
||||||
},
|
},
|
||||||
// Valid baseline without logs
|
// Logs disabled
|
||||||
{
|
{
|
||||||
targets: fakeTargets,
|
targets: fakeTargets,
|
||||||
routes: fakeRoutes,
|
routes: fakeRoutes,
|
||||||
@@ -136,23 +305,32 @@ func TestAttackRoute(t *testing.T) {
|
|||||||
expectedStreams: fakeTargets,
|
expectedStreams: fakeTargets,
|
||||||
expectedErrMsg: "no routes found",
|
expectedErrMsg: "no routes found",
|
||||||
},
|
},
|
||||||
// TODO: Refacto and make tests with all possible error cases
|
|
||||||
}
|
}
|
||||||
for i, vector := range vectors {
|
for i, test := range testCases {
|
||||||
results, err := AttackRoute(vector.targets, vector.routes, vector.timeout, vector.log)
|
curlerMock := &CurlerMock{}
|
||||||
|
|
||||||
if len(vector.expectedErrMsg) > 0 {
|
if !test.invalidTargets {
|
||||||
|
curlerMock.On("Setopt", mock.Anything, mock.Anything).Return(nil)
|
||||||
|
curlerMock.On("Perform").Return(test.performErr)
|
||||||
|
if test.performErr == nil {
|
||||||
|
curlerMock.On("Getinfo", mock.Anything).Return(test.status, test.getInfoErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := AttackRoute(curlerMock, test.targets, test.routes, test.timeout, test.log)
|
||||||
|
|
||||||
|
if len(test.expectedErrMsg) > 0 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fmt.Printf("unexpected success in AttackRoute test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg)
|
fmt.Printf("unexpected success in AttackRoute test, iteration %d. expected error: %s\n", i, test.expectedErrMsg)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("unexpected error in AttackRoute test, iteration %d: %v\n", i, err)
|
fmt.Printf("unexpected error in AttackRoute test, iteration %d: %v\n", i, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
for _, stream := range vector.expectedStreams {
|
for _, stream := range test.expectedStreams {
|
||||||
foundStream := false
|
foundStream := false
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
||||||
@@ -162,6 +340,11 @@ func TestAttackRoute(t *testing.T) {
|
|||||||
assert.Equal(t, true, foundStream, "wrong streams parsed")
|
assert.Equal(t, true, foundStream, "wrong streams parsed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(vector.expectedStreams), len(results), "wrong streams parsed")
|
assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed")
|
||||||
|
curlerMock.AssertExpectations(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDotWrite(t *testing.T) {
|
||||||
|
assert.Equal(t, true, doNotWrite(nil, nil))
|
||||||
|
}
|
||||||
|
|||||||
+13
-6
@@ -8,12 +8,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/EtixLabs/cameradar"
|
"github.com/EtixLabs/cameradar"
|
||||||
|
|
||||||
|
curl "github.com/andelf/go-curl"
|
||||||
|
"github.com/fatih/color"
|
||||||
"github.com/gernest/wow"
|
"github.com/gernest/wow"
|
||||||
"github.com/gernest/wow/spin"
|
"github.com/gernest/wow/spin"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
@@ -92,6 +93,13 @@ func main() {
|
|||||||
|
|
||||||
w := startSpinner(options.EnableLogs)
|
w := startSpinner(options.EnableLogs)
|
||||||
|
|
||||||
|
err = curl.GlobalInit(curl.GLOBAL_ALL)
|
||||||
|
c := curl.EasyInit()
|
||||||
|
if err != nil || c == nil {
|
||||||
|
printErr(errors.New("libcurl initialization failed"))
|
||||||
|
}
|
||||||
|
defer curl.GlobalCleanup()
|
||||||
|
|
||||||
updateSpinner(w, "Loading dictionaries...", options.EnableLogs)
|
updateSpinner(w, "Loading dictionaries...", options.EnableLogs)
|
||||||
gopath := os.Getenv("GOPATH")
|
gopath := os.Getenv("GOPATH")
|
||||||
options.Credentials = strings.Replace(options.Credentials, "<GOPATH>", gopath, 1)
|
options.Credentials = strings.Replace(options.Credentials, "<GOPATH>", gopath, 1)
|
||||||
@@ -116,15 +124,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Most cameras will be accessed successfully with these two attacks
|
// Most cameras will be accessed successfully with these two attacks
|
||||||
|
|
||||||
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Attacking their routes...", options.EnableLogs)
|
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Attacking their routes...", options.EnableLogs)
|
||||||
streams, err = cmrdr.AttackRoute(streams, routes, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
|
streams, err = cmrdr.AttackRoute(c, streams, routes, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
|
||||||
if err != nil && len(streams) > 0 {
|
if err != nil && len(streams) > 0 {
|
||||||
printErr(err)
|
printErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Attacking their credentials...", options.EnableLogs)
|
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Attacking their credentials...", options.EnableLogs)
|
||||||
streams, err = cmrdr.AttackCredentials(streams, credentials, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
|
streams, err = cmrdr.AttackCredentials(c, streams, credentials, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
|
||||||
if err != nil && len(streams) > 0 {
|
if err != nil && len(streams) > 0 {
|
||||||
printErr(err)
|
printErr(err)
|
||||||
}
|
}
|
||||||
@@ -135,7 +142,7 @@ func main() {
|
|||||||
if stream.RouteFound == false || stream.CredentialsFound == false {
|
if stream.RouteFound == false || stream.CredentialsFound == false {
|
||||||
|
|
||||||
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Final attack...", options.EnableLogs)
|
updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Final attack...", options.EnableLogs)
|
||||||
streams, err = cmrdr.AttackRoute(streams, routes, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
|
streams, err = cmrdr.AttackRoute(c, streams, routes, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs)
|
||||||
if err != nil && len(streams) > 0 {
|
if err != nil && len(streams) > 0 {
|
||||||
printErr(err)
|
printErr(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package cmrdr
|
||||||
|
|
||||||
|
import (
|
||||||
|
curl "github.com/andelf/go-curl"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Curler is an interface that implements the CURL interface of the go-curl library
|
||||||
|
// Used for mocking
|
||||||
|
type Curler interface {
|
||||||
|
Setopt(opt int, param interface{}) error
|
||||||
|
Perform() error
|
||||||
|
Getinfo(info curl.CurlInfo) (interface{}, error)
|
||||||
|
}
|
||||||
+31
-31
@@ -37,7 +37,7 @@ func TestNmapRun(t *testing.T) {
|
|||||||
execCommand = fakeExecCommand
|
execCommand = fakeExecCommand
|
||||||
defer func() { execCommand = exec.Command }()
|
defer func() { execCommand = exec.Command }()
|
||||||
|
|
||||||
vectors := []struct {
|
testCases := []struct {
|
||||||
targets string
|
targets string
|
||||||
ports string
|
ports string
|
||||||
resultFilePath string
|
resultFilePath string
|
||||||
@@ -65,14 +65,14 @@ func TestNmapRun(t *testing.T) {
|
|||||||
expectedErrMsg: "invalid nmap speed value",
|
expectedErrMsg: "invalid nmap speed value",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, vector := range vectors {
|
for _, test := range testCases {
|
||||||
err := NmapRun(vector.targets, vector.ports, vector.resultFilePath, vector.nmapSpeed, vector.enableLogs)
|
err := NmapRun(test.targets, test.ports, test.resultFilePath, test.nmapSpeed, test.enableLogs)
|
||||||
if len(vector.expectedErrMsg) > 0 {
|
if len(test.expectedErrMsg) > 0 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fmt.Printf("unexpected success. expected error: %s\n", vector.expectedErrMsg)
|
fmt.Printf("unexpected success. expected error: %s\n", test.expectedErrMsg)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("unexpected error: %v\n", err)
|
fmt.Printf("unexpected error: %v\n", err)
|
||||||
@@ -107,7 +107,7 @@ func TestNmapParseResults(t *testing.T) {
|
|||||||
Port: 1337,
|
Port: 1337,
|
||||||
}
|
}
|
||||||
|
|
||||||
vectors := []struct {
|
testCases := []struct {
|
||||||
fileExists bool
|
fileExists bool
|
||||||
streamsXML *nmapResult
|
streamsXML *nmapResult
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ func TestNmapParseResults(t *testing.T) {
|
|||||||
fileExists: true,
|
fileExists: true,
|
||||||
},
|
},
|
||||||
// File exists
|
// File exists
|
||||||
// Two invalid streams, no error
|
// Two invalid targets, no error
|
||||||
{
|
{
|
||||||
fileExists: true,
|
fileExists: true,
|
||||||
expectedStreams: []Stream{invalidStreamNoPort, invalidStreamNoAddress},
|
expectedStreams: []Stream{invalidStreamNoPort, invalidStreamNoAddress},
|
||||||
@@ -261,11 +261,11 @@ func TestNmapParseResults(t *testing.T) {
|
|||||||
expectedErrMsg: "expected element type <nmaprun> but have <failure>",
|
expectedErrMsg: "expected element type <nmaprun> but have <failure>",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, vector := range vectors {
|
for i, test := range testCases {
|
||||||
filePath := "/tmp/cameradar_test_parse_results_" + fmt.Sprint(i) + ".xml"
|
filePath := "/tmp/cameradar_test_parse_results_" + fmt.Sprint(i) + ".xml"
|
||||||
|
|
||||||
// create file
|
// create file
|
||||||
if vector.fileExists {
|
if test.fileExists {
|
||||||
_, err := os.Create(filePath)
|
_, err := os.Create(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("could not create xml file for NmapParseResults: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
fmt.Printf("could not create xml file for NmapParseResults: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||||
@@ -273,10 +273,10 @@ func TestNmapParseResults(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// marshal and write
|
// marshal and write
|
||||||
if vector.streamsXML != nil {
|
if test.streamsXML != nil {
|
||||||
streams, err := xml.Marshal(vector.streamsXML)
|
streams, err := xml.Marshal(test.streamsXML)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("invalid streams for NmapParseResults: %v. iteration: %d. streams: %v\n", err, i, vector.streamsXML)
|
fmt.Printf("invalid targets for NmapParseResults: %v. iteration: %d. streams: %v\n", err, i, test.streamsXML)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,18 +295,18 @@ func TestNmapParseResults(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
results, err := NmapParseResults(filePath)
|
results, err := NmapParseResults(filePath)
|
||||||
if len(vector.expectedErrMsg) > 0 {
|
if len(test.expectedErrMsg) > 0 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fmt.Printf("unexpected success. expected error: %s\n", vector.expectedErrMsg)
|
fmt.Printf("unexpected success. expected error: %s\n", test.expectedErrMsg)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("unexpected error: %v\n", err)
|
fmt.Printf("unexpected error: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
for _, stream := range vector.expectedStreams {
|
for _, stream := range test.expectedStreams {
|
||||||
foundStream := false
|
foundStream := false
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
||||||
@@ -316,7 +316,7 @@ func TestNmapParseResults(t *testing.T) {
|
|||||||
assert.Equal(t, true, foundStream, "wrong streams parsed")
|
assert.Equal(t, true, foundStream, "wrong streams parsed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(vector.expectedStreams), len(results), "wrong streams parsed")
|
assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,7 +348,7 @@ func TestDiscover(t *testing.T) {
|
|||||||
Port: 1337,
|
Port: 1337,
|
||||||
}
|
}
|
||||||
|
|
||||||
vectors := []struct {
|
testCases := []struct {
|
||||||
targets string
|
targets string
|
||||||
ports string
|
ports string
|
||||||
resultFilePath string
|
resultFilePath string
|
||||||
@@ -526,7 +526,7 @@ func TestDiscover(t *testing.T) {
|
|||||||
enableLogs: false,
|
enableLogs: false,
|
||||||
},
|
},
|
||||||
// File exists
|
// File exists
|
||||||
// Two invalid streams, no error
|
// Two invalid targets, no error
|
||||||
{
|
{
|
||||||
fileExists: true,
|
fileExists: true,
|
||||||
expectedStreams: []Stream{invalidStreamNoPort, invalidStreamNoAddress},
|
expectedStreams: []Stream{invalidStreamNoPort, invalidStreamNoAddress},
|
||||||
@@ -642,11 +642,11 @@ func TestDiscover(t *testing.T) {
|
|||||||
enableLogs: false,
|
enableLogs: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, vector := range vectors {
|
for i, test := range testCases {
|
||||||
filePath := "/tmp/cameradar_test_discover_" + fmt.Sprint(i) + ".xml"
|
filePath := "/tmp/cameradar_test_discover_" + fmt.Sprint(i) + ".xml"
|
||||||
|
|
||||||
// create file
|
// create file
|
||||||
if vector.fileExists {
|
if test.fileExists {
|
||||||
_, err := os.Create(filePath)
|
_, err := os.Create(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("could not create xml file for Discover: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
fmt.Printf("could not create xml file for Discover: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||||
@@ -654,10 +654,10 @@ func TestDiscover(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// marshal and write
|
// marshal and write
|
||||||
if vector.streamsXML != nil {
|
if test.streamsXML != nil {
|
||||||
streams, err := xml.Marshal(vector.streamsXML)
|
streams, err := xml.Marshal(test.streamsXML)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("invalid streams for Discover: %v. iteration: %d. streams: %v\n", err, i, vector.streamsXML)
|
fmt.Printf("invalid targets for Discover: %v. iteration: %d. streams: %v\n", err, i, test.streamsXML)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -675,20 +675,20 @@ func TestDiscover(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err := Discover(vector.targets, vector.ports, filePath, vector.nmapSpeed, vector.enableLogs)
|
results, err := Discover(test.targets, test.ports, filePath, test.nmapSpeed, test.enableLogs)
|
||||||
|
|
||||||
if len(vector.expectedErrMsg) > 0 {
|
if len(test.expectedErrMsg) > 0 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fmt.Printf("unexpected success in Discover test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg)
|
fmt.Printf("unexpected success in Discover test, iteration %d. expected error: %s\n", i, test.expectedErrMsg)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("unexpected error in Discover test, iteration %d: %v\n", i, err)
|
fmt.Printf("unexpected error in Discover test, iteration %d: %v\n", i, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
for _, stream := range vector.expectedStreams {
|
for _, stream := range test.expectedStreams {
|
||||||
foundStream := false
|
foundStream := false
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
||||||
@@ -698,6 +698,6 @@ func TestDiscover(t *testing.T) {
|
|||||||
assert.Equal(t, true, foundStream, "wrong streams parsed")
|
assert.Equal(t, true, foundStream, "wrong streams parsed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.Equal(t, len(vector.expectedStreams), len(results), "wrong streams parsed")
|
assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-12
@@ -31,7 +31,7 @@ func TestReplace(t *testing.T) {
|
|||||||
Port: 1337,
|
Port: 1337,
|
||||||
}
|
}
|
||||||
|
|
||||||
vectors := []struct {
|
testCases := []struct {
|
||||||
streams []Stream
|
streams []Stream
|
||||||
newStream Stream
|
newStream Stream
|
||||||
|
|
||||||
@@ -45,10 +45,10 @@ func TestReplace(t *testing.T) {
|
|||||||
expectedStreams: []Stream{validStream1, validStream2, invalidStreamNoPortModified},
|
expectedStreams: []Stream{validStream1, validStream2, invalidStreamNoPortModified},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, vector := range vectors {
|
for _, test := range testCases {
|
||||||
streams := replace(vector.streams, vector.newStream)
|
streams := replace(test.streams, test.newStream)
|
||||||
|
|
||||||
for _, stream := range vector.streams {
|
for _, stream := range test.streams {
|
||||||
foundStream := false
|
foundStream := false
|
||||||
for _, result := range streams {
|
for _, result := range streams {
|
||||||
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
||||||
@@ -69,7 +69,7 @@ func TestGetCameraRTSPURL(t *testing.T) {
|
|||||||
Port: 1337,
|
Port: 1337,
|
||||||
}
|
}
|
||||||
|
|
||||||
vectors := []struct {
|
testCases := []struct {
|
||||||
stream Stream
|
stream Stream
|
||||||
|
|
||||||
expectedRTSPURL string
|
expectedRTSPURL string
|
||||||
@@ -81,9 +81,9 @@ func TestGetCameraRTSPURL(t *testing.T) {
|
|||||||
expectedRTSPURL: "rtsp://ullaakut:ba69897483886f0d2b0afb6345b76c0c@1.2.3.4:1337/cameradar.sdp",
|
expectedRTSPURL: "rtsp://ullaakut:ba69897483886f0d2b0afb6345b76c0c@1.2.3.4:1337/cameradar.sdp",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, vector := range vectors {
|
for _, test := range testCases {
|
||||||
output := GetCameraRTSPURL(vector.stream)
|
output := GetCameraRTSPURL(test.stream)
|
||||||
assert.Equal(t, vector.expectedRTSPURL, output, "wrong RTSP URL generated")
|
assert.Equal(t, test.expectedRTSPURL, output, "wrong RTSP URL generated")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ func TestGetCameraAdminPanelURL(t *testing.T) {
|
|||||||
Address: "1.2.3.4",
|
Address: "1.2.3.4",
|
||||||
}
|
}
|
||||||
|
|
||||||
vectors := []struct {
|
testCases := []struct {
|
||||||
stream Stream
|
stream Stream
|
||||||
|
|
||||||
expectedRTSPURL string
|
expectedRTSPURL string
|
||||||
@@ -104,8 +104,8 @@ func TestGetCameraAdminPanelURL(t *testing.T) {
|
|||||||
expectedRTSPURL: "http://1.2.3.4/",
|
expectedRTSPURL: "http://1.2.3.4/",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, vector := range vectors {
|
for _, test := range testCases {
|
||||||
output := GetCameraAdminPanelURL(vector.stream)
|
output := GetCameraAdminPanelURL(test.stream)
|
||||||
assert.Equal(t, vector.expectedRTSPURL, output, "wrong Admin Panel URL generated")
|
assert.Equal(t, test.expectedRTSPURL, output, "wrong Admin Panel URL generated")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+109
-17
@@ -16,7 +16,7 @@ func TestLoadCredentials(t *testing.T) {
|
|||||||
Passwords: []string{"12345", "root"},
|
Passwords: []string{"12345", "root"},
|
||||||
}
|
}
|
||||||
|
|
||||||
vectors := []struct {
|
testCases := []struct {
|
||||||
input []byte
|
input []byte
|
||||||
fileExists bool
|
fileExists bool
|
||||||
|
|
||||||
@@ -47,17 +47,17 @@ func TestLoadCredentials(t *testing.T) {
|
|||||||
input: []byte("{\"invalid\":\"json\"}"),
|
input: []byte("{\"invalid\":\"json\"}"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, vector := range vectors {
|
for i, test := range testCases {
|
||||||
filePath := "/tmp/cameradar_test_load_credentials_" + fmt.Sprint(i) + ".xml"
|
filePath := "/tmp/cameradar_test_load_credentials_" + fmt.Sprint(i) + ".xml"
|
||||||
// create file
|
// create file
|
||||||
if vector.fileExists {
|
if test.fileExists {
|
||||||
_, err := os.Create(filePath)
|
_, err := os.Create(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("could not create xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
fmt.Printf("could not create xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ioutil.WriteFile(filePath, vector.input, 0644)
|
err = ioutil.WriteFile(filePath, test.input, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("could not write xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
fmt.Printf("could not write xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -65,18 +65,18 @@ func TestLoadCredentials(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result, err := LoadCredentials(filePath)
|
result, err := LoadCredentials(filePath)
|
||||||
if len(vector.expectedErrMsg) > 0 {
|
if len(test.expectedErrMsg) > 0 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fmt.Printf("unexpected success in LoadCredentials test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg)
|
fmt.Printf("unexpected success in LoadCredentials test, iteration %d. expected error: %s\n", i, test.expectedErrMsg)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("unexpected error in LoadCredentials test, iteration %d: %v\n", i, err)
|
fmt.Printf("unexpected error in LoadCredentials test, iteration %d: %v\n", i, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
for _, expectedUsername := range vector.expectedOutput.Usernames {
|
for _, expectedUsername := range test.expectedOutput.Usernames {
|
||||||
foundUsername := false
|
foundUsername := false
|
||||||
for _, username := range result.Usernames {
|
for _, username := range result.Usernames {
|
||||||
if username == expectedUsername {
|
if username == expectedUsername {
|
||||||
@@ -85,7 +85,7 @@ func TestLoadCredentials(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assert.Equal(t, true, foundUsername, "wrong usernames parsed")
|
assert.Equal(t, true, foundUsername, "wrong usernames parsed")
|
||||||
}
|
}
|
||||||
for _, expectedPassword := range vector.expectedOutput.Passwords {
|
for _, expectedPassword := range test.expectedOutput.Passwords {
|
||||||
foundPassword := false
|
foundPassword := false
|
||||||
for _, password := range result.Passwords {
|
for _, password := range result.Passwords {
|
||||||
if password == expectedPassword {
|
if password == expectedPassword {
|
||||||
@@ -102,7 +102,7 @@ func TestLoadRoutes(t *testing.T) {
|
|||||||
routesJSONString := []byte("admin\nroot")
|
routesJSONString := []byte("admin\nroot")
|
||||||
validRoutes := Routes{"admin", "root"}
|
validRoutes := Routes{"admin", "root"}
|
||||||
|
|
||||||
vectors := []struct {
|
testCases := []struct {
|
||||||
input []byte
|
input []byte
|
||||||
fileExists bool
|
fileExists bool
|
||||||
|
|
||||||
@@ -127,17 +127,17 @@ func TestLoadRoutes(t *testing.T) {
|
|||||||
input: []byte(""),
|
input: []byte(""),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, vector := range vectors {
|
for i, test := range testCases {
|
||||||
filePath := "/tmp/cameradar_test_load_routes_" + fmt.Sprint(i) + ".xml"
|
filePath := "/tmp/cameradar_test_load_routes_" + fmt.Sprint(i) + ".xml"
|
||||||
// create file
|
// create file
|
||||||
if vector.fileExists {
|
if test.fileExists {
|
||||||
_, err := os.Create(filePath)
|
_, err := os.Create(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("could not create xml file for LoadRoutes: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
fmt.Printf("could not create xml file for LoadRoutes: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ioutil.WriteFile(filePath, vector.input, 0644)
|
err = ioutil.WriteFile(filePath, test.input, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("could not write xml file for LoadRoutes: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
fmt.Printf("could not write xml file for LoadRoutes: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -145,18 +145,18 @@ func TestLoadRoutes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result, err := LoadRoutes(filePath)
|
result, err := LoadRoutes(filePath)
|
||||||
if len(vector.expectedErrMsg) > 0 {
|
if len(test.expectedErrMsg) > 0 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fmt.Printf("unexpected success in LoadRoutes test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg)
|
fmt.Printf("unexpected success in LoadRoutes test, iteration %d. expected error: %s\n", i, test.expectedErrMsg)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message")
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("unexpected error in LoadRoutes test, iteration %d: %v\n", i, err)
|
fmt.Printf("unexpected error in LoadRoutes test, iteration %d: %v\n", i, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
for _, expectedRoute := range vector.expectedOutput {
|
for _, expectedRoute := range test.expectedOutput {
|
||||||
foundRoute := false
|
foundRoute := false
|
||||||
for _, route := range result {
|
for _, route := range result {
|
||||||
if route == expectedRoute {
|
if route == expectedRoute {
|
||||||
@@ -168,3 +168,95 @@ func TestLoadRoutes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseCredentialsFromString(t *testing.T) {
|
||||||
|
defaultCredentials := Credentials{
|
||||||
|
Usernames: []string{
|
||||||
|
"",
|
||||||
|
"admin",
|
||||||
|
"Admin",
|
||||||
|
"Administrator",
|
||||||
|
"root",
|
||||||
|
"supervisor",
|
||||||
|
"ubnt",
|
||||||
|
"service",
|
||||||
|
"Dinion",
|
||||||
|
"administrator",
|
||||||
|
"admin1",
|
||||||
|
},
|
||||||
|
Passwords: []string{
|
||||||
|
"",
|
||||||
|
"admin",
|
||||||
|
"9999",
|
||||||
|
"123456",
|
||||||
|
"pass",
|
||||||
|
"camera",
|
||||||
|
"1234",
|
||||||
|
"12345",
|
||||||
|
"fliradmin",
|
||||||
|
"system",
|
||||||
|
"jvc",
|
||||||
|
"meinsm",
|
||||||
|
"root",
|
||||||
|
"4321",
|
||||||
|
"111111",
|
||||||
|
"1111111",
|
||||||
|
"password",
|
||||||
|
"ikwd",
|
||||||
|
"supervisor",
|
||||||
|
"ubnt",
|
||||||
|
"wbox123",
|
||||||
|
"service",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
str string
|
||||||
|
expectedResult Credentials
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
str: "{\"usernames\":[\"\",\"admin\",\"Admin\",\"Administrator\",\"root\",\"supervisor\",\"ubnt\",\"service\",\"Dinion\",\"administrator\",\"admin1\"],\"passwords\":[\"\",\"admin\",\"9999\",\"123456\",\"pass\",\"camera\",\"1234\",\"12345\",\"fliradmin\",\"system\",\"jvc\",\"meinsm\",\"root\",\"4321\",\"111111\",\"1111111\",\"password\",\"ikwd\",\"supervisor\",\"ubnt\",\"wbox123\",\"service\"]}",
|
||||||
|
expectedResult: defaultCredentials,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
str: "{}",
|
||||||
|
expectedResult: Credentials{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
str: "{\"invalid_field\":42}",
|
||||||
|
expectedResult: Credentials{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
str: "not json",
|
||||||
|
expectedResult: Credentials{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
parsedCredentials, _ := ParseCredentialsFromString(test.str)
|
||||||
|
assert.Equal(t, test.expectedResult, parsedCredentials, "unexpected result, parse error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseRoutesFromString(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
str string
|
||||||
|
expectedResult Routes
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
str: "a\nb\nc",
|
||||||
|
expectedResult: []string{"a", "b", "c"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
str: "a",
|
||||||
|
expectedResult: []string{"a"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
str: "",
|
||||||
|
expectedResult: []string{""},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
parsedRoutes := ParseRoutesFromString(test.str)
|
||||||
|
assert.Equal(t, test.expectedResult, parsedRoutes, "unexpected result, parse error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user