github.com/seilagamo/poc-lava-release@v0.3.3-rc3/internal/engine/engine_test.go (about)

     1  // Copyright 2023 Adevinta
     2  
     3  package engine
     4  
     5  import (
     6  	"context"
     7  	"encoding/json"
     8  	"flag"
     9  	"fmt"
    10  	"io"
    11  	"log/slog"
    12  	"math/rand"
    13  	"net/http"
    14  	"net/http/httptest"
    15  	"os"
    16  	"strings"
    17  	"testing"
    18  
    19  	agentconfig "github.com/adevinta/vulcan-agent/config"
    20  	report "github.com/adevinta/vulcan-report"
    21  	types "github.com/adevinta/vulcan-types"
    22  	dockertypes "github.com/docker/docker/api/types"
    23  	"github.com/docker/docker/pkg/archive"
    24  	"github.com/jroimartin/clilog"
    25  
    26  	"github.com/seilagamo/poc-lava-release/internal/assettypes"
    27  	"github.com/seilagamo/poc-lava-release/internal/config"
    28  	"github.com/seilagamo/poc-lava-release/internal/dockerutil"
    29  )
    30  
    31  func TestMain(m *testing.M) {
    32  	flag.Parse()
    33  
    34  	level := slog.LevelError
    35  	if testing.Verbose() {
    36  		level = slog.LevelDebug
    37  	}
    38  
    39  	h := clilog.NewCLIHandler(os.Stderr, &clilog.HandlerOptions{Level: level})
    40  	slog.SetDefault(slog.New(h))
    41  
    42  	os.Exit(m.Run())
    43  }
    44  
    45  func TestRun(t *testing.T) {
    46  	if err := dockerBuild("testdata/engine/lava-engine-test", "lava-engine-test:latest"); err != nil {
    47  		t.Fatalf("could build Docker image: %v", err)
    48  	}
    49  
    50  	wantDetails := fmt.Sprintf("lava engine test response %v", rand.Uint64())
    51  
    52  	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    53  		fmt.Fprint(w, wantDetails)
    54  	}))
    55  	defer srv.Close()
    56  
    57  	t.Logf("test server listening at %v", srv.URL)
    58  
    59  	var (
    60  		checktypeURLs = []string{"testdata/engine/checktypes_lava_engine_test.json"}
    61  		targets       = []config.Target{
    62  			{
    63  				Identifier: srv.URL,
    64  				AssetType:  types.WebAddress,
    65  			},
    66  		}
    67  		agentConfig = config.AgentConfig{
    68  			PullPolicy: agentconfig.PullPolicyNever,
    69  		}
    70  	)
    71  
    72  	engineReport, err := Run(checktypeURLs, targets, agentConfig)
    73  	if err != nil {
    74  		t.Fatalf("unexpected error: %v", err)
    75  	}
    76  
    77  	checkReportTarget(t, engineReport, dockerInternalHost)
    78  
    79  	var checkReports []report.Report
    80  	for _, v := range engineReport {
    81  		checkReports = append(checkReports, v)
    82  	}
    83  
    84  	if len(checkReports) != 1 {
    85  		t.Fatalf("unexpected number of reports: %v", len(checkReports))
    86  	}
    87  
    88  	gotReport := checkReports[0]
    89  
    90  	if gotReport.Status != "FINISHED" {
    91  		t.Errorf("unexpected status: %v", gotReport.Status)
    92  	}
    93  
    94  	if gotReport.Target != srv.URL {
    95  		t.Errorf("unexpected target: got: %v, want: %v", gotReport.Target, srv.URL)
    96  	}
    97  
    98  	if len(gotReport.Vulnerabilities) != 1 {
    99  		t.Fatalf("unexpected number of vulnerabilities: %v", len(gotReport.Vulnerabilities))
   100  	}
   101  
   102  	gotDetails := gotReport.Vulnerabilities[0].Details
   103  
   104  	if gotDetails != wantDetails {
   105  		t.Errorf("unexpected details: got: %#q, want: %#q", gotDetails, wantDetails)
   106  	}
   107  }
   108  
   109  func TestRun_docker_image(t *testing.T) {
   110  	var (
   111  		checktypeURLs = []string{"testdata/engine/checktypes_trivy.json"}
   112  		targets       = []config.Target{
   113  			{
   114  				Identifier: "python:3.4-alpine",
   115  				AssetType:  types.DockerImage,
   116  			},
   117  		}
   118  		agentConfig = config.AgentConfig{
   119  			PullPolicy: agentconfig.PullPolicyAlways,
   120  		}
   121  	)
   122  
   123  	engineReport, err := Run(checktypeURLs, targets, agentConfig)
   124  	if err != nil {
   125  		t.Fatalf("unexpected error: %v", err)
   126  	}
   127  
   128  	checkReportTarget(t, engineReport, dockerInternalHost)
   129  
   130  	var checkReports []report.Report
   131  	for _, v := range engineReport {
   132  		checkReports = append(checkReports, v)
   133  	}
   134  
   135  	if len(checkReports) != 1 {
   136  		t.Fatalf("unexpected number of reports: %v", len(checkReports))
   137  	}
   138  
   139  	gotReport := checkReports[0]
   140  
   141  	if gotReport.Status != "FINISHED" {
   142  		t.Errorf("unexpected status: %v", gotReport.Status)
   143  	}
   144  
   145  	if len(gotReport.Vulnerabilities) == 0 {
   146  		t.Errorf("no vulnerabilities found")
   147  	}
   148  
   149  	t.Logf("found %v vulnerabilities", len(gotReport.Vulnerabilities))
   150  }
   151  
   152  func TestRun_path(t *testing.T) {
   153  	var (
   154  		checktypeURLs = []string{"testdata/engine/checktypes_trivy.json"}
   155  		agentConfig   = config.AgentConfig{
   156  			PullPolicy: agentconfig.PullPolicyAlways,
   157  		}
   158  	)
   159  
   160  	tests := []struct {
   161  		name       string
   162  		target     config.Target
   163  		wantStatus string
   164  		wantVulns  bool
   165  	}{
   166  		{
   167  			name: "dir",
   168  			target: config.Target{
   169  				Identifier: "testdata/engine/vulnpath",
   170  				AssetType:  assettypes.Path,
   171  			},
   172  			wantStatus: "FINISHED",
   173  			wantVulns:  true,
   174  		},
   175  		{
   176  			name: "file",
   177  			target: config.Target{
   178  				Identifier: "testdata/engine/vulnpath/Dockerfile",
   179  				AssetType:  assettypes.Path,
   180  			},
   181  			wantStatus: "FINISHED",
   182  			wantVulns:  true,
   183  		},
   184  		{
   185  			name: "not exist",
   186  			target: config.Target{
   187  				Identifier: "testdata/engine/notexist",
   188  				AssetType:  assettypes.Path,
   189  			},
   190  			wantStatus: "FAILED",
   191  			wantVulns:  false,
   192  		},
   193  	}
   194  
   195  	for _, tt := range tests {
   196  		t.Run(tt.name, func(t *testing.T) {
   197  			engineReport, err := Run(checktypeURLs, []config.Target{tt.target}, agentConfig)
   198  			if err != nil {
   199  				t.Fatalf("unexpected error: %v", err)
   200  			}
   201  
   202  			checkReportTarget(t, engineReport, dockerInternalHost)
   203  
   204  			var checkReports []report.Report
   205  			for _, v := range engineReport {
   206  				checkReports = append(checkReports, v)
   207  			}
   208  
   209  			if len(checkReports) != 1 {
   210  				t.Fatalf("unexpected number of reports: %v", len(checkReports))
   211  			}
   212  
   213  			gotReport := checkReports[0]
   214  
   215  			if gotReport.Status != tt.wantStatus {
   216  				t.Errorf("unexpected status: %v", gotReport.Status)
   217  			}
   218  
   219  			if (len(gotReport.Vulnerabilities) > 0) != tt.wantVulns {
   220  				t.Errorf("unexpected number of vulnerabilities: %v", len(gotReport.Vulnerabilities))
   221  			}
   222  
   223  			t.Logf("found %v vulnerabilities", len(gotReport.Vulnerabilities))
   224  		})
   225  	}
   226  }
   227  
   228  func TestRun_inconclusive(t *testing.T) {
   229  	checktypeURLs := []string{"testdata/engine/checktypes_trivy.json"}
   230  	agentConfig := config.AgentConfig{
   231  		PullPolicy: agentconfig.PullPolicyAlways,
   232  	}
   233  	target := config.Target{
   234  		Identifier: "testdata/engine/vulnpath",
   235  		AssetType:  types.GitRepository,
   236  	}
   237  	engineReport, err := Run(checktypeURLs, []config.Target{target}, agentConfig)
   238  	if err != nil {
   239  		t.Fatalf("unexpected error: %v", err)
   240  	}
   241  
   242  	checkReportTarget(t, engineReport, dockerInternalHost)
   243  
   244  	var checkReports []report.Report
   245  	for _, v := range engineReport {
   246  		checkReports = append(checkReports, v)
   247  	}
   248  
   249  	if len(checkReports) != 1 {
   250  		t.Fatalf("unexpected number of reports: %v", len(checkReports))
   251  	}
   252  
   253  	gotReport := checkReports[0]
   254  
   255  	if gotReport.Status != "INCONCLUSIVE" {
   256  		t.Errorf("unexpected status: %v", gotReport.Status)
   257  	}
   258  
   259  	if len(gotReport.Vulnerabilities) > 0 {
   260  		t.Errorf("unexpected number of vulnerabilities: %v", len(gotReport.Vulnerabilities))
   261  	}
   262  }
   263  
   264  func TestRun_no_jobs(t *testing.T) {
   265  	var (
   266  		checktypeURLs = []string{"testdata/engine/checktypes_lava_engine_test.json"}
   267  		agentConfig   = config.AgentConfig{
   268  			PullPolicy: agentconfig.PullPolicyNever,
   269  		}
   270  	)
   271  
   272  	engineReport, err := Run(checktypeURLs, nil, agentConfig)
   273  	if err != nil {
   274  		t.Fatalf("unexpected error: %v", err)
   275  	}
   276  
   277  	if len(engineReport) != 0 {
   278  		t.Fatalf("unexpected number of reports: %v", len(engineReport))
   279  	}
   280  }
   281  
   282  func dockerBuild(path, tag string) error {
   283  	cli, err := dockerutil.NewAPIClient()
   284  	if err != nil {
   285  		return fmt.Errorf("new client: %w", err)
   286  	}
   287  	defer cli.Close()
   288  
   289  	tar, err := archive.TarWithOptions(path, &archive.TarOptions{})
   290  	if err != nil {
   291  		return fmt.Errorf("new tar: %w", err)
   292  	}
   293  
   294  	opts := dockertypes.ImageBuildOptions{
   295  		Tags:   []string{tag},
   296  		Remove: true,
   297  	}
   298  	resp, err := cli.ImageBuild(context.Background(), tar, opts)
   299  	if err != nil {
   300  		return fmt.Errorf("image build: %w", err)
   301  	}
   302  	defer resp.Body.Close()
   303  
   304  	if _, err := io.Copy(io.Discard, resp.Body); err != nil {
   305  		return fmt.Errorf("read response: %w", err)
   306  	}
   307  
   308  	return nil
   309  }
   310  
   311  // checkReportTarget encodes report as JSON and looks for substr in
   312  // the output. If substr is not found, checkReportTarget calls
   313  // t.Errorf.
   314  func checkReportTarget(t *testing.T, report Report, substr string) {
   315  	doc, err := json.MarshalIndent(report, "", "  ")
   316  	if err != nil {
   317  		t.Fatalf("marshal error: %v", err)
   318  	}
   319  
   320  	if strings.Contains(string(doc), substr) {
   321  		t.Errorf("report contains %q:\n%s", dockerInternalHost, doc)
   322  	}
   323  }