github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/cmd/codeqlExecuteScan_test.go (about) 1 //go:build unit 2 // +build unit 3 4 package cmd 5 6 import ( 7 "fmt" 8 "testing" 9 "time" 10 11 "github.com/SAP/jenkins-library/pkg/codeql" 12 "github.com/SAP/jenkins-library/pkg/mock" 13 "github.com/SAP/jenkins-library/pkg/orchestrator" 14 "github.com/pkg/errors" 15 "github.com/stretchr/testify/assert" 16 ) 17 18 type codeqlExecuteScanMockUtils struct { 19 *mock.ExecMockRunner 20 *mock.FilesMock 21 } 22 23 func newCodeqlExecuteScanTestsUtils() codeqlExecuteScanMockUtils { 24 utils := codeqlExecuteScanMockUtils{ 25 ExecMockRunner: &mock.ExecMockRunner{}, 26 FilesMock: &mock.FilesMock{}, 27 } 28 return utils 29 } 30 31 func TestRunCodeqlExecuteScan(t *testing.T) { 32 33 t.Run("Valid CodeqlExecuteScan", func(t *testing.T) { 34 config := codeqlExecuteScanOptions{BuildTool: "maven", ModulePath: "./"} 35 _, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils()) 36 assert.NoError(t, err) 37 }) 38 39 t.Run("No auth token passed on upload results", func(t *testing.T) { 40 config := codeqlExecuteScanOptions{BuildTool: "maven", UploadResults: true, ModulePath: "./"} 41 _, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils()) 42 assert.Error(t, err) 43 }) 44 45 t.Run("GitCommitID is NA on upload results", func(t *testing.T) { 46 config := codeqlExecuteScanOptions{BuildTool: "maven", UploadResults: true, ModulePath: "./", CommitID: "NA"} 47 _, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils()) 48 assert.Error(t, err) 49 }) 50 51 t.Run("Custom buildtool", func(t *testing.T) { 52 config := codeqlExecuteScanOptions{BuildTool: "custom", Language: "javascript", ModulePath: "./"} 53 _, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils()) 54 assert.NoError(t, err) 55 }) 56 57 t.Run("Custom buildtool but no language specified", func(t *testing.T) { 58 config := codeqlExecuteScanOptions{BuildTool: "custom", ModulePath: "./", GithubToken: "test"} 59 _, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils()) 60 assert.Error(t, err) 61 }) 62 63 t.Run("Invalid buildtool and no language specified", func(t *testing.T) { 64 config := codeqlExecuteScanOptions{BuildTool: "test", ModulePath: "./", GithubToken: "test"} 65 _, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils()) 66 assert.Error(t, err) 67 }) 68 69 t.Run("Invalid buildtool but language specified", func(t *testing.T) { 70 config := codeqlExecuteScanOptions{BuildTool: "test", Language: "javascript", ModulePath: "./", GithubToken: "test"} 71 _, err := runCodeqlExecuteScan(&config, nil, newCodeqlExecuteScanTestsUtils()) 72 assert.NoError(t, err) 73 }) 74 } 75 76 func TestGetGitRepoInfo(t *testing.T) { 77 t.Run("Valid https URL1", func(t *testing.T) { 78 var repoInfo RepoInfo 79 err := getGitRepoInfo("https://github.hello.test/Testing/fortify.git", &repoInfo) 80 assert.NoError(t, err) 81 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 82 assert.Equal(t, "fortify", repoInfo.repo) 83 assert.Equal(t, "Testing", repoInfo.owner) 84 }) 85 86 t.Run("Valid https URL2", func(t *testing.T) { 87 var repoInfo RepoInfo 88 err := getGitRepoInfo("https://github.hello.test/Testing/fortify", &repoInfo) 89 assert.NoError(t, err) 90 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 91 assert.Equal(t, "fortify", repoInfo.repo) 92 assert.Equal(t, "Testing", repoInfo.owner) 93 }) 94 t.Run("Valid https URL1 with dots", func(t *testing.T) { 95 var repoInfo RepoInfo 96 err := getGitRepoInfo("https://github.hello.test/Testing/com.sap.fortify.git", &repoInfo) 97 assert.NoError(t, err) 98 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 99 assert.Equal(t, "com.sap.fortify", repoInfo.repo) 100 assert.Equal(t, "Testing", repoInfo.owner) 101 }) 102 103 t.Run("Valid https URL2 with dots", func(t *testing.T) { 104 var repoInfo RepoInfo 105 err := getGitRepoInfo("https://github.hello.test/Testing/com.sap.fortify", &repoInfo) 106 assert.NoError(t, err) 107 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 108 assert.Equal(t, "com.sap.fortify", repoInfo.repo) 109 assert.Equal(t, "Testing", repoInfo.owner) 110 }) 111 t.Run("Valid https URL1 with username and token", func(t *testing.T) { 112 var repoInfo RepoInfo 113 err := getGitRepoInfo("https://username:token@github.hello.test/Testing/fortify.git", &repoInfo) 114 assert.NoError(t, err) 115 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 116 assert.Equal(t, "fortify", repoInfo.repo) 117 assert.Equal(t, "Testing", repoInfo.owner) 118 }) 119 120 t.Run("Valid https URL2 with username and token", func(t *testing.T) { 121 var repoInfo RepoInfo 122 err := getGitRepoInfo("https://username:token@github.hello.test/Testing/fortify", &repoInfo) 123 assert.NoError(t, err) 124 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 125 assert.Equal(t, "fortify", repoInfo.repo) 126 assert.Equal(t, "Testing", repoInfo.owner) 127 }) 128 129 t.Run("Invalid https URL as no org/owner passed", func(t *testing.T) { 130 var repoInfo RepoInfo 131 assert.Error(t, getGitRepoInfo("https://github.com/fortify", &repoInfo)) 132 }) 133 134 t.Run("Invalid URL as no protocol passed", func(t *testing.T) { 135 var repoInfo RepoInfo 136 assert.Error(t, getGitRepoInfo("github.hello.test/Testing/fortify", &repoInfo)) 137 }) 138 139 t.Run("Valid ssh URL1", func(t *testing.T) { 140 var repoInfo RepoInfo 141 err := getGitRepoInfo("git@github.hello.test/Testing/fortify.git", &repoInfo) 142 assert.NoError(t, err) 143 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 144 assert.Equal(t, "fortify", repoInfo.repo) 145 assert.Equal(t, "Testing", repoInfo.owner) 146 }) 147 148 t.Run("Valid ssh URL2", func(t *testing.T) { 149 var repoInfo RepoInfo 150 err := getGitRepoInfo("git@github.hello.test/Testing/fortify", &repoInfo) 151 assert.NoError(t, err) 152 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 153 assert.Equal(t, "fortify", repoInfo.repo) 154 assert.Equal(t, "Testing", repoInfo.owner) 155 }) 156 t.Run("Valid ssh URL1 with dots", func(t *testing.T) { 157 var repoInfo RepoInfo 158 err := getGitRepoInfo("git@github.hello.test/Testing/com.sap.fortify.git", &repoInfo) 159 assert.NoError(t, err) 160 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 161 assert.Equal(t, "com.sap.fortify", repoInfo.repo) 162 assert.Equal(t, "Testing", repoInfo.owner) 163 }) 164 165 t.Run("Valid ssh URL2 with dots", func(t *testing.T) { 166 var repoInfo RepoInfo 167 err := getGitRepoInfo("git@github.hello.test/Testing/com.sap.fortify", &repoInfo) 168 assert.NoError(t, err) 169 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 170 assert.Equal(t, "com.sap.fortify", repoInfo.repo) 171 assert.Equal(t, "Testing", repoInfo.owner) 172 }) 173 174 t.Run("Invalid ssh URL as no org/owner passed", func(t *testing.T) { 175 var repoInfo RepoInfo 176 assert.Error(t, getGitRepoInfo("git@github.com/fortify", &repoInfo)) 177 }) 178 } 179 180 func TestInitGitInfo(t *testing.T) { 181 t.Run("Valid URL1", func(t *testing.T) { 182 config := codeqlExecuteScanOptions{Repository: "https://github.hello.test/Testing/codeql.git", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"} 183 repoInfo, err := initGitInfo(&config) 184 assert.NoError(t, err) 185 assert.Equal(t, "abcd1234", repoInfo.commitId) 186 assert.Equal(t, "Testing", repoInfo.owner) 187 assert.Equal(t, "codeql", repoInfo.repo) 188 assert.Equal(t, "refs/head/branch", repoInfo.ref) 189 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 190 }) 191 192 t.Run("Valid URL2", func(t *testing.T) { 193 config := codeqlExecuteScanOptions{Repository: "https://github.hello.test/Testing/codeql", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"} 194 repoInfo, err := initGitInfo(&config) 195 assert.NoError(t, err) 196 assert.Equal(t, "abcd1234", repoInfo.commitId) 197 assert.Equal(t, "Testing", repoInfo.owner) 198 assert.Equal(t, "codeql", repoInfo.repo) 199 assert.Equal(t, "refs/head/branch", repoInfo.ref) 200 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 201 }) 202 203 t.Run("Valid url with dots URL1", func(t *testing.T) { 204 config := codeqlExecuteScanOptions{Repository: "https://github.hello.test/Testing/com.sap.codeql.git", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"} 205 repoInfo, err := initGitInfo(&config) 206 assert.NoError(t, err) 207 assert.Equal(t, "abcd1234", repoInfo.commitId) 208 assert.Equal(t, "Testing", repoInfo.owner) 209 assert.Equal(t, "com.sap.codeql", repoInfo.repo) 210 assert.Equal(t, "refs/head/branch", repoInfo.ref) 211 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 212 }) 213 214 t.Run("Valid url with dots URL2", func(t *testing.T) { 215 config := codeqlExecuteScanOptions{Repository: "https://github.hello.test/Testing/com.sap.codeql", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"} 216 repoInfo, err := initGitInfo(&config) 217 assert.NoError(t, err) 218 assert.Equal(t, "abcd1234", repoInfo.commitId) 219 assert.Equal(t, "Testing", repoInfo.owner) 220 assert.Equal(t, "com.sap.codeql", repoInfo.repo) 221 assert.Equal(t, "refs/head/branch", repoInfo.ref) 222 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 223 }) 224 225 t.Run("Valid url with username and token URL1", func(t *testing.T) { 226 config := codeqlExecuteScanOptions{Repository: "https://username:token@github.hello.test/Testing/codeql.git", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"} 227 repoInfo, err := initGitInfo(&config) 228 assert.NoError(t, err) 229 assert.Equal(t, "abcd1234", repoInfo.commitId) 230 assert.Equal(t, "Testing", repoInfo.owner) 231 assert.Equal(t, "codeql", repoInfo.repo) 232 assert.Equal(t, "refs/head/branch", repoInfo.ref) 233 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 234 }) 235 236 t.Run("Valid url with username and token URL2", func(t *testing.T) { 237 config := codeqlExecuteScanOptions{Repository: "https://username:token@github.hello.test/Testing/codeql", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"} 238 repoInfo, err := initGitInfo(&config) 239 assert.NoError(t, err) 240 assert.Equal(t, "abcd1234", repoInfo.commitId) 241 assert.Equal(t, "Testing", repoInfo.owner) 242 assert.Equal(t, "codeql", repoInfo.repo) 243 assert.Equal(t, "refs/head/branch", repoInfo.ref) 244 assert.Equal(t, "https://github.hello.test", repoInfo.serverUrl) 245 }) 246 247 t.Run("Invalid URL with no org/reponame", func(t *testing.T) { 248 config := codeqlExecuteScanOptions{Repository: "https://github.hello.test", AnalyzedRef: "refs/head/branch", CommitID: "abcd1234"} 249 repoInfo, err := initGitInfo(&config) 250 assert.NoError(t, err) 251 _, err = orchestrator.NewOrchestratorSpecificConfigProvider() 252 assert.Equal(t, "abcd1234", repoInfo.commitId) 253 assert.Equal(t, "refs/head/branch", repoInfo.ref) 254 if err != nil { 255 assert.Equal(t, "", repoInfo.owner) 256 assert.Equal(t, "", repoInfo.repo) 257 assert.Equal(t, "", repoInfo.serverUrl) 258 } 259 }) 260 } 261 262 func TestBuildRepoReference(t *testing.T) { 263 t.Run("Valid ref with branch", func(t *testing.T) { 264 repository := "https://github.hello.test/Testing/fortify" 265 analyzedRef := "refs/head/branch" 266 ref, err := buildRepoReference(repository, analyzedRef) 267 assert.NoError(t, err) 268 assert.Equal(t, "https://github.hello.test/Testing/fortify/tree/branch", ref) 269 }) 270 t.Run("Valid ref with PR", func(t *testing.T) { 271 repository := "https://github.hello.test/Testing/fortify" 272 analyzedRef := "refs/pull/1/merge" 273 ref, err := buildRepoReference(repository, analyzedRef) 274 assert.NoError(t, err) 275 assert.Equal(t, "https://github.hello.test/Testing/fortify/pull/1", ref) 276 }) 277 t.Run("Invalid ref without branch name", func(t *testing.T) { 278 repository := "https://github.hello.test/Testing/fortify" 279 analyzedRef := "refs/head" 280 ref, err := buildRepoReference(repository, analyzedRef) 281 assert.Error(t, err) 282 assert.ErrorContains(t, err, "Wrong analyzedRef format") 283 assert.Equal(t, "", ref) 284 }) 285 t.Run("Invalid ref without PR id", func(t *testing.T) { 286 repository := "https://github.hello.test/Testing/fortify" 287 analyzedRef := "refs/pull/merge" 288 ref, err := buildRepoReference(repository, analyzedRef) 289 assert.Error(t, err) 290 assert.ErrorContains(t, err, "Wrong analyzedRef format") 291 assert.Equal(t, "", ref) 292 }) 293 } 294 295 func getRepoReferences(repoInfo RepoInfo) (string, string, string) { 296 repoUrl := fmt.Sprintf("%s/%s/%s", repoInfo.serverUrl, repoInfo.owner, repoInfo.repo) 297 repoReference, _ := buildRepoReference(repoUrl, repoInfo.ref) 298 repoCodeqlScanUrl := fmt.Sprintf("%s/security/code-scanning?query=is:open+ref:%s", repoUrl, repoInfo.ref) 299 return repoUrl, repoReference, repoCodeqlScanUrl 300 } 301 func TestCreateToolRecordCodeql(t *testing.T) { 302 t.Run("Valid toolrun file", func(t *testing.T) { 303 repoInfo := RepoInfo{serverUrl: "https://github.hello.test", commitId: "test", ref: "refs/head/branch", owner: "Testing", repo: "fortify"} 304 repoUrl, repoReference, repoCodeqlScanUrl := getRepoReferences(repoInfo) 305 toolRecord, err := createToolRecordCodeql(newCodeqlExecuteScanTestsUtils(), repoInfo, repoUrl, repoReference, repoCodeqlScanUrl) 306 assert.NoError(t, err) 307 assert.Equal(t, toolRecord.ToolName, "codeql") 308 assert.Equal(t, toolRecord.ToolInstance, "https://github.hello.test") 309 assert.Equal(t, toolRecord.DisplayName, "Testing fortify - refs/head/branch test") 310 assert.Equal(t, toolRecord.DisplayURL, "https://github.hello.test/Testing/fortify/security/code-scanning?query=is:open+ref:refs/head/branch") 311 }) 312 t.Run("Empty repository URL", func(t *testing.T) { 313 repoInfo := RepoInfo{serverUrl: "", commitId: "test", ref: "refs/head/branch", owner: "Testing", repo: "fortify"} 314 repoUrl, repoReference, repoCodeqlScanUrl := getRepoReferences(repoInfo) 315 _, err := createToolRecordCodeql(newCodeqlExecuteScanTestsUtils(), repoInfo, repoUrl, repoReference, repoCodeqlScanUrl) 316 317 assert.Error(t, err) 318 assert.ErrorContains(t, err, "Repository not set") 319 }) 320 321 t.Run("Empty analyzedRef", func(t *testing.T) { 322 repoInfo := RepoInfo{serverUrl: "https://github.hello.test", commitId: "test", ref: "", owner: "Testing", repo: "fortify"} 323 repoUrl, repoReference, repoCodeqlScanUrl := getRepoReferences(repoInfo) 324 _, err := createToolRecordCodeql(newCodeqlExecuteScanTestsUtils(), repoInfo, repoUrl, repoReference, repoCodeqlScanUrl) 325 326 assert.Error(t, err) 327 assert.ErrorContains(t, err, "Analyzed Reference not set") 328 }) 329 330 t.Run("Empty CommitId", func(t *testing.T) { 331 repoInfo := RepoInfo{serverUrl: "https://github.hello.test", commitId: "", ref: "refs/head/branch", owner: "Testing", repo: "fortify"} 332 repoUrl, repoReference, repoCodeqlScanUrl := getRepoReferences(repoInfo) 333 _, err := createToolRecordCodeql(newCodeqlExecuteScanTestsUtils(), repoInfo, repoUrl, repoReference, repoCodeqlScanUrl) 334 335 assert.Error(t, err) 336 assert.ErrorContains(t, err, "CommitId not set") 337 }) 338 t.Run("Invalid analyzedRef", func(t *testing.T) { 339 repoInfo := RepoInfo{serverUrl: "https://github.hello.test", commitId: "", ref: "refs/branch", owner: "Testing", repo: "fortify"} 340 repoUrl, repoReference, repoCodeqlScanUrl := getRepoReferences(repoInfo) 341 _, err := createToolRecordCodeql(newCodeqlExecuteScanTestsUtils(), repoInfo, repoUrl, repoReference, repoCodeqlScanUrl) 342 343 assert.Error(t, err) 344 }) 345 } 346 347 func TestWaitSarifUploaded(t *testing.T) { 348 t.Parallel() 349 config := codeqlExecuteScanOptions{SarifCheckRetryInterval: 1, SarifCheckMaxRetries: 5} 350 t.Run("Fast complete upload", func(t *testing.T) { 351 codeqlScanAuditMock := CodeqlSarifUploaderMock{counter: 0} 352 timerStart := time.Now() 353 err := waitSarifUploaded(&config, &codeqlScanAuditMock) 354 assert.Less(t, time.Now().Sub(timerStart), time.Second) 355 assert.NoError(t, err) 356 }) 357 t.Run("Long completed upload", func(t *testing.T) { 358 codeqlScanAuditMock := CodeqlSarifUploaderMock{counter: 2} 359 timerStart := time.Now() 360 err := waitSarifUploaded(&config, &codeqlScanAuditMock) 361 assert.GreaterOrEqual(t, time.Now().Sub(timerStart), time.Second*2) 362 assert.NoError(t, err) 363 }) 364 t.Run("Failed upload", func(t *testing.T) { 365 codeqlScanAuditMock := CodeqlSarifUploaderMock{counter: -1} 366 err := waitSarifUploaded(&config, &codeqlScanAuditMock) 367 assert.Error(t, err) 368 assert.ErrorContains(t, err, "failed to upload sarif file") 369 }) 370 t.Run("Error while checking sarif uploading", func(t *testing.T) { 371 codeqlScanAuditErrorMock := CodeqlSarifUploaderErrorMock{counter: -1} 372 err := waitSarifUploaded(&config, &codeqlScanAuditErrorMock) 373 assert.Error(t, err) 374 assert.ErrorContains(t, err, "test error") 375 }) 376 t.Run("Completed upload after getting errors from server", func(t *testing.T) { 377 codeqlScanAuditErrorMock := CodeqlSarifUploaderErrorMock{counter: 3} 378 err := waitSarifUploaded(&config, &codeqlScanAuditErrorMock) 379 assert.NoError(t, err) 380 }) 381 t.Run("Max retries reached", func(t *testing.T) { 382 codeqlScanAuditErrorMock := CodeqlSarifUploaderErrorMock{counter: 6} 383 err := waitSarifUploaded(&config, &codeqlScanAuditErrorMock) 384 assert.Error(t, err) 385 assert.ErrorContains(t, err, "max retries reached") 386 }) 387 } 388 389 type CodeqlSarifUploaderMock struct { 390 counter int 391 } 392 393 func (c *CodeqlSarifUploaderMock) GetSarifStatus() (codeql.SarifFileInfo, error) { 394 if c.counter == 0 { 395 return codeql.SarifFileInfo{ 396 ProcessingStatus: "complete", 397 Errors: nil, 398 }, nil 399 } 400 if c.counter == -1 { 401 return codeql.SarifFileInfo{ 402 ProcessingStatus: "failed", 403 Errors: []string{"upload error"}, 404 }, nil 405 } 406 c.counter-- 407 return codeql.SarifFileInfo{ 408 ProcessingStatus: "pending", 409 Errors: nil, 410 }, nil 411 } 412 413 type CodeqlSarifUploaderErrorMock struct { 414 counter int 415 } 416 417 func (c *CodeqlSarifUploaderErrorMock) GetSarifStatus() (codeql.SarifFileInfo, error) { 418 if c.counter == -1 { 419 return codeql.SarifFileInfo{}, errors.New("test error") 420 } 421 if c.counter == 0 { 422 return codeql.SarifFileInfo{ 423 ProcessingStatus: "complete", 424 Errors: nil, 425 }, nil 426 } 427 c.counter-- 428 return codeql.SarifFileInfo{ProcessingStatus: "Service unavailable"}, nil 429 }