-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Golang HTTP API Implement v3 --------- Co-authored-by: Philip Stadermann <[email protected]> Co-authored-by: robin.hubbig <[email protected]> Co-authored-by: Verdict-as-a-Service Team <[email protected]> Co-authored-by: Kevin Heise <[email protected]>
- Loading branch information
1 parent
6a28bdf
commit e00a463
Showing
25 changed files
with
2,709 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
[![vaas-golang-ci](https://github.com/GDATASoftwareAG/vaas/actions/workflows/ci-golang.yaml/badge.svg)](https://github.com/GDATASoftwareAG/vaas/actions/workflows/ci-golang.yaml) | ||
[![Vulnerability Check](https://github.com/GDATASoftwareAG/vaas/actions/workflows/vulncheck-golang.yml/badge.svg)](https://github.com/GDATASoftwareAG/vaas/actions/workflows/vulncheck-golang.yml) | ||
[![Go Reference](https://pkg.go.dev/badge/github.com/GDATASoftwareAG/vaas/golang/vaas/.svg)](https://pkg.go.dev/github.com/GDATASoftwareAG/vaas/golang/vaas/) | ||
[![Go Report Card](https://goreportcard.com/badge/github.com/GDATASoftwareAG/vaas/golang/vaas)](https://goreportcard.com/report/github.com/GDATASoftwareAG/vaas/golang/vaas) | ||
|
||
# Go VaaS Client | ||
|
||
This is a Golang package that provides a client for the G DATA VaaS API. | ||
|
||
_Verdict-as-a-Service_ (VaaS) is a service that provides a platform for scanning files for malware and other threats. It allows easy integration into your application. With a few lines of code, you can start scanning files for malware. | ||
|
||
# Table of Contents | ||
|
||
- [What does the SDK do?](#what-does-the-sdk-do) | ||
- [How to use](#how-to-use) | ||
- [Installation](#installation) | ||
- [Import](#import) | ||
- [Authentication](#authentication) | ||
- [Client Credentials Grant](#client-credentials-grant) | ||
- [Resource Owner Password Grant](#resource-owner-password-grant) | ||
- [Request a verdict](#request-a-verdict) | ||
- [I'm interested in VaaS](#interested) | ||
- [Developing with Visual Studio Code](#developing-with-visual-studio-code) | ||
|
||
|
||
## What does the SDK do? | ||
|
||
It gives you as a developer functions to talk to G DATA VaaS. It wraps away the complexity of the API into basic functions. | ||
|
||
### Connect(ctx context.Context, auth authenticator.Authenticator) (errorChan <-chan error, err error) | ||
|
||
Connect opens a websocket connection to the VAAS Server. Use Close() to terminate the connection. The errorChan indicates when a connection was closed. In the case of an unexpected close, an error is written to the channel. | ||
|
||
### ForSha256(ctx context.Context, sha256 string) (messages.VaasVerdict, error) | ||
|
||
Retrieves the verdict for the given SHA256 hash from the G DATA VaaS API. `ctx` is the context for request cancellation, and `sha256` is the SHA256 hash of the file. If the request fails, an error will be returned. Otherwise, a `messages.VaasVerdict` object containing the verdict will be returned. | ||
|
||
### ForFile(ctx context.Context, filePath string) (messages.VaasVerdict, error) | ||
|
||
Retrieves the verdict for the given file at the specified `filePath` from the G DATA VaaS API. `ctx` is the context for request cancellation. If the file cannot be opened, an error will be returned. Otherwise, a `messages.VaasVerdict` object containing the verdict will be returned. | ||
|
||
### ForFileInMemory(ctx context.Context, fileData io.Reader) (messages.VaasVerdict, error) | ||
|
||
Retrieves the verdict for file data provided as an `io.Reader` to the G DATA VaaS API. `ctx` is the context for request cancellation. If the request fails, an error will be returned. Otherwise, a `messages.VaasVerdict` object containing the verdict will be returned. | ||
|
||
### ForUrl(ctx context.Context, url string) (messages.VaasVerdict, error) | ||
|
||
Retrieves the verdict for the given file URL from the G DATA VaaS API. `ctx` is the context for request cancellation. If the request fails, an error will be returned. Otherwise, a `messages.VaasVerdict` object containing the verdict will be returned. | ||
|
||
## How to use | ||
|
||
### Installation | ||
|
||
```sh | ||
go get github.com/GDATASoftwareAG/vaas/golang/vaas | ||
``` | ||
|
||
### Import | ||
|
||
```go | ||
import ( | ||
"github.com/GDATASoftwareAG/vaas/golang/vaas/pkg/authenticator" | ||
"github.com/GDATASoftwareAG/vaas/golang/vaas/pkg/vaas" | ||
) | ||
``` | ||
|
||
### Authentication | ||
|
||
VaaS offers two authentication methods: | ||
|
||
#### Client Credentials Grant | ||
This is suitable for cases where you have a `client_id`and `client_secret`. Here's how to use it: | ||
|
||
```go | ||
authenticator := authenticator.New("client_id", "client_secret", "token_endpoint") | ||
``` | ||
or | ||
```go | ||
authenticator := authenticator.NewWithDefaultTokenEndpoint("client_id", "client_secret") | ||
``` | ||
#### Resource Owner Password Grant | ||
This method is used when you have a `username` and `password`. Here's how to use it: | ||
|
||
```go | ||
authenticator := authenticator.NewWithResourceOwnerPassword("client_id", "username", "password", "token_endpoint") | ||
``` | ||
If you do not have a specific Client ID, please use `"vaas-customer"` as the client_id. | ||
|
||
### Request a verdict | ||
|
||
Authentication & Initialization: | ||
```go | ||
// Create a new authenticator with the provided Client ID and Client Secret | ||
auth := authenticator.NewWithDefaultTokenEndpoint(clientID, clientSecret) | ||
|
||
// Create a new VaaS client with default options | ||
vaasClient := vaas.NewWithDefaultEndpoint(options.VaasOptions{ | ||
UseHashLookup: true, | ||
UseCache: false, | ||
EnableLogs: false, | ||
}) | ||
|
||
// Create a context with a cancellation function | ||
ctx, webSocketCancel := context.WithCancel(context.Background()) | ||
|
||
// Establish a WebSocket connection to the VaaS server | ||
errorChan, err := vaasClient.Connect(ctx, auth) | ||
if err != nil { | ||
log.Fatalf("failed to connect to VaaS %s", err.Error()) | ||
} | ||
defer vaasClient.Close() | ||
|
||
// Create a context with a timeout for the analysis | ||
analysisCtx, analysisCancel := context.WithTimeout(context.Background(), 20*time.Second) | ||
defer analysisCancel() | ||
``` | ||
|
||
Verdict Request for SHA256: | ||
```go | ||
// Request a verdict for a specific SHA256 hash (replace "sha256-hash" with the actual SHA256 hash) | ||
result, err := vaasClient.ForFile(analysisCtx, "sha256-hash") | ||
if err != nil { | ||
log.Fatalf("Failed to get verdict: %v", err) | ||
} | ||
fmt.Println(result.Verdict) | ||
``` | ||
|
||
Verdict Request for a file: | ||
```go | ||
// Request a verdict for a specific file (replace "path-to-your-file" with the actual file path) | ||
result, err := vaasClient.ForFile(analysisCtx, "path-to-your-file") | ||
if err != nil { | ||
log.Fatalf("Failed to get verdict: %v", err) | ||
} | ||
fmt.Printf("Verdict: %s\n", result.Verdict) | ||
``` | ||
|
||
Verdict Request for file data provided as an io.Reader: | ||
```go | ||
fileData := bytes.NewReader([]byte("file contents")) | ||
result, err := vaasClient.ForFileInMemory(analysisCtx, fileData) | ||
if err != nil { | ||
log.Fatalf("Failed to get verdict: %v", err) | ||
} | ||
fmt.Printf("Verdict: %s\n", result.Verdict) | ||
``` | ||
|
||
Verdict Request for a file URL: | ||
```go | ||
result, err := vaasClient.ForUrl(analysisCtx, "https://example.com/examplefile") | ||
if err != nil { | ||
log.Fatalf("Failed to get verdict: %v", err) | ||
} | ||
fmt.Printf("Verdict: %s\n", result.Verdict) | ||
``` | ||
|
||
|
||
## <a name="interested"></a>I'm interested in VaaS | ||
|
||
You need credentials to use the service in your application. If you are interested in using VaaS, please [contact us](mailto:[email protected]). | ||
|
||
## Developing with Visual Studio Code | ||
|
||
Every single SDKs also includes [Devcontainer](./.devcontainer/). If you use the [Visual Studio Code Dev Containers extension](https://code.visualstudio.com/docs/devcontainers/containers), you can run the code in a full-featured development environment. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"log" | ||
"net/url" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/GDATASoftwareAG/vaas/golang/vaas/v3/pkg/authenticator" | ||
"github.com/GDATASoftwareAG/vaas/golang/vaas/v3/pkg/messages" | ||
"github.com/GDATASoftwareAG/vaas/golang/vaas/v3/pkg/vaas" | ||
) | ||
|
||
func main() { | ||
if len(os.Args) < 3 { | ||
log.Fatal("need 2 parameter: remote, targetBranch") | ||
} | ||
|
||
remote := os.Args[1] | ||
if remote == "" { | ||
log.Fatal("no remote set") | ||
} | ||
log.Println("remote:", remote) | ||
targetBranch := os.Args[2] | ||
if targetBranch == "" { | ||
log.Fatal("no targetBranch set") | ||
} | ||
log.Println("targetBranch:", targetBranch) | ||
|
||
vaasAuthenticator, credentialsError := getAuthenticator( | ||
os.Getenv("VAAS_CLIENT_ID"), os.Getenv("VAAS_CLIENT_SECRET"), os.Getenv("VAAS_USERAME"), os.Getenv("VAAS_PASSWORD")) | ||
if credentialsError != nil { | ||
log.Fatal(credentialsError) | ||
} | ||
|
||
vaasURLString, exists := os.LookupEnv("VAAS_URL") | ||
if !exists { | ||
vaasURLString = "https://gateway.production.vaas.gdatasecurity.de" | ||
} | ||
vaasURL, err := url.Parse(vaasURLString) | ||
if err != nil { | ||
log.Fatal("VAAS_URL is not an URL") | ||
} | ||
log.Println("vaas url:", vaasURL) | ||
|
||
gitRevParseCommand := exec.Command("git", "rev-parse", "--show-toplevel") | ||
rootDirectoryBytes, err := gitRevParseCommand.CombinedOutput() | ||
if err != nil { | ||
log.Fatal("git rev-parse: ", err, " ", string(rootDirectoryBytes)) | ||
} | ||
rootDirectory := strings.Split(strings.ReplaceAll(string(rootDirectoryBytes), "\r\n", "\n"), "\n")[0] | ||
log.Println("repository root directory: ", rootDirectory) | ||
|
||
fetchBytesCommand := exec.Command("git", "fetch", remote, targetBranch) | ||
fetchBytes, err := fetchBytesCommand.CombinedOutput() | ||
if err != nil { | ||
log.Fatal("git fetch ", err, " ", string(fetchBytes)) | ||
} | ||
log.Println("fetch result: ", string(fetchBytes)) | ||
|
||
gitDiffCommand := exec.Command("git", "diff", "--name-only", remote+"/"+targetBranch) | ||
diffBytes, err := gitDiffCommand.CombinedOutput() | ||
if err != nil { | ||
log.Fatal("git diff ", err, " ", string(diffBytes)) | ||
} | ||
files := strings.Split(strings.ReplaceAll(string(diffBytes), "\r\n", "\n"), "\n") | ||
if len(files) < 1 { | ||
log.Println("no changed files found in diff") | ||
os.Exit(0) | ||
} | ||
|
||
vaas := vaas.New(vaasURL, vaasAuthenticator) | ||
ctx, webSocketCancel := context.WithCancel(context.Background()) | ||
var maliciousFileFound bool | ||
for _, file := range files { | ||
if file == "" { | ||
continue | ||
} | ||
if _, err := os.Stat(file); err != nil { | ||
continue | ||
} | ||
log.Println("checking file: ", file) | ||
pathToFile := filepath.Join(rootDirectory, file) | ||
verdict, err := vaas.ForFile(ctx, pathToFile, nil) | ||
if err != nil { | ||
log.Fatalln(err) | ||
} | ||
log.Println(pathToFile + ": " + string(verdict.Verdict)) | ||
if verdict.Verdict == messages.Malicious { | ||
maliciousFileFound = true | ||
} | ||
} | ||
webSocketCancel() | ||
if maliciousFileFound { | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func getAuthenticator(clientId, clientSecret, username, password string) (vaasAuthenticator authenticator.Authenticator, credentialsError error) { | ||
tokenUrl, exists := os.LookupEnv("VAAS_TOKEN_URL") | ||
if !exists { | ||
tokenUrl = "https://account.gdata.de/realms/vaas-production/protocol/openid-connect/token" | ||
} | ||
log.Println("token url:", tokenUrl) | ||
|
||
if (clientId != "" && clientSecret != "") || (username != "" && password != "") { | ||
if username != "" && password != "" { | ||
vaasAuthenticator = authenticator.NewWithResourceOwnerPassword(username, password, "vaas-github-actions", tokenUrl) | ||
} else { | ||
vaasAuthenticator = authenticator.New(clientId, clientSecret, tokenUrl) | ||
} | ||
|
||
return | ||
|
||
} | ||
return nil, errors.New("you either need VAAS_CLIENT_ID and VAAS_CLIENT_SECRET or VAAS_USERAME and VAAS_PASSWORD") | ||
|
||
} |
Oops, something went wrong.