Skip to content

Commit

Permalink
Cpp tests (#543)
Browse files Browse the repository at this point in the history
cpp: Add tests

* Rename c++ folder to cpp
* Add tests

---------

Co-authored-by: Philip Stadermann <[email protected]>
Co-authored-by: Fröhling, Maximilian <[email protected]>
  • Loading branch information
3 people authored Jul 30, 2024
1 parent 0619fd5 commit 0f538f7
Show file tree
Hide file tree
Showing 17 changed files with 263 additions and 13 deletions.
56 changes: 56 additions & 0 deletions .github/workflows/ci-cpp-toolchain.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: vaas-cpp-toolchain-ci
on:
push:
tags:
- "cpp-toolchain*"
paths:
- "cpp/Dockerfile.build"
- ".github/workflows/ci-cpp-toolchain.yaml"

jobs:
cpp-toolchain:
name: Build & Push C++ toolchain
permissions:
contents: read
packages: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
cpp/Dockerfile.build
sparse-checkout-cone-mode: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: set version
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/cpp-toolchain}" >> $GITHUB_ENV

- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/gdatasoftwareag/vaas/cpp-toolchain
tags: |
type=semver,pattern={{version}},value=${{ env.RELEASE_VERSION }}
type=semver,pattern={{major}}.{{minor}},value=${{ env.RELEASE_VERSION }}
type=semver,pattern={{major}},value=${{ env.RELEASE_VERSION }}
flavor: |
latest=auto
- name: login to ghcr.io/gdatasoftwareag
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ secrets.GHCR_IO_GDATASOFTWAREAG_USERNAME }}
password: ${{ secrets.GHCR_IO_GDATASOFTWAREAG_PASSWORD }}

- name: Build and push
uses: docker/build-push-action@v6
with:
context: "cpp/"
file: "cpp/Dockerfile.build"
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
85 changes: 85 additions & 0 deletions .github/workflows/ci-cpp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: vaas-cpp-ci
on:
push:
paths:
- "cpp/**"
- ".github/workflows/ci-cpp.yaml"
pull_request:
paths:
- "cpp/**"
- ".github/workflows/ci-cpp.yaml"
workflow_dispatch:
inputs:
environment:
type: choice
description: "Test environment"
options:
- production
- staging
- develop
default: "production"

env:
CLIENT_ID: ${{ secrets.CLIENT_ID }}
CLIENT_SECRET: ${{secrets.CLIENT_SECRET}}
VAAS_URL: "https://upload.production.vaas.gdatasecurity.de"
TOKEN_URL: "https://account.gdata.de/realms/vaas-production/protocol/openid-connect/token"
VAAS_CLIENT_ID: ${{ secrets.VAAS_CLIENT_ID }}
VAAS_USER_NAME: ${{ secrets.VAAS_USER_NAME }}
VAAS_PASSWORD: ${{secrets.VAAS_PASSWORD}}

jobs:
cpp-build:
name: Build & Test C++ SDK
runs-on: ubuntu-latest
container: ghcr.io/gdatasoftwareag/vaas/cpp-toolchain
steps:
- uses: actions/checkout@v4

- name: Scan for Viruses
uses: ./.github/actions/vaas-scan-action
with:
VAAS_CLIENT_ID: ${{ secrets.VAAS_SCAN_CLIENT_ID }}
VAAS_CLIENT_SECRET: ${{ secrets.VAAS_SCAN_CLIENT_SECRET }}

- name: set staging environment
if: (inputs.environment == 'staging' || (startsWith(github.ref, 'refs/tags/cpp') && endsWith(github.ref, '-beta')))
run: |
echo "CLIENT_ID=${{ secrets.STAGING_CLIENT_ID }}" >> $GITHUB_ENV
echo "CLIENT_SECRET=${{ secrets.STAGING_CLIENT_SECRET }}" >> $GITHUB_ENV
echo "VAAS_URL=https://upload.staging.vaas.gdatasecurity.de" >> $GITHUB_ENV
echo "TOKEN_URL=https://account-staging.gdata.de/realms/vaas-staging/protocol/openid-connect/token" >> $GITHUB_ENV
echo "VAAS_CLIENT_ID=${{ secrets.STAGING_VAAS_CLIENT_ID }}" >> $GITHUB_ENV
echo "VAAS_USER_NAME=${{ secrets.STAGING_VAAS_USER_NAME }}" >> $GITHUB_ENV
echo "VAAS_PASSWORD=${{ secrets.STAGING_VAAS_PASSWORD }}" >> $GITHUB_ENV
- name: set develop environment
if: (inputs.environment == 'develop' || (startsWith(github.ref, 'refs/tags/cpp') && endsWith(github.ref, '-alpha')))
run: |
echo "CLIENT_ID=${{ secrets.DEVELOP_CLIENT_ID }}" >> $GITHUB_ENV
echo "CLIENT_SECRET=${{ secrets.DEVELOP_CLIENT_SECRET }}" >> $GITHUB_ENV
echo "VAAS_URL=https://upload.develop.vaas.gdatasecurity.de" >> $GITHUB_ENV
echo "TOKEN_URL=https://account-staging.gdata.de/realms/vaas-develop/protocol/openid-connect/token" >> $GITHUB_ENV
echo "VAAS_CLIENT_ID=${{ secrets.DEVELOP_VAAS_CLIENT_ID }}" >> $GITHUB_ENV
echo "VAAS_USER_NAME=${{ secrets.DEVELOP_VAAS_USER_NAME }}" >> $GITHUB_ENV
echo "VAAS_PASSWORD=${{ secrets.DEVELOP_VAAS_PASSWORD }}" >> $GITHUB_ENV
# https://learn.microsoft.com/en-us/vcpkg/consume/binary-caching-github-actions-cache
- name: Export GitHub Actions cache environment variables
uses: actions/github-script@v7
with:
script: |
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Build C++ SDK
env:
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
run: |
./build.sh
working-directory: cpp

- name: Test C++ SDK
run: |
./test.sh
working-directory: cpp
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 8 additions & 1 deletion c++/CMakeLists.txt → cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ set(CMAKE_CXX_STANDARD 17)
# Find and link libraries
find_package(CURL REQUIRED)
find_package(jsoncpp CONFIG REQUIRED)
find_package(doctest CONFIG REQUIRED)

add_executable(vaas_example main.cpp vaas.h)
# vaas_example
add_executable(vaas_example main.cpp)

target_link_libraries(vaas_example PRIVATE CURL::libcurl JsonCpp::JsonCpp)

# test
add_executable(vaas_test vaas_test.cpp)

target_link_libraries(vaas_test PRIVATE CURL::libcurl JsonCpp::JsonCpp)
File renamed without changes.
16 changes: 16 additions & 0 deletions cpp/Dockerfile.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#### Base Image
FROM ubuntu:24.04

RUN apt-get update -qq && \
# install dependencies
apt-get install -y --no-install-recommends git ca-certificates curl zip unzip tar build-essential cmake make pkg-config libssl3 libssl-dev linux-libc-dev && \
# install vcpkg
cd /usr/local && \
git clone https://github.com/microsoft/vcpkg.git && \
cd vcpkg && ./bootstrap-vcpkg.sh

ENV VCPKG_ROOT="/usr/local/vcpkg/"
ENV PATH="$VCPKG_ROOT:$PATH"


ENTRYPOINT ["/bin/bash"]
File renamed without changes.
4 changes: 4 additions & 0 deletions cpp/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
set -ex
cmake --preset release
cmake --build build
File renamed without changes.
3 changes: 3 additions & 0 deletions cpp/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
set -ex
./build/vaas_test --exit=true
43 changes: 32 additions & 11 deletions c++/vaas.h → cpp/vaas.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ static size_t writeAppendToString(void* contents, const size_t size, const size_

static long getServerResponse(CURL* curl, Json::Value& jsonResponse) {
std::string response;
vaas_internals::ensureCurlOk(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, vaas_internals::writeAppendToString));
vaas_internals::ensureCurlOk(curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response));
ensureCurlOk(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, vaas_internals::writeAppendToString));
ensureCurlOk(curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response));

vaas_internals::ensureCurlOk(curl_easy_perform(curl));
ensureCurlOk(curl_easy_perform(curl));

long response_code;
vaas_internals::ensureCurlOk(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code));
ensureCurlOk(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code));

if (response_code < 200 || response_code >= 300) {
return response_code;
Expand Down Expand Up @@ -177,12 +177,20 @@ namespace vaas {
class OIDCClient {
public:
OIDCClient(std::string tokenEndpoint, std::string clientId, std::string clientSecret)
: tokenEndpoint(std::move(tokenEndpoint)), clientId(std::move(clientId)), clientSecret(std::move(clientSecret)), curl(curl_easy_init()) {
: tokenEndpoint(std::move(tokenEndpoint)), clientId(std::move(clientId)),
clientSecret(std::move(clientSecret)), curl(curl_easy_init()) {
if (!curl) {
throw std::runtime_error("Failed to initialize CURL");
}
}

OIDCClient(OIDCClient&& other) noexcept
: tokenEndpoint(std::move(tokenEndpoint)), clientId(std::move(clientId)),
clientSecret(std::move(clientSecret)),
curl(other.curl) {
other.curl = nullptr;
}

~OIDCClient() {
if (curl) {
curl_easy_cleanup(curl);
Expand All @@ -204,7 +212,8 @@ class OIDCClient {
vaas_internals::CurlHeaders headers;
headers.append("Content-Type: application/x-www-form-urlencoded");

const std::string postFields = "grant_type=client_credentials&client_id=" + clientId + "&client_secret=" + clientSecret;
const std::string postFields = "grant_type=client_credentials&client_id=" + clientId + "&client_secret=" +
clientSecret;

vaas_internals::ensureCurlOk(curl_easy_setopt(curl, CURLOPT_URL, tokenEndpoint.c_str()));
vaas_internals::ensureCurlOk(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers.raw()));
Expand All @@ -214,7 +223,8 @@ class OIDCClient {

const auto response_code = vaas_internals::getServerResponse(curl, jsonResponse);
if (!(response_code == 200 || response_code == 401)) {
throw AuthenticationException("Server replied with unexpected HTTP response code " + std::to_string(response_code));
throw AuthenticationException(
"Server replied with unexpected HTTP response code " + std::to_string(response_code));
}

if (jsonResponse.isMember("error") || response_code != 200) {
Expand Down Expand Up @@ -245,6 +255,7 @@ class OIDCClient {

/// VaasReport contains an analysis report for a file, such as verdict information.
class VaasReport {
public:
const std::string sha256;

enum Verdict {
Expand Down Expand Up @@ -278,7 +289,8 @@ class VaasReport {
}

explicit VaasReport(const std::string& sha256, Verdict verdict)
: sha256{sha256}, verdict{verdict} {}
: sha256{sha256}, verdict{verdict} {
}
};

inline std::ostream& operator<<(std::ostream& os, const VaasReport& report) {
Expand All @@ -289,13 +301,22 @@ inline std::ostream& operator<<(std::ostream& os, const VaasReport& report) {
/// Vaas talks to the VaaS service and provides reports for files or streams.
class Vaas {
public:
Vaas(std::string serverEndpoint, const std::string& tokenEndpoint, const std::string& clientId, const std::string& clientSecret)
: serverEndpoint(serverEndpoint), authenticator(tokenEndpoint, clientId, clientSecret), curl(curl_easy_init()) {
Vaas(const std::string& serverEndpoint, const std::string& tokenEndpoint, const std::string& clientId,
const std::string& clientSecret)
: serverEndpoint(serverEndpoint), authenticator(tokenEndpoint, clientId, clientSecret),
curl(curl_easy_init()) {
if (!curl) {
throw std::runtime_error("Failed to initialize CURL");
}
}

Vaas(Vaas&& other) noexcept
: serverEndpoint(std::move(other.serverEndpoint)), // Can't actually move because it's const
authenticator(std::move(other.authenticator)),
curl(other.curl) {
other.curl = nullptr;
}

~Vaas() {
if (curl) {
curl_easy_cleanup(curl);
Expand Down Expand Up @@ -411,4 +432,4 @@ class Vaas {
};

} // namespace vaas
#endif // VAAS_H
#endif // VAAS_H
57 changes: 57 additions & 0 deletions cpp/vaas_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#define DOCTEST_CONFIG_IMPLEMENT
#include "vaas.h"
#include <doctest/doctest.h>

static char* program;

int main(int argc, char** argv) {
program = argv[0];
doctest::Context context;
context.applyCommandLine(argc, argv);

int res = context.run(); // run doctest

// important - query flags (and --exit) rely on the user doing this
if (context.shouldExit()) {
// propagate the result of the tests
return res;
}

return 0;
}

vaas::Vaas initVaas() {
const auto vaasUrl = std::getenv("VAAS_URL")
? std::getenv("VAAS_URL")
: "https://upload.staging.vaas.gdatasecurity.de";
const auto tokenUrl = std::getenv("TOKEN_URL")
? std::getenv("TOKEN_URL")
: "https://account-staging.gdata.de/realms/vaas-staging/protocol/openid-connect/token";
const auto clientId = std::getenv("CLIENT_ID")
? std::getenv("CLIENT_ID")
: throw std::runtime_error("CLIENT_ID must be set");
const auto clientSecret = std::getenv("CLIENT_SECRET")
? std::getenv("CLIENT_SECRET")
: throw std::runtime_error("CLIENT_SECRET must be set");
return vaas::Vaas(vaasUrl, tokenUrl, clientId, clientSecret);
}

class VaasTestFixture {
protected:
vaas::Vaas vaas;

VaasTestFixture() : vaas(initVaas()) {
}
};

TEST_CASE_FIXTURE(VaasTestFixture, "forFile_withCleanFile_returnsClean") {
auto report = vaas.forFile(program);
CHECK(report.verdict == vaas::VaasReport::Verdict::Clean);
}

/* TODO: Currently broken
TEST_CASE_FIXTURE(VaasTestFixture, "forHash_withMaliciousFile_returnsMalicious") {
auto report = vaas.forHash("275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f");
CHECK(report.verdict == vaas::VaasReport::Verdict::Malicious);
}
*/
File renamed without changes.
3 changes: 2 additions & 1 deletion c++/vcpkg.json → cpp/vcpkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
]
},
"jsoncpp",
"openssl"
"openssl",
"doctest"
],
"version": "0.0.0",
"name": "vaas"
Expand Down

0 comments on commit 0f538f7

Please sign in to comment.