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 }