mirror of
https://github.com/kubescape/kubescape.git
synced 2026-04-15 06:58:11 +00:00
Merge pull request #1131 from fredbi/test/more-tests-report-receiver
test(reports): adds unit test to the report receiver
This commit is contained in:
@@ -1,10 +1,37 @@
|
||||
package reporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/reporter"
|
||||
"github.com/kubescape/opa-utils/reporthandling"
|
||||
"github.com/kubescape/opa-utils/reporthandling/apis"
|
||||
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/prioritization"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
|
||||
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestReportMock_GetURL(t *testing.T) {
|
||||
func TestReportMockGetURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type fields struct {
|
||||
query string
|
||||
message string
|
||||
@@ -31,15 +58,44 @@ func TestReportMock_GetURL(t *testing.T) {
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
reportMock := &ReportMock{
|
||||
query: tt.fields.query,
|
||||
message: tt.fields.message,
|
||||
}
|
||||
if got := reportMock.GetURL(); got != tt.want {
|
||||
t.Errorf("ReportMock.GetURL() = %v, want %v", got, tt.want)
|
||||
}
|
||||
|
||||
for _, toPin := range tests {
|
||||
tc := toPin
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var reportMock reporter.IReport = NewReportMock(tc.fields.query, tc.fields.message)
|
||||
|
||||
t.Run("mock reports should support GetURL", func(t *testing.T) {
|
||||
got := reportMock.GetURL()
|
||||
require.Equalf(t, tc.want, got,
|
||||
"ReportMock.GetURL() = %v, want %v", got, tc.want,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("mock reports should support DisplayReportURL", func(t *testing.T) {
|
||||
capture, clean := captureStderr(t)
|
||||
defer clean()
|
||||
|
||||
reportMock.DisplayReportURL()
|
||||
require.NoError(t, capture.Close())
|
||||
|
||||
buf, err := os.ReadFile(capture.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
if tc.fields.message != "" {
|
||||
require.NotEmpty(t, buf)
|
||||
} else {
|
||||
require.Empty(t, buf)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("mock reports should support Submit", func(t *testing.T) {
|
||||
require.NoError(t,
|
||||
reportMock.Submit(context.Background(), &cautils.OPASessionObj{}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -83,3 +139,219 @@ func TestReportMock_strToDisplay(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const pathTestReport = "/k8s/v2/postureReport"
|
||||
|
||||
type (
|
||||
// mockableOPASessionObj reproduces OPASessionObj with concrete types instead of interfaces.
|
||||
// It may be unmarshaled from a JSON fixture.
|
||||
mockableOPASessionObj struct {
|
||||
K8SResources *cautils.K8SResources
|
||||
ArmoResource *cautils.KSResources
|
||||
AllPolicies *cautils.Policies
|
||||
AllResources map[string]*workloadinterface.Workload
|
||||
ResourcesResult map[string]resourcesresults.Result
|
||||
ResourceSource map[string]reporthandling.Source
|
||||
ResourcesPrioritized map[string]prioritization.PrioritizedResource
|
||||
ResourceAttackTracks map[string]*v1alpha1.AttackTrack
|
||||
AttackTracks map[string]*v1alpha1.AttackTrack
|
||||
Report *reporthandlingv2.PostureReport
|
||||
RegoInputData cautils.RegoInputData
|
||||
Metadata *reporthandlingv2.Metadata
|
||||
InfoMap map[string]apis.StatusInfo
|
||||
ResourceToControlsMap map[string][]string
|
||||
SessionID string
|
||||
Policies []reporthandling.Framework
|
||||
Exceptions []armotypes.PostureExceptionPolicy
|
||||
OmitRawResources bool
|
||||
}
|
||||
|
||||
// testServer wraps a mock http server.
|
||||
//
|
||||
// It exposes a route to POST reports and asserts the submitted requests.
|
||||
testServer struct {
|
||||
*httptest.Server
|
||||
}
|
||||
|
||||
// interceptor is a http.RoundTripper used to re-route the calls to the mock API server.
|
||||
//
|
||||
// NOTE(fredbi): ideally, the target URL is configurable so we don't need to resort to this to run tests.
|
||||
interceptor struct {
|
||||
original http.RoundTripper
|
||||
host string
|
||||
}
|
||||
)
|
||||
|
||||
// mockOPASessionObj builds an OPASessionObj from a JSON fixture.
|
||||
func mockOPASessionObj(t testing.TB) *cautils.OPASessionObj {
|
||||
buf, err := os.ReadFile(filepath.Join(currentDir(), "testdata", "mock_opasessionobj.json"))
|
||||
require.NoError(t, err)
|
||||
|
||||
var v mockableOPASessionObj
|
||||
require.NoError(t,
|
||||
json.Unmarshal(buf, &v),
|
||||
)
|
||||
|
||||
o := cautils.OPASessionObj{
|
||||
K8SResources: v.K8SResources,
|
||||
ArmoResource: v.ArmoResource,
|
||||
AllPolicies: v.AllPolicies,
|
||||
//AllResources map[string]*workloadinterface.Workload // all scanned resources, map[<resource ID>]<resource>
|
||||
ResourcesResult: v.ResourcesResult,
|
||||
ResourceSource: v.ResourceSource,
|
||||
ResourcesPrioritized: v.ResourcesPrioritized,
|
||||
//ResourceAttackTracks map[string]*v1alpha1.AttackTrack // resources attack tracks, map[<resource ID>]<attack track>
|
||||
//AttackTracks map[string]*v1alpha1.AttackTrack
|
||||
Report: v.Report,
|
||||
RegoInputData: v.RegoInputData,
|
||||
Metadata: v.Metadata,
|
||||
InfoMap: v.InfoMap,
|
||||
ResourceToControlsMap: v.ResourceToControlsMap,
|
||||
SessionID: v.SessionID,
|
||||
Policies: v.Policies,
|
||||
Exceptions: v.Exceptions,
|
||||
OmitRawResources: v.OmitRawResources,
|
||||
}
|
||||
|
||||
o.AllResources = make(map[string]workloadinterface.IMetadata, len(v.AllResources))
|
||||
for k, val := range v.AllResources {
|
||||
o.AllResources[k] = val
|
||||
}
|
||||
|
||||
o.ResourceAttackTracks = make(map[string]v1alpha1.IAttackTrack, len(v.ResourceAttackTracks))
|
||||
for k, val := range v.ResourceAttackTracks {
|
||||
o.ResourceAttackTracks[k] = val
|
||||
}
|
||||
|
||||
o.AttackTracks = make(map[string]v1alpha1.IAttackTrack, len(v.AttackTracks))
|
||||
for k, val := range v.AttackTracks {
|
||||
o.AttackTracks[k] = val
|
||||
}
|
||||
|
||||
return &o
|
||||
}
|
||||
|
||||
func (s *testServer) Root() string {
|
||||
return s.Server.URL
|
||||
}
|
||||
|
||||
func (s *testServer) URL(pth string) string {
|
||||
pth = strings.TrimLeft(pth, "/")
|
||||
|
||||
return fmt.Sprintf("%s/%s", s.Server.URL, pth)
|
||||
}
|
||||
|
||||
// mockAPIServer builds a mock API running with a TLS endpoint.
|
||||
//
|
||||
// Running tests with the DEBUG_TEST=1 environment will result in dumping a trace of
|
||||
// the incoming requests.
|
||||
func mockAPIServer(t testing.TB) *testServer {
|
||||
h := http.NewServeMux()
|
||||
|
||||
server := &testServer{
|
||||
Server: httptest.NewUnstartedServer(h),
|
||||
}
|
||||
|
||||
h.HandleFunc(pathTestReport, func(w http.ResponseWriter, r *http.Request) {
|
||||
if os.Getenv("DEBUG_TEST") != "" {
|
||||
dump, _ := httputil.DumpRequest(r, true)
|
||||
t.Logf("%s\n", dump)
|
||||
}
|
||||
|
||||
if !assert.Equal(t, http.MethodPost, r.Method) {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.NoErrorf(t, r.ParseForm(), "expected params to parse") {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
cluster := r.Form.Get("clusterName")
|
||||
contextName := r.Form.Get("contextName")
|
||||
customer := r.Form.Get("customerGUID")
|
||||
report := r.Form.Get("reportGUID")
|
||||
|
||||
if cluster == "" || contextName == "" || customer == "" || report == "" {
|
||||
t.Error("missing query parameter")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE(fredbi): (i) requests should have header Content-Type: "application/json"
|
||||
// NOTE(fredbi): (ii) shouldn't we require an extra authentication (e.g. secretKey or Token)?
|
||||
|
||||
buf, err := io.ReadAll(r.Body)
|
||||
defer func() {
|
||||
_ = r.Body.Close()
|
||||
}()
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var input reporthandlingv2.PostureReport
|
||||
if !assert.NoError(t, json.Unmarshal(buf, &input)) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
h.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
dump, _ := httputil.DumpRequest(r, true)
|
||||
t.Logf("%s\n", dump)
|
||||
|
||||
t.Errorf("unexpected route in input request: %v", r.URL)
|
||||
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
})
|
||||
|
||||
server.StartTLS()
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
// newInterceptor builds a new http.RoundTripper to re-route outgoing requests.
|
||||
func newInterceptor(transport http.RoundTripper, host string) *interceptor {
|
||||
return &interceptor{
|
||||
original: transport,
|
||||
host: host,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *interceptor) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
defer r.Body.Close()
|
||||
|
||||
hijacked := r.Clone(r.Context())
|
||||
hijacked.URL.Host = i.host
|
||||
|
||||
return i.original.RoundTrip(hijacked)
|
||||
}
|
||||
|
||||
// hijackedClient builds an HTTP client suited for working against a mock server.
|
||||
//
|
||||
// This client supports mocked TLS and re-routes outgoing calls to the local mock server.
|
||||
func hijackedClient(t testing.TB, srv *testServer) *http.Client {
|
||||
tlsClient := srv.Client()
|
||||
transport, ok := tlsClient.Transport.(*http.Transport)
|
||||
require.True(t, ok)
|
||||
mockURL, err := url.Parse(srv.Root())
|
||||
require.NoError(t, err)
|
||||
|
||||
return &http.Client{
|
||||
Transport: newInterceptor(transport, mockURL.Host),
|
||||
}
|
||||
}
|
||||
|
||||
func currentDir() string {
|
||||
_, filename, _, _ := runtime.Caller(1)
|
||||
|
||||
return filepath.Dir(filename)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
package reporter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/prettylogger"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// mxStdio serializes the capture of os.Stderr or os.Stdout
|
||||
var mxStdio sync.Mutex
|
||||
|
||||
func TestReportEventReceiver_addPathURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
report *ReportEventReceiver
|
||||
urlObj *url.URL
|
||||
@@ -57,19 +70,146 @@ func TestReportEventReceiver_addPathURL(t *testing.T) {
|
||||
RawQuery: "customerGUID=FFFF&invitationToken=XXXX&utm_medium=createaccount&utm_source=ARMOgithub",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add rbac path",
|
||||
report: &ReportEventReceiver{
|
||||
clusterName: "test",
|
||||
customerGUID: "FFFF",
|
||||
token: "XXXX",
|
||||
customerAdminEMail: "test@test",
|
||||
reportID: "1234",
|
||||
submitContext: SubmitContextRBAC,
|
||||
},
|
||||
urlObj: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "localhost:8080",
|
||||
},
|
||||
want: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "localhost:8080",
|
||||
Path: "rbac-visualizer",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add repository path",
|
||||
report: &ReportEventReceiver{
|
||||
clusterName: "test",
|
||||
customerGUID: "FFFF",
|
||||
token: "XXXX",
|
||||
customerAdminEMail: "test@test",
|
||||
reportID: "1234",
|
||||
submitContext: SubmitContextRepository,
|
||||
},
|
||||
urlObj: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "localhost:8080",
|
||||
},
|
||||
want: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "localhost:8080",
|
||||
Path: "repository-scanning/1234",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add default path",
|
||||
report: &ReportEventReceiver{
|
||||
clusterName: "test",
|
||||
customerGUID: "FFFF",
|
||||
token: "XXXX",
|
||||
customerAdminEMail: "test@test",
|
||||
reportID: "1234",
|
||||
submitContext: SubmitContext("invalid"),
|
||||
},
|
||||
urlObj: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "localhost:8080",
|
||||
},
|
||||
want: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "localhost:8080",
|
||||
Path: "dashboard",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "path when no email and no token",
|
||||
report: &ReportEventReceiver{
|
||||
clusterName: "test",
|
||||
customerGUID: "FFFF",
|
||||
token: "",
|
||||
customerAdminEMail: "",
|
||||
reportID: "1234",
|
||||
submitContext: SubmitContextScan,
|
||||
},
|
||||
urlObj: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "localhost:8080",
|
||||
},
|
||||
want: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "localhost:8080",
|
||||
Path: "compliance/test",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "path when email and no token",
|
||||
report: &ReportEventReceiver{
|
||||
clusterName: "test",
|
||||
customerGUID: "FFFF",
|
||||
token: "",
|
||||
customerAdminEMail: "test@test",
|
||||
reportID: "1234",
|
||||
submitContext: SubmitContextScan,
|
||||
},
|
||||
urlObj: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "localhost:8080",
|
||||
},
|
||||
want: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "localhost:8080",
|
||||
Path: "compliance/test",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "path when no email and token",
|
||||
report: &ReportEventReceiver{
|
||||
clusterName: "test",
|
||||
customerGUID: "FFFF",
|
||||
token: "XYZ",
|
||||
customerAdminEMail: "",
|
||||
reportID: "1234",
|
||||
submitContext: SubmitContextScan,
|
||||
},
|
||||
urlObj: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "localhost:8080",
|
||||
},
|
||||
want: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "localhost:8080",
|
||||
Path: "account/sign-up",
|
||||
RawQuery: "customerGUID=FFFF&invitationToken=XYZ&utm_medium=createaccount&utm_source=ARMOgithub",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.report.addPathURL(tt.urlObj)
|
||||
assert.Equal(t, tt.want.String(), tt.urlObj.String())
|
||||
for _, toPin := range tests {
|
||||
tc := toPin
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tc.report.addPathURL(tc.urlObj)
|
||||
require.Equal(t, tc.want.String(), tc.urlObj.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetURL(t *testing.T) {
|
||||
// Test submit and registered url
|
||||
{
|
||||
t.Parallel()
|
||||
|
||||
t.Run("with scan submit and registered url", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
reporter := NewReportEventReceiver(
|
||||
&cautils.ConfigObj{
|
||||
AccountID: "1234",
|
||||
@@ -81,10 +221,11 @@ func TestGetURL(t *testing.T) {
|
||||
SubmitContextScan,
|
||||
)
|
||||
assert.Equal(t, "https://cloud.armosec.io/compliance/test", reporter.GetURL())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with rbac submit and registered url", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Test rbac submit and registered url
|
||||
{
|
||||
reporter := NewReportEventReceiver(
|
||||
&cautils.ConfigObj{
|
||||
AccountID: "1234",
|
||||
@@ -96,10 +237,11 @@ func TestGetURL(t *testing.T) {
|
||||
SubmitContextRBAC,
|
||||
)
|
||||
assert.Equal(t, "https://cloud.armosec.io/rbac-visualizer", reporter.GetURL())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with repository submit and registered url", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Test repo submit and registered url
|
||||
{
|
||||
reporter := NewReportEventReceiver(
|
||||
&cautils.ConfigObj{
|
||||
AccountID: "1234",
|
||||
@@ -111,10 +253,10 @@ func TestGetURL(t *testing.T) {
|
||||
SubmitContextRepository,
|
||||
)
|
||||
assert.Equal(t, "https://cloud.armosec.io/repository-scanning/XXXX", reporter.GetURL())
|
||||
}
|
||||
})
|
||||
|
||||
// Test submit and NOT registered url
|
||||
{
|
||||
t.Run("with scan submit and NOT registered url", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
reporter := NewReportEventReceiver(
|
||||
&cautils.ConfigObj{
|
||||
@@ -126,51 +268,286 @@ func TestGetURL(t *testing.T) {
|
||||
SubmitContextScan,
|
||||
)
|
||||
assert.Equal(t, "https://cloud.armosec.io/account/sign-up?customerGUID=1234&invitationToken=token&utm_medium=createaccount&utm_source=ARMOgithub", reporter.GetURL())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with unknown submit and NOT registered url (default route)", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
reporter := NewReportEventReceiver(
|
||||
&cautils.ConfigObj{
|
||||
AccountID: "1234",
|
||||
ClusterName: "test",
|
||||
},
|
||||
"",
|
||||
SubmitContext("unknown"),
|
||||
)
|
||||
assert.Equal(t, "https://cloud.armosec.io/dashboard", reporter.GetURL())
|
||||
})
|
||||
}
|
||||
|
||||
func Test_prepareReportKeepsOriginalScanningTarget(t *testing.T) {
|
||||
func TestDisplayReportURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// prepareReport should keep the original scanning target it received, and not mutate it
|
||||
testCases := []struct {
|
||||
Name string
|
||||
Want reporthandlingv2.ScanningTarget
|
||||
}{
|
||||
{"Cluster", reporthandlingv2.Cluster},
|
||||
{"File", reporthandlingv2.File},
|
||||
{"Repo", reporthandlingv2.Repo},
|
||||
{"GitLocal", reporthandlingv2.GitLocal},
|
||||
{"Directory", reporthandlingv2.Directory},
|
||||
t.Run("should display an empty message", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
reporter := NewReportEventReceiver(
|
||||
&cautils.ConfigObj{
|
||||
AccountID: "1234",
|
||||
Token: "token",
|
||||
ClusterName: "test",
|
||||
},
|
||||
"",
|
||||
SubmitContextScan,
|
||||
)
|
||||
|
||||
capture, clean := captureStderr(t)
|
||||
defer clean()
|
||||
|
||||
reporter.DisplayReportURL()
|
||||
require.NoError(t, capture.Close())
|
||||
|
||||
buf, err := os.ReadFile(capture.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Empty(t, buf)
|
||||
})
|
||||
|
||||
t.Run("should display a non-empty message", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
reporter := NewReportEventReceiver(
|
||||
&cautils.ConfigObj{
|
||||
AccountID: "1234",
|
||||
Token: "token",
|
||||
ClusterName: "test",
|
||||
},
|
||||
"",
|
||||
SubmitContextScan,
|
||||
)
|
||||
reporter.generateMessage()
|
||||
|
||||
capture, clean := captureStderr(t)
|
||||
defer clean()
|
||||
|
||||
reporter.DisplayReportURL()
|
||||
require.NoError(t, capture.Close())
|
||||
|
||||
buf, err := os.ReadFile(capture.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotEmpty(t, buf)
|
||||
assert.Contains(t, string(buf), "WOW!")
|
||||
assert.Contains(t, string(buf), "https://cloud.armosec.io/account/sign-up")
|
||||
|
||||
t.Log(string(buf))
|
||||
})
|
||||
}
|
||||
|
||||
func TestPrepareReport(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("should keep the original scanning target it received and not mutate it", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Name string
|
||||
Want reporthandlingv2.ScanningTarget
|
||||
}{
|
||||
{"Cluster", reporthandlingv2.Cluster},
|
||||
{"File", reporthandlingv2.File},
|
||||
{"Repo", reporthandlingv2.Repo},
|
||||
{"GitLocal", reporthandlingv2.GitLocal},
|
||||
{"Directory", reporthandlingv2.Directory},
|
||||
}
|
||||
|
||||
reporter := NewReportEventReceiver(
|
||||
&cautils.ConfigObj{
|
||||
AccountID: "1e3ae7c4-a8bb-4d7c-9bdf-eb86bc25e6bb",
|
||||
Token: "token",
|
||||
ClusterName: "test",
|
||||
},
|
||||
"",
|
||||
SubmitContextScan,
|
||||
)
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
want := tc.Want
|
||||
|
||||
opaSessionObj := &cautils.OPASessionObj{
|
||||
Report: &reporthandlingv2.PostureReport{},
|
||||
Metadata: &reporthandlingv2.Metadata{
|
||||
ScanMetadata: reporthandlingv2.ScanMetadata{ScanningTarget: want},
|
||||
},
|
||||
}
|
||||
|
||||
reporter.prepareReport(opaSessionObj)
|
||||
|
||||
got := opaSessionObj.Metadata.ScanMetadata.ScanningTarget
|
||||
require.Equalf(t, want, got,
|
||||
"Scanning targets don’t match after preparing report. Got: %v, want %v", got, want,
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestSubmit(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
srv := mockAPIServer(t)
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
t.Run("should submit simple report", func(t *testing.T) {
|
||||
reporter := NewReportEventReceiver(
|
||||
&cautils.ConfigObj{
|
||||
AccountID: "1e3ae7c4-a8bb-4d7c-9bdf-eb86bc25e6bb",
|
||||
Token: "",
|
||||
ClusterName: "test",
|
||||
},
|
||||
"cbabd56f-bac6-416a-836b-b815ef347647",
|
||||
SubmitContextScan,
|
||||
)
|
||||
|
||||
opaSession := mockOPASessionObj(t)
|
||||
reporter.httpClient = hijackedClient(t, srv) // re-route the http client to our mock server, as this is not easily configurable in the reporter.
|
||||
|
||||
require.NoError(t,
|
||||
reporter.Submit(ctx, opaSession),
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("should warn when no customerGUID", func(t *testing.T) {
|
||||
reporter := NewReportEventReceiver(
|
||||
&cautils.ConfigObj{
|
||||
Token: "",
|
||||
ClusterName: "test",
|
||||
},
|
||||
"cbabd56f-bac6-416a-836b-b815ef347647",
|
||||
SubmitContextScan,
|
||||
)
|
||||
|
||||
opaSession := mockOPASessionObj(t)
|
||||
reporter.httpClient = hijackedClient(t, srv)
|
||||
|
||||
capture, clean := captureStderr(t)
|
||||
if pretty, ok := logger.L().(*prettylogger.PrettyLogger); ok {
|
||||
pretty.SetWriter(capture)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
clean()
|
||||
if pretty, ok := logger.L().(*prettylogger.PrettyLogger); ok {
|
||||
pretty.SetWriter(os.Stderr)
|
||||
}
|
||||
}()
|
||||
|
||||
require.NoError(t,
|
||||
reporter.Submit(ctx, opaSession),
|
||||
)
|
||||
require.NoError(t, capture.Close())
|
||||
|
||||
buf, err := os.ReadFile(capture.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, string(buf), "failed to publish result")
|
||||
assert.Contains(t, string(buf), "Unknown acc")
|
||||
})
|
||||
|
||||
t.Run("should warn when no cluster name", func(t *testing.T) {
|
||||
reporter := NewReportEventReceiver(
|
||||
&cautils.ConfigObj{
|
||||
AccountID: "1e3ae7c4-a8bb-4d7c-9bdf-eb86bc25e6bb",
|
||||
Token: "",
|
||||
},
|
||||
"cbabd56f-bac6-416a-836b-b815ef347647",
|
||||
SubmitContextScan,
|
||||
)
|
||||
|
||||
opaSession := mockOPASessionObj(t)
|
||||
opaSession.Metadata.ScanMetadata.ScanningTarget = reporthandlingv2.Cluster
|
||||
|
||||
reporter.httpClient = hijackedClient(t, srv)
|
||||
|
||||
capture, clean := captureStderr(t)
|
||||
if pretty, ok := logger.L().(*prettylogger.PrettyLogger); ok {
|
||||
pretty.SetWriter(capture)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
clean()
|
||||
if pretty, ok := logger.L().(*prettylogger.PrettyLogger); ok {
|
||||
pretty.SetWriter(os.Stderr)
|
||||
}
|
||||
}()
|
||||
|
||||
require.NoError(t,
|
||||
reporter.Submit(ctx, opaSession),
|
||||
)
|
||||
require.NoError(t, capture.Close())
|
||||
|
||||
buf, err := os.ReadFile(capture.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, string(buf), "failed to publish result")
|
||||
assert.Contains(t, string(buf), "cluster name")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetters(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
pickString := func() string {
|
||||
return strconv.Itoa(rand.Intn(10000)) //nolint:gosec
|
||||
}
|
||||
|
||||
reporter := NewReportEventReceiver(
|
||||
&cautils.ConfigObj{
|
||||
AccountID: "1e3ae7c4-a8bb-4d7c-9bdf-eb86bc25e6bb",
|
||||
Token: "token",
|
||||
ClusterName: "test",
|
||||
AccountID: "1e3ae7c4-a8bb-4d7c-9bdf-eb86bc25e6bb",
|
||||
Token: "",
|
||||
},
|
||||
"",
|
||||
"cbabd56f-bac6-416a-836b-b815ef347647",
|
||||
SubmitContextScan,
|
||||
)
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
want := tc.Want
|
||||
t.Run("should set customerID", func(t *testing.T) {
|
||||
guid := pickString()
|
||||
reporter.SetCustomerGUID(guid)
|
||||
|
||||
opaSessionObj := &cautils.OPASessionObj{
|
||||
Report: &reporthandlingv2.PostureReport{},
|
||||
Metadata: &reporthandlingv2.Metadata{
|
||||
ScanMetadata: reporthandlingv2.ScanMetadata{ScanningTarget: want},
|
||||
},
|
||||
}
|
||||
require.Equal(t, guid, reporter.customerGUID)
|
||||
})
|
||||
|
||||
reporter.prepareReport(opaSessionObj)
|
||||
t.Run("should set cluster name", func(t *testing.T) {
|
||||
cluster := pickString()
|
||||
reporter.SetClusterName(cluster)
|
||||
|
||||
got := opaSessionObj.Metadata.ScanMetadata.ScanningTarget
|
||||
if got != want {
|
||||
t.Errorf("Scanning targets don’t match after preparing report. Got: %v, want %v", got, want)
|
||||
}
|
||||
},
|
||||
)
|
||||
require.Equal(t, cluster, reporter.clusterName)
|
||||
})
|
||||
|
||||
t.Run("should normalize cluster name", func(t *testing.T) {
|
||||
const cluster = " x y\t\tz"
|
||||
reporter.SetClusterName(cluster)
|
||||
|
||||
require.Equal(t, "-x-y-z", reporter.clusterName)
|
||||
})
|
||||
}
|
||||
|
||||
func captureStderr(t testing.TB) (*os.File, func()) {
|
||||
mxStdio.Lock()
|
||||
saved := os.Stderr
|
||||
capture, err := os.CreateTemp("", "stderr")
|
||||
if !assert.NoError(t, err) {
|
||||
mxStdio.Unlock()
|
||||
|
||||
t.FailNow()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
os.Stderr = capture
|
||||
|
||||
return capture, func() {
|
||||
_ = capture.Close()
|
||||
_ = os.Remove(capture.Name())
|
||||
|
||||
os.Stderr = saved
|
||||
mxStdio.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
62772
core/pkg/resultshandling/reporter/v2/testdata/mock_opasessionobj.json
vendored
Normal file
62772
core/pkg/resultshandling/reporter/v2/testdata/mock_opasessionobj.json
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user