Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds for stream method #367

Merged
merged 2 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion golang/vaas/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,23 @@ require (
)

require (
github.com/Noooste/fhttp v1.0.6 // indirect
github.com/Noooste/fhttp v1.0.8 // indirect
github.com/Noooste/utls v1.2.5 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect
github.com/klauspost/compress v1.17.6 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/onsi/ginkgo/v2 v2.15.0 // indirect
github.com/onsi/gomega v1.30.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/quic-go v0.41.0 // indirect
github.com/refraction-networking/utls v1.6.2 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.18.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
28 changes: 14 additions & 14 deletions golang/vaas/go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
github.com/Noooste/fhttp v1.0.6 h1:E1u8b+GMhRZSuoINNpiXjE1MHUdZMIcs/g4HEjapWLg=
github.com/Noooste/fhttp v1.0.6/go.mod h1:7rH441v5BuOAQ60LPj7Uwinew1bmv7A0q8DryN/YA6s=
github.com/Noooste/fhttp v1.0.8 h1:iLSM75L7SInEirfdvwJUrUd/Y3AeF1LwpMuOQMM0zEg=
github.com/Noooste/fhttp v1.0.8/go.mod h1:CMVxKOhNheqJN5HYE4Rlvz2SRdV8Uv7YWmi6OwmB/Bk=
github.com/Noooste/utls v1.2.5 h1:x7ye66hXXeeMju2redAUSQ5IZBVpTMqX0/C5dHPLpUA=
github.com/Noooste/utls v1.2.5/go.mod h1:MRUEmRiDO6ORKziZ2ObNwMjxy0vRviJ91JF1qVa0loM=
github.com/Noooste/websocket v1.0.3 h1:drW7tvZ3YqzqI9wApnaH1Q0syFMXO7gbLlsBWjZvMNA=
Expand All @@ -11,14 +11,14 @@ github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBS
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
Expand All @@ -29,10 +29,10 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand All @@ -52,8 +52,8 @@ golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
45 changes: 45 additions & 0 deletions golang/vaas/pkg/messages/verdict_request_for_stream.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Package messages provides structures for handling communication messages between the client and the VaaS server.
package messages

import (
"github.com/GDATASoftwareAG/vaas/golang/vaas/pkg/options"
"github.com/google/uuid"
)

// VerdictRequestForURL is a specific implementation of VerdictRequest used for URL analysis requests.
type verdictRequestForStream struct {
Kind Kind `json:"kind" default:"VerdictRequestForStream"`
GUID string `json:"guid"`
SessionID string `json:"session_id"`
VerdictRequestAttributes VerdictRequestAttributes `json:"verdict_request_attributes"`
UseCache bool `json:"use_cache"`
UseHashLookup bool `json:"use_shed"`
}

// GetGUID returns the GUID of the verdictRequestForURL.
func (r verdictRequestForStream) GetGUID() string {
return r.GUID
}

// NewVerdictRequestForURL creates a new verdictRequestForURL instance.
func NewVerdictRequestForStream(sessionID string, options options.VaasOptions) VerdictRequest {
return verdictRequestForStream{
Kind: VerdictRequestForStreamKind,
SessionID: sessionID,
GUID: uuid.New().String(),
UseCache: options.UseCache,
UseHashLookup: options.UseHashLookup,
}
}

// NewVerdictRequestForURLWithAttributes creates a new verdictRequestForURL instance with attributes.
func NewVerdictRequestForStreamWithAttributes(sessionID string, options options.VaasOptions, attributes VerdictRequestAttributes) VerdictRequest {
return verdictRequestForStream{
Kind: VerdictRequestForStreamKind,
SessionID: sessionID,
GUID: uuid.New().String(),
UseCache: options.UseCache,
UseHashLookup: options.UseHashLookup,
VerdictRequestAttributes: attributes,
}
}
5 changes: 3 additions & 2 deletions golang/vaas/pkg/messages/verdict_request_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ type Kind string

// VerdictRequestKind and VerdictRequestForURLKind are kinds of VerdictRequest.
const (
VerdictRequestKind Kind = "VerdictRequest"
VerdictRequestForURLKind Kind = "VerdictRequestForUrl"
VerdictRequestKind Kind = "VerdictRequest"
VerdictRequestForURLKind Kind = "VerdictRequestForUrl"
VerdictRequestForStreamKind Kind = "VerdictRequestForStream"
)

// VerdictRequest is an interface for various types of verdict requests.
Expand Down
96 changes: 83 additions & 13 deletions golang/vaas/pkg/vaas/vaas.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
"strings"

"io"
"log"
Expand All @@ -30,6 +31,7 @@ import (
type Vaas interface {
Connect(ctx context.Context, auth authenticator.Authenticator) (termChan <-chan error, err error)
ForUrl(ctx context.Context, uri string) (msg.VaasVerdict, error)
ForStream(ctx context.Context, stream io.Reader, contentLength int64) (msg.VaasVerdict, error)
ForSha256(ctx context.Context, sha256 string) (msg.VaasVerdict, error)
ForFile(ctx context.Context, path string) (msg.VaasVerdict, error)
ForFileInMemory(ctx context.Context, file io.Reader) (msg.VaasVerdict, error)
Expand Down Expand Up @@ -355,6 +357,62 @@ func (v *vaas) ForUrl(ctx context.Context, url string) (msg.VaasVerdict, error)
}, nil
}

// ForStream sends an analysis request for a file stream to the Vaas server and returns the verdict.
// The analysis can be canceled using the provided context.
//
// Example usage:
//
// vaasClient := vaas.New(options, "wss://example.authentication.endpoint")
// ctx := context.Background()
// verdict, err := vaasClient.ForStream(ctx, stream)
// if err != nil {
// log.Fatalf("Failed to get verdict: %v", err)
// }
// fmt.Printf("Verdict: %s\n", verdict.Verdict)
// fmt.Printf("SHA256: %s\n", verdict.Sha256)
func (v *vaas) ForStream(ctx context.Context, stream io.Reader, contentLength int64) (msg.VaasVerdict, error) {
if v.sessionID == "" {
return msg.VaasVerdict{}, errors.New("invalid operation")
}

request := msg.NewVerdictRequestForStream(v.sessionID, v.options)

responseChan := v.openRequest(request)
defer v.closeRequest(request)

var response msg.VerdictResponse
select {
case response = <-responseChan:
case <-ctx.Done():
return msg.VaasVerdict{}, ctx.Err()
}

if response.Verdict != "" && response.Verdict != msg.Unknown {
return msg.VaasVerdict{}, errors.New("server returned verdict without receiving content")
}

if len(strings.TrimSpace(response.UploadToken)) == 0 {
return msg.VaasVerdict{}, errors.New("verdictResponse missing UploadToken for stream upload")
}

if len(strings.TrimSpace(response.URL)) == 0 {
return msg.VaasVerdict{}, errors.New("verdictResponse missing URL for stream upload")
}

if err := v.uploadFile(stream, contentLength, response.URL, response.UploadToken); err != nil {
return msg.VaasVerdict{
Verdict: msg.Error,
ErrMsg: err.Error(),
}, err
}
response = <-responseChan

return msg.VaasVerdict{
Verdict: response.Verdict,
Sha256: response.Sha256,
}, nil
}

func (v *vaas) authenticate(ctx context.Context, auth authenticator.Authenticator) error {
v.waitAuthenticated.Add(1)
defer v.waitAuthenticated.Done()
Expand Down Expand Up @@ -419,7 +477,7 @@ func (v *vaas) forFileWithSha(ctx context.Context, data io.Reader, sha256 string
}

if response.Verdict == msg.Unknown {
if err := v.uploadFile(data, response.URL, response.UploadToken); err != nil {
if err := v.uploadFile(data, 0, response.URL, response.UploadToken); err != nil {
return msg.VaasVerdict{
Verdict: msg.Error,
Sha256: sha256,
Expand Down Expand Up @@ -461,24 +519,36 @@ func (v *vaas) closeRequest(request msg.VerdictRequest) {
v.openRequestsMutex.Unlock()
}

func (v *vaas) uploadFile(file io.Reader, url string, token string) error {
func (v *vaas) uploadFile(file io.Reader, contentLength int64, url string, token string) error {
req, err := http.NewRequest(http.MethodPut, url, file)
if err != nil {
return err
}

// VAAS requires a set Content-Length.
// Here can add support for various io.Reader, which are not supported by the http package.
if req.ContentLength == 0 {
switch t := file.(type) {
case *os.File:
var info os.FileInfo
if info, err = t.Stat(); err != nil {
return err
if contentLength > 0 {
req.ContentLength = contentLength
} else {
// VAAS requires a set Content-Length.
// Here can add support for various io.Reader, which are not supported by the http package.
if req.ContentLength == 0 {
switch t := file.(type) {
case *os.File:
var info os.FileInfo
if info, err = t.Stat(); err != nil {
return err
}
req.ContentLength = info.Size()
case io.ReadCloser:
if s, ok := file.(io.Seeker); ok {
if size, err := s.Seek(0, io.SeekEnd); err == nil {
if _, err = s.Seek(0, io.SeekStart); err == nil {
req.ContentLength = size
}
}
}
default:
return fmt.Errorf("unsupported reader (%T), can not determine content length", file)
}
req.ContentLength = info.Size()
default:
return fmt.Errorf("unsupported reader (%T), can not determine content length", file)
}
}

Expand Down
38 changes: 37 additions & 1 deletion golang/vaas/pkg/vaas/vaas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io"
"log"
"math/rand"
"net/http"
"os"
"path/filepath"
"strings"
Expand All @@ -16,8 +17,8 @@ import (
"github.com/joho/godotenv"
"github.com/stretchr/testify/assert"

msg "github.com/GDATASoftwareAG/vaas/golang/vaas/pkg/messages"
"github.com/GDATASoftwareAG/vaas/golang/vaas/pkg/authenticator"
msg "github.com/GDATASoftwareAG/vaas/golang/vaas/pkg/messages"
"github.com/GDATASoftwareAG/vaas/golang/vaas/pkg/options"
)

Expand Down Expand Up @@ -286,6 +287,41 @@ func TestVaas_ForFile_And_ForFileInMemory(t *testing.T) {
}
}

func TestVaas_ForStream_WithStreamFromString(t *testing.T) {
eicarReader := strings.NewReader("X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*")
fixture := new(testFixture)
VaasClient := fixture.setUp(t)
defer fixture.tearDown(t)

verdict, err := VaasClient.ForStream(context.Background(), eicarReader, eicarReader.Size())

if err != nil {
t.Fatalf("unexpected error - %v", err)
}

if verdict.Verdict != msg.Malicious {
t.Errorf("verdict should be %v, got %v", msg.Malicious, verdict.Verdict)
}
}

func TestVaas_ForStream_WithStreamFromUrl(t *testing.T) {
response, _ := http.Get("https://secure.eicar.org/eicar.com.txt")

fixture := new(testFixture)
VaasClient := fixture.setUp(t)
defer fixture.tearDown(t)

verdict, err := VaasClient.ForStream(context.Background(), response.Body, response.ContentLength)

if err != nil {
t.Fatalf("unexpected error - %v", err)
}

if verdict.Verdict != msg.Malicious {
t.Errorf("verdict should be %v, got %v", msg.Malicious, verdict.Verdict)
}
}

func TestVaas_ForUrl(t *testing.T) {
const (
cleanURL string = "https://random-data-api.com/api/v2/beers"
Expand Down
Loading