188 lines
7.0 KiB
Go
188 lines
7.0 KiB
Go
package scanners_test
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
|
|
ic "github.com/opencloud-eu/icap-client"
|
|
|
|
"github.com/qsfera/server/services/antivirus/pkg/scanners"
|
|
"github.com/qsfera/server/services/antivirus/pkg/scanners/mocks"
|
|
)
|
|
|
|
func TestICAP_Scan(t *testing.T) {
|
|
var (
|
|
earlyExitErr = errors.New("stop here")
|
|
testUrl = "icap://test"
|
|
client = mocks.NewScanner(t)
|
|
scanner = &scanners.ICAP{Client: client, URL: testUrl}
|
|
)
|
|
|
|
t.Run("it sends a OPTIONS request to determine details", func(t *testing.T) {
|
|
client.EXPECT().Do(mock.Anything).RunAndReturn(func(request ic.Request) (ic.Response, error) {
|
|
assert.Equal(t, ic.MethodOPTIONS, request.Method)
|
|
assert.Equal(t, testUrl, request.URL.String())
|
|
return ic.Response{}, earlyExitErr
|
|
}).Once()
|
|
|
|
_, err := scanner.Scan(scanners.Input{})
|
|
assert.ErrorIs(t, earlyExitErr, err) // we can exit early, just in case check the error to be identical to the early exit error
|
|
})
|
|
|
|
t.Run("it sends a REQMOD request with all the details", func(t *testing.T) {
|
|
|
|
t.Run("request with ContentLength", func(t *testing.T) {
|
|
t.Run("with size", func(t *testing.T) {
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{}, nil).Once()
|
|
|
|
client.EXPECT().Do(mock.Anything).RunAndReturn(func(request ic.Request) (ic.Response, error) {
|
|
assert.Equal(t, ic.MethodREQMOD, request.Method)
|
|
assert.Equal(t, testUrl, request.URL.String())
|
|
assert.EqualValues(t, 999, request.HTTPRequest.ContentLength)
|
|
return ic.Response{}, earlyExitErr
|
|
}).Once()
|
|
|
|
_, err := scanner.Scan(scanners.Input{Size: 999})
|
|
assert.ErrorIs(t, earlyExitErr, err)
|
|
})
|
|
|
|
t.Run("without size", func(t *testing.T) {
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{}, nil).Once()
|
|
|
|
client.EXPECT().Do(mock.Anything).RunAndReturn(func(request ic.Request) (ic.Response, error) {
|
|
assert.Equal(t, ic.MethodREQMOD, request.Method)
|
|
assert.Equal(t, testUrl, request.URL.String())
|
|
assert.EqualValues(t, 0, request.HTTPRequest.ContentLength)
|
|
return ic.Response{}, earlyExitErr
|
|
}).Once()
|
|
|
|
_, err := scanner.Scan(scanners.Input{})
|
|
assert.ErrorIs(t, earlyExitErr, err)
|
|
})
|
|
})
|
|
|
|
t.Run("request with Content-Type header", func(t *testing.T) {
|
|
t.Run("name contains known extension", func(t *testing.T) {
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{}, nil).Once()
|
|
|
|
client.EXPECT().Do(mock.Anything).RunAndReturn(func(request ic.Request) (ic.Response, error) {
|
|
assert.Equal(t, "application/pdf", request.HTTPRequest.Header.Get("Content-Type"))
|
|
return ic.Response{}, earlyExitErr
|
|
}).Once()
|
|
|
|
_, err := scanner.Scan(scanners.Input{Name: "report.pdf"})
|
|
assert.ErrorIs(t, earlyExitErr, err)
|
|
})
|
|
|
|
t.Run("name with unknown extension", func(t *testing.T) {
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{}, nil).Once()
|
|
|
|
client.EXPECT().Do(mock.Anything).RunAndReturn(func(request ic.Request) (ic.Response, error) {
|
|
assert.Equal(t, "application/octet-stream", request.HTTPRequest.Header.Get("Content-Type"))
|
|
return ic.Response{}, earlyExitErr
|
|
}).Once()
|
|
|
|
_, err := scanner.Scan(scanners.Input{Name: "report.unknown"})
|
|
assert.ErrorIs(t, earlyExitErr, err)
|
|
})
|
|
|
|
t.Run("name without extension", func(t *testing.T) {
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{}, nil).Once()
|
|
|
|
client.EXPECT().Do(mock.Anything).RunAndReturn(func(request ic.Request) (ic.Response, error) {
|
|
assert.Equal(t, "httpd/unix-directory", request.HTTPRequest.Header.Get("Content-Type"))
|
|
return ic.Response{}, earlyExitErr
|
|
}).Once()
|
|
|
|
_, err := scanner.Scan(scanners.Input{Name: "report"})
|
|
assert.ErrorIs(t, earlyExitErr, err)
|
|
})
|
|
})
|
|
|
|
t.Run("request with the OPTIONS response preview size ", func(t *testing.T) {
|
|
t.Run("with PreviewBytes set", func(t *testing.T) {
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{PreviewBytes: 444}, nil).Once()
|
|
|
|
client.EXPECT().Do(mock.Anything).RunAndReturn(func(request ic.Request) (ic.Response, error) {
|
|
assert.Equal(t, 444, request.PreviewBytes)
|
|
return ic.Response{}, earlyExitErr
|
|
}).Once()
|
|
|
|
_, err := scanner.Scan(scanners.Input{Body: bytes.NewReader(make([]byte, 888))})
|
|
assert.ErrorIs(t, earlyExitErr, err)
|
|
})
|
|
|
|
t.Run("without PreviewBytes set", func(t *testing.T) {
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{}, nil).Once()
|
|
|
|
client.EXPECT().Do(mock.Anything).RunAndReturn(func(request ic.Request) (ic.Response, error) {
|
|
assert.Equal(t, 0, request.PreviewBytes)
|
|
return ic.Response{}, earlyExitErr
|
|
}).Once()
|
|
|
|
_, err := scanner.Scan(scanners.Input{Body: bytes.NewReader(make([]byte, 888))})
|
|
assert.ErrorIs(t, earlyExitErr, err)
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("request with the OPTIONS response preview size ", func(t *testing.T) {
|
|
t.Run("with PreviewBytes set", func(t *testing.T) {
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{PreviewBytes: 444}, nil).Once()
|
|
|
|
client.EXPECT().Do(mock.Anything).RunAndReturn(func(request ic.Request) (ic.Response, error) {
|
|
assert.Equal(t, 444, request.PreviewBytes)
|
|
return ic.Response{}, earlyExitErr
|
|
}).Once()
|
|
|
|
_, err := scanner.Scan(scanners.Input{Body: bytes.NewReader(make([]byte, 888))})
|
|
assert.ErrorIs(t, earlyExitErr, err)
|
|
})
|
|
|
|
t.Run("it handles virus scan results", func(t *testing.T) {
|
|
t.Run("no virus", func(t *testing.T) {
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{}, nil).Once()
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{}, nil).Once()
|
|
|
|
result, err := scanner.Scan(scanners.Input{})
|
|
assert.Nil(t, err)
|
|
assert.False(t, result.Infected)
|
|
})
|
|
|
|
// clamav returns an X-Infection-Found header with the threat description
|
|
t.Run("X-Infection-Found header ", func(t *testing.T) {
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{}, nil).Once()
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{Header: http.Header{"X-Infection-Found": []string{"Threat=bad threat;"}}}, nil).Once()
|
|
|
|
result, err := scanner.Scan(scanners.Input{})
|
|
assert.Nil(t, err)
|
|
assert.True(t, result.Infected)
|
|
assert.Equal(t, "bad threat", result.Description)
|
|
})
|
|
|
|
// skyhigh returns the information via the content response
|
|
t.Run("X-Infection-Found header", func(t *testing.T) {
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{}, nil).Once()
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{ContentResponse: &http.Response{StatusCode: http.StatusForbidden, Status: "some status"}}, nil).Once()
|
|
|
|
result, err := scanner.Scan(scanners.Input{})
|
|
assert.Nil(t, err)
|
|
assert.True(t, result.Infected)
|
|
assert.Equal(t, "some status", result.Description)
|
|
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{}, nil).Once()
|
|
client.EXPECT().Do(mock.Anything).Return(ic.Response{ContentResponse: &http.Response{StatusCode: http.StatusOK}}, nil).Once()
|
|
|
|
result, err = scanner.Scan(scanners.Input{})
|
|
assert.Nil(t, err)
|
|
assert.False(t, result.Infected)
|
|
})
|
|
})
|
|
})
|
|
}
|