Files
sap-jenkins-library/cmd/contrastExecuteScan_test.go

316 lines
9.1 KiB
Go

package cmd
import (
"encoding/base64"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"github.com/SAP/jenkins-library/pkg/contrast"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/stretchr/testify/assert"
)
type contrastExecuteScanMockUtils struct {
*mock.ExecMockRunner
*mock.FilesMock
}
func newContrastExecuteScanTestsUtils() contrastExecuteScanMockUtils {
utils := contrastExecuteScanMockUtils{
ExecMockRunner: &mock.ExecMockRunner{},
FilesMock: &mock.FilesMock{},
}
return utils
}
func TestGetAuth(t *testing.T) {
t.Run("Success", func(t *testing.T) {
config := &contrastExecuteScanOptions{
UserAPIKey: "user-api-key",
Username: "username",
ServiceKey: "service-key",
}
authString := getAuth(config)
assert.NotEmpty(t, authString)
data, err := base64.StdEncoding.DecodeString(authString)
assert.NoError(t, err)
assert.Equal(t, "username:service-key", string(data))
})
}
func TestGetApplicationUrls(t *testing.T) {
t.Run("Success", func(t *testing.T) {
config := &contrastExecuteScanOptions{
Server: "https://server.com",
OrganizationID: "orgId",
ApplicationID: "appId",
}
appUrl, guiUrl := getApplicationUrls(config)
assert.Equal(t, "https://server.com/api/v4/organizations/orgId/applications/appId", appUrl)
assert.Equal(t, "https://server.com/Contrast/static/ng/index.html#/orgId/applications/appId", guiUrl)
})
}
func TestValidateConfigs(t *testing.T) {
t.Parallel()
validConfig := contrastExecuteScanOptions{
UserAPIKey: "user-api-key",
ServiceKey: "service-key",
Username: "username",
Server: "https://server.com",
OrganizationID: "orgId",
ApplicationID: "appId",
}
t.Run("Valid config", func(t *testing.T) {
config := validConfig
err := validateConfigs(&config)
assert.NoError(t, err)
})
t.Run("Valid config, server url without https://", func(t *testing.T) {
config := validConfig
config.Server = "server.com"
err := validateConfigs(&config)
assert.NoError(t, err)
assert.Equal(t, config.Server, "https://server.com")
})
t.Run("Empty config", func(t *testing.T) {
config := contrastExecuteScanOptions{}
err := validateConfigs(&config)
assert.Error(t, err)
})
t.Run("Empty userAPIKey", func(t *testing.T) {
config := validConfig
config.UserAPIKey = ""
err := validateConfigs(&config)
assert.Error(t, err)
})
t.Run("Empty username", func(t *testing.T) {
config := validConfig
config.Username = ""
err := validateConfigs(&config)
assert.Error(t, err)
})
t.Run("Empty serviceKey", func(t *testing.T) {
config := validConfig
config.ServiceKey = ""
err := validateConfigs(&config)
assert.Error(t, err)
})
t.Run("Empty server", func(t *testing.T) {
config := validConfig
config.Server = ""
err := validateConfigs(&config)
assert.Error(t, err)
})
t.Run("Empty organizationId", func(t *testing.T) {
config := validConfig
config.OrganizationID = ""
err := validateConfigs(&config)
assert.Error(t, err)
})
t.Run("Empty applicationID", func(t *testing.T) {
config := validConfig
config.ApplicationID = ""
err := validateConfigs(&config)
assert.Error(t, err)
})
}
// Test constants for mock and end-to-end tests
const (
// Mock test constants
mockContrastAPIKey = "mock-api-key"
mockContrastServiceKey = "mock-service-key"
mockContrastUsername = "mock@example.com"
mockContrastOrgID = "org-mock-123"
mockContrastServerURL = "https://mock.contrastsecurity.com"
mockContrastAppID = "app-mock-456"
// End-to-end test constants - Fill these with your real values for end-to-end testing
e2eContrastAPIKey = "YOUR_API_KEY"
e2eContrastServiceKey = "YOUR_SERVICE_KEY"
e2eContrastUsername = "YOUR_USERNAME"
e2eContrastOrgID = "YOUR_ORG_ID"
e2eContrastServerURL = "https://YOUR_SERVER.contrastsecurity.com"
e2eContrastAppID = "YOUR_APP_ID"
)
// Mock-based unit tests (no real credentials needed)
func TestGenerateSarifReportMockSuccess(t *testing.T) {
// Setup mock HTTP server
serverURL := ""
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
if strings.Contains(path, "/sarif/async") {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"success": true, "uuid": "test-sarif-uuid"}`))
} else if strings.Contains(path, "/status") {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"success": true, "status": "ACTIVE", "downloadUrl": "` + serverURL + `/download"}`))
} else if strings.Contains(path, "/download") {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"version": "2.1.0"}`))
}
}))
defer server.Close()
serverURL = server.URL
mockConfig := &contrastExecuteScanOptions{
UserAPIKey: mockContrastAPIKey,
ServiceKey: mockContrastServiceKey,
Username: mockContrastUsername,
OrganizationID: mockContrastOrgID,
Server: server.URL,
ApplicationID: mockContrastAppID,
}
mockUtils := newContrastExecuteScanTestsUtils()
mockClient := contrast.NewClient(
mockContrastAPIKey,
mockContrastServiceKey,
mockContrastUsername,
mockContrastOrgID,
server.URL,
server.URL+"/api/v4/organizations/"+mockContrastOrgID+"/applications/"+mockContrastAppID,
)
reports, err := generateSarifReport(mockConfig, mockUtils, mockClient)
assert.NoError(t, err, "generateSarifReport should not return error")
assert.NotEmpty(t, reports, "Expected reports to be generated")
assert.Equal(t, 1, len(reports))
assert.Equal(t, "Contrast SARIF Report", reports[0].Name)
}
func TestGeneratePdfReportMockSuccess(t *testing.T) {
// Setup mock HTTP server
serverURL := ""
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
if strings.Contains(path, "/attestation") {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"success": true, "uuid": "test-pdf-uuid"}`))
} else if strings.Contains(path, "/status") {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"success": true, "status": "ACTIVE", "downloadUrl": "` + serverURL + `/download"}`))
} else if strings.Contains(path, "/download") {
w.WriteHeader(http.StatusOK)
w.Write([]byte("PDF content"))
}
}))
defer server.Close()
serverURL = server.URL
mockConfig := &contrastExecuteScanOptions{
UserAPIKey: mockContrastAPIKey,
ServiceKey: mockContrastServiceKey,
Username: mockContrastUsername,
OrganizationID: mockContrastOrgID,
Server: server.URL,
ApplicationID: mockContrastAppID,
}
mockUtils := newContrastExecuteScanTestsUtils()
mockClient := contrast.NewClient(
mockContrastAPIKey,
mockContrastServiceKey,
mockContrastUsername,
mockContrastOrgID,
server.URL,
server.URL+"/api/v4/organizations/"+mockContrastOrgID+"/applications/"+mockContrastAppID,
)
reports, err := generatePdfReport(mockConfig, mockUtils, mockClient)
assert.NoError(t, err, "generatePdfReport should not return error")
assert.NotEmpty(t, reports, "Expected reports to be generated")
assert.Equal(t, 1, len(reports))
assert.Equal(t, "Contrast PDF Attestation Report", reports[0].Name)
}
// TestContrastExecuteScanEndToEnd performs an end-to-end test of the runContrastExecuteScan function.
// It requires valid Contrast credentials to be set in the constants above.
// This test is skipped if the credentials are not filled in.
func TestContrastExecuteScanEndToEnd(t *testing.T) {
if e2eContrastAPIKey == "YOUR_API_KEY" {
t.Skip("Skipping end-to-end test: Contrast credentials not provided.")
}
outputDir := "./contrast-e2e-output"
_ = os.RemoveAll(outputDir) // Best-effort cleanup
if err := os.MkdirAll(outputDir, 0755); err != nil {
t.Fatalf("Failed to create output directory: %v", err)
}
defer os.RemoveAll(outputDir)
oldCWD, _ := os.Getwd()
err := os.Chdir(outputDir)
if err != nil {
t.Fatalf("Failed to change directory: %v", err)
}
defer os.Chdir(oldCWD)
config := contrastExecuteScanOptions{
Server: e2eContrastServerURL,
OrganizationID: e2eContrastOrgID,
ApplicationID: e2eContrastAppID,
UserAPIKey: e2eContrastAPIKey,
Username: e2eContrastUsername,
ServiceKey: e2eContrastServiceKey,
GenerateSarif: true,
GeneratePdf: true,
}
utils := newContrastExecuteScanUtils()
reports, err := runContrastExecuteScan(&config, nil, utils)
assert.NoError(t, err, "runContrastExecuteScan should not return an error")
assert.NotEmpty(t, reports, "Expected reports to be generated")
// Verify SARIF report
sarifPath := filepath.Join(".", "contrast", "piper_contrast.sarif")
assert.FileExists(t, sarifPath, "SARIF report file should exist")
foundSarif := false
for _, report := range reports {
if filepath.Clean(report.Target) == filepath.Clean(sarifPath) {
foundSarif = true
break
}
}
assert.True(t, foundSarif, "SARIF report should be in the returned reports list")
// Verify PDF report
pdfPath := filepath.Join(".", "contrast", "piper_contrast_attestation.pdf")
assert.FileExists(t, pdfPath, "PDF report file should exist")
foundPdf := false
for _, report := range reports {
if filepath.Clean(report.Target) == filepath.Clean(pdfPath) {
foundPdf = true
break
}
}
assert.True(t, foundPdf, "PDF report should be in the returned reports list")
}