github.com/adevinta/lava@v0.7.2/cmd/lava/internal/run/run_test.go (about)

     1  // Copyright 2024 Adevinta
     2  
     3  package run
     4  
     5  import (
     6  	"context"
     7  	"flag"
     8  	"fmt"
     9  	"log/slog"
    10  	"os"
    11  	"testing"
    12  
    13  	agentconfig "github.com/adevinta/vulcan-agent/config"
    14  	"github.com/docker/docker/api/types/filters"
    15  	"github.com/docker/docker/api/types/image"
    16  	"github.com/jroimartin/clilog"
    17  
    18  	"github.com/adevinta/lava/internal/containers"
    19  	"github.com/adevinta/lava/internal/report"
    20  )
    21  
    22  const checktype = "vulcansec/vulcan-gitleaks:ea42ea5-b6abd8a"
    23  
    24  var testRuntime containers.Runtime
    25  
    26  func TestMain(m *testing.M) {
    27  	flag.Parse()
    28  
    29  	level := slog.LevelError
    30  	if testing.Verbose() {
    31  		level = slog.LevelDebug
    32  	}
    33  
    34  	h := clilog.NewCLIHandler(os.Stderr, &clilog.HandlerOptions{Level: level})
    35  	slog.SetDefault(slog.New(h))
    36  
    37  	rt, err := containers.GetenvRuntime()
    38  	if err != nil {
    39  		fmt.Fprintf(os.Stderr, "error: get env runtime: %v", err)
    40  		os.Exit(2)
    41  	}
    42  	testRuntime = rt
    43  
    44  	os.Exit(m.Run())
    45  }
    46  
    47  func TestRunRun(t *testing.T) {
    48  	tests := []struct {
    49  		name         string
    50  		path         string
    51  		wantExitCode int
    52  	}{
    53  		{
    54  			name:         "good path",
    55  			path:         "testdata/goodpath",
    56  			wantExitCode: 0,
    57  		},
    58  		{
    59  			name:         "vulnerable path",
    60  			path:         "testdata/vulnpath",
    61  			wantExitCode: 103,
    62  		},
    63  	}
    64  
    65  	for _, tt := range tests {
    66  		t.Run(tt.name, func(t *testing.T) {
    67  			oldPwd := mustGetwd()
    68  			oldOsExit := osExit
    69  			oldRunO := runO
    70  			oldRunOptfile := runOptfile
    71  			defer func() {
    72  				mustChdir(oldPwd)
    73  				osExit = oldOsExit
    74  				runO = oldRunO
    75  				runOptfile = oldRunOptfile
    76  			}()
    77  
    78  			runO = "output.txt"
    79  			runOptfile = "options.json"
    80  
    81  			var exitCode int
    82  			osExit = func(status int) {
    83  				exitCode = status
    84  			}
    85  
    86  			mustChdir(tt.path)
    87  			if err := runRun([]string{checktype, "."}); err != nil {
    88  				t.Fatalf("unexpected error: %v", err)
    89  			}
    90  
    91  			if exitCode != tt.wantExitCode {
    92  				t.Errorf("unexpected exit code: got: %v, want: %v", exitCode, tt.wantExitCode)
    93  			}
    94  		})
    95  	}
    96  }
    97  
    98  func TestRunRun_path_checktype(t *testing.T) {
    99  	oldPwd := mustGetwd()
   100  	oldOsExit := osExit
   101  	oldRunO := runO
   102  	defer func() {
   103  		mustChdir(oldPwd)
   104  		osExit = oldOsExit
   105  		runO = oldRunO
   106  	}()
   107  
   108  	runO = "output.txt"
   109  
   110  	var exitCode int
   111  	osExit = func(status int) {
   112  		exitCode = status
   113  	}
   114  
   115  	cli, err := containers.NewDockerdClient(testRuntime)
   116  	if err != nil {
   117  		t.Fatalf("could not create dockerd client: %v", err)
   118  	}
   119  	defer cli.Close()
   120  
   121  	mustChdir("testdata/lava-run-test")
   122  	if err := runRun([]string{".", "."}); err != nil {
   123  		t.Fatalf("unexpected error: %v", err)
   124  	}
   125  	defer func() {
   126  		const imgRef = "lava-run-test:lava-run"
   127  		rmOpts := image.RemoveOptions{Force: true, PruneChildren: true}
   128  		if _, err := cli.ImageRemove(context.Background(), imgRef, rmOpts); err != nil {
   129  			t.Logf("could not delete test Docker image %q: %v", imgRef, err)
   130  		}
   131  	}()
   132  
   133  	if exitCode != int(report.ExitCodeCritical) {
   134  		t.Errorf("unexpected exit code: %v", exitCode)
   135  	}
   136  }
   137  
   138  func TestRunRun_path_checktype_same_id(t *testing.T) {
   139  	oldPwd := mustGetwd()
   140  	oldOsExit := osExit
   141  	oldRunO := runO
   142  	oldPull := runPull
   143  	defer func() {
   144  		mustChdir(oldPwd)
   145  		osExit = oldOsExit
   146  		runO = oldRunO
   147  		runPull = oldPull
   148  	}()
   149  
   150  	runO = "output.txt"
   151  	runPull = agentconfig.PullPolicyNever
   152  
   153  	var exitCode int
   154  	osExit = func(status int) {
   155  		exitCode = status
   156  	}
   157  
   158  	cli, err := containers.NewDockerdClient(testRuntime)
   159  	if err != nil {
   160  		t.Fatalf("could not create dockerd client: %v", err)
   161  	}
   162  	defer cli.Close()
   163  
   164  	mustChdir("testdata/lava-run-test")
   165  
   166  	const ref = "lava-run-test:lava-run"
   167  
   168  	defer func() {
   169  		rmOpts := image.RemoveOptions{Force: true, PruneChildren: true}
   170  		if _, err := cli.ImageRemove(context.Background(), ref, rmOpts); err != nil {
   171  			t.Logf("could not delete test Docker image %q: %v", ref, err)
   172  		}
   173  	}()
   174  
   175  	var ids [2]string
   176  	for i := 0; i < 2; i++ {
   177  		exitCode = 0
   178  		if err := runRun([]string{".", "."}); err != nil {
   179  			t.Fatalf("unexpected error in run %v: %v", i, err)
   180  		}
   181  		if exitCode != int(report.ExitCodeCritical) {
   182  			t.Errorf("unexpected exit code in run %v: %v", i, exitCode)
   183  		}
   184  
   185  		summ, err := cli.ImageList(context.Background(), image.ListOptions{
   186  			Filters: filters.NewArgs(filters.Arg("reference", ref)),
   187  		})
   188  		if err != nil {
   189  			t.Fatalf("could not get image details in run %v: %v", i, err)
   190  		}
   191  		if len(summ) != 1 {
   192  			t.Fatalf("wrong number of images in run %v: %v", i, err)
   193  		}
   194  		ids[i] = summ[0].ID
   195  	}
   196  
   197  	if ids[0] != ids[1] {
   198  		t.Errorf("image IDs do not match: %q != %q", ids[0], ids[1])
   199  	}
   200  }
   201  
   202  // mustGetwd returns a rooted path name corresponding to the current
   203  // directory. It panics on error.
   204  func mustGetwd() string {
   205  	wd, err := os.Getwd()
   206  	if err != nil {
   207  		panic(err)
   208  	}
   209  	return wd
   210  }
   211  
   212  // mustChdir changes the current working directory to the named
   213  // directory. It panics on error.
   214  func mustChdir(path string) {
   215  	if err := os.Chdir(path); err != nil {
   216  		panic(err)
   217  	}
   218  }