Transition Vulnerabilities, Permissions, CI-Tests, Dependency-Update-Tool, Code-Reviews to structured details (#889)

* move other checks togit add -u

* more checks

* fixes
This commit is contained in:
laurentsimon 2021-08-23 17:54:22 -07:00 committed by GitHub
parent 27c5821764
commit b731f450b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 148 additions and 29 deletions

View File

@ -36,8 +36,12 @@ func AutomaticDependencyUpdate(c *checker.CheckRequest) checker.CheckResult {
return checker.CreateRuntimeErrorResult(CheckDependencyUpdateTool, err) return checker.CreateRuntimeErrorResult(CheckDependencyUpdateTool, err)
} }
if !r { if !r {
c.Dlogger.Warn("dependabot not detected") c.Dlogger.Warn3(&checker.LogMessage{
c.Dlogger.Warn("renovatebot not detected") Text: "dependabot not detected",
})
c.Dlogger.Warn3(&checker.LogMessage{
Text: "renovatebot not detected",
})
return checker.CreateMinScoreResult(CheckDependencyUpdateTool, "no update tool detected") return checker.CreateMinScoreResult(CheckDependencyUpdateTool, "no update tool detected")
} }
@ -51,11 +55,23 @@ func fileExists(name string, dl checker.DetailLogger, data FileCbData) (bool, er
switch strings.ToLower(name) { switch strings.ToLower(name) {
case ".github/dependabot.yml": case ".github/dependabot.yml":
dl.Info("dependabot detected : %s", name) dl.Info3(&checker.LogMessage{
Path: name,
Type: checker.FileTypeSource,
// Source file must have line number > 0.
Offset: 1,
Text: "dependabot detected",
})
// https://docs.renovatebot.com/configuration-options/ // https://docs.renovatebot.com/configuration-options/
case ".github/renovate.json", ".github/renovate.json5", ".renovaterc.json", "renovate.json", case ".github/renovate.json", ".github/renovate.json5", ".renovaterc.json", "renovate.json",
"renovate.json5", ".renovaterc": "renovate.json5", ".renovaterc":
dl.Info("renovate detected: %s", name) dl.Info3(&checker.LogMessage{
Path: name,
Type: checker.FileTypeSource,
// Source file must have line number > 0.
Offset: 1,
Text: "renovate detected",
})
default: default:
// Continue iterating. // Continue iterating.
return true, nil return true, nil

View File

@ -90,7 +90,9 @@ func CITests(c *checker.CheckRequest) checker.CheckResult {
} }
if !foundCI { if !foundCI {
c.Dlogger.Debug("merged PR without CI test: %d", pr.Number) c.Dlogger.Debug3(&checker.LogMessage{
Text: fmt.Sprintf("merged PR without CI test: %d", pr.Number),
})
} }
} }
@ -116,8 +118,12 @@ func prHasSuccessStatus(pr *clients.PullRequest, c *checker.CheckRequest) (bool,
continue continue
} }
if isTest(status.GetContext()) { if isTest(status.GetContext()) {
c.Dlogger.Debug("CI test found: pr: %d, context: %success, url: %success", pr.Number, c.Dlogger.Debug3(&checker.LogMessage{
status.GetContext(), status.GetURL()) Path: status.GetURL(),
Type: checker.FileTypeURL,
Text: fmt.Sprintf("CI test found: pr: %d, context: %s", pr.Number,
status.GetContext()),
})
return true, nil return true, nil
} }
} }
@ -145,8 +151,12 @@ func prHasSuccessfulCheck(pr *clients.PullRequest, c *checker.CheckRequest) (boo
continue continue
} }
if isTest(cr.GetApp().GetSlug()) { if isTest(cr.GetApp().GetSlug()) {
c.Dlogger.Debug("CI test found: pr: %d, context: %success, url: %success", pr.Number, c.Dlogger.Debug3(&checker.LogMessage{
cr.GetApp().GetSlug(), cr.GetURL()) Path: cr.GetURL(),
Type: checker.FileTypeURL,
Text: fmt.Sprintf("CI test found: pr: %d, context: %s", pr.Number,
cr.GetApp().GetSlug()),
})
return true, nil return true, nil
} }
} }

View File

@ -74,11 +74,15 @@ func DoesCodeReview(c *checker.CheckRequest) checker.CheckResult {
func selectBestScoreAndReason(s1, s2 int, r1, r2 string, func selectBestScoreAndReason(s1, s2 int, r1, r2 string,
dl checker.DetailLogger) (int, string) { dl checker.DetailLogger) (int, string) {
if s1 > s2 { if s1 > s2 {
dl.Info(r2) dl.Info3(&checker.LogMessage{
Text: r2,
})
return s1, r1 return s1, r1
} }
dl.Info(r1) dl.Info3(&checker.LogMessage{
Text: r1,
})
return s2, r2 return s2, r2
} }
@ -101,7 +105,9 @@ func githubCodeReview(c *checker.CheckRequest) (int, string, error) {
foundApprovedReview := false foundApprovedReview := false
for _, r := range pr.Reviews { for _, r := range pr.Reviews {
if r.State == "APPROVED" { if r.State == "APPROVED" {
c.Dlogger.Debug("found review approved pr: %d", pr.Number) c.Dlogger.Debug3(&checker.LogMessage{
Text: fmt.Sprintf("found review approved pr: %d", pr.Number),
})
totalReviewed++ totalReviewed++
foundApprovedReview = true foundApprovedReview = true
break break
@ -113,7 +119,9 @@ func githubCodeReview(c *checker.CheckRequest) (int, string, error) {
// time on clicking the approve button. // time on clicking the approve button.
if !foundApprovedReview { if !foundApprovedReview {
if !pr.MergeCommit.AuthoredByCommitter { if !pr.MergeCommit.AuthoredByCommitter {
c.Dlogger.Debug("found pr with committer different than author: %d", pr.Number) c.Dlogger.Debug3(&checker.LogMessage{
Text: fmt.Sprintf("found pr with committer different than author: %d", pr.Number),
})
totalReviewed++ totalReviewed++
} }
} }
@ -184,7 +192,9 @@ func commitMessageHints(c *checker.CheckRequest) (int, string, error) {
} }
} }
if isBot { if isBot {
c.Dlogger.Debug("skip commit from bot account: %s", committer) c.Dlogger.Debug3(&checker.LogMessage{
Text: fmt.Sprintf("skip commit from bot account: %s", committer),
})
continue continue
} }
@ -194,7 +204,9 @@ func commitMessageHints(c *checker.CheckRequest) (int, string, error) {
commitMessage := commit.Message commitMessage := commit.Message
if strings.Contains(commitMessage, "\nReviewed-on: ") && if strings.Contains(commitMessage, "\nReviewed-on: ") &&
strings.Contains(commitMessage, "\nReviewed-by: ") { strings.Contains(commitMessage, "\nReviewed-by: ") {
c.Dlogger.Debug("Gerrit review found for commit '%s'", commit.SHA) c.Dlogger.Debug3(&checker.LogMessage{
Text: fmt.Sprintf("Gerrit review found for commit '%s'", commit.SHA),
})
totalReviewed++ totalReviewed++
continue continue
} }

View File

@ -70,7 +70,9 @@ func Contributors(c *checker.CheckRequest) checker.CheckResult {
names = append(names, c) names = append(names, c)
} }
c.Dlogger.Info("contributors work for: %v", strings.Join(names, ",")) c.Dlogger.Info3(&checker.LogMessage{
Text: fmt.Sprintf("contributors work for: %v", strings.Join(names, ",")),
})
reason := fmt.Sprintf("%d different companies found", len(companies)) reason := fmt.Sprintf("%d different companies found", len(companies))
return checker.CreateProportionalScoreResult(CheckContributors, reason, len(companies), numberCompaniesForTopScore) return checker.CreateProportionalScoreResult(CheckContributors, reason, len(companies), numberCompaniesForTopScore)

View File

@ -63,17 +63,38 @@ func validatePermission(key string, value interface{}, path string,
if strings.EqualFold(val, "write") { if strings.EqualFold(val, "write") {
if isPermissionOfInterest(key, ignoredPermissions) { if isPermissionOfInterest(key, ignoredPermissions) {
dl.Warn("'%v' permission set to '%v' in %v", key, val, path) dl.Warn3(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
// TODO: set line.
Offset: 1,
Text: fmt.Sprintf("'%v' permission set to '%v'", key, val),
// TODO: set Snippet.
})
recordPermissionWrite(key, pPermissions) recordPermissionWrite(key, pPermissions)
} else { } else {
// Only log for debugging, otherwise // Only log for debugging, otherwise
// it may confuse users. // it may confuse users.
dl.Debug("'%v' permission set to '%v' in %v", key, val, path) dl.Debug3(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
// TODO: set line.
Offset: 1,
Text: fmt.Sprintf("'%v' permission set to '%v'", key, val),
// TODO: set Snippet.
})
} }
return nil return nil
} }
dl.Info("'%v' permission set to '%v' in %v", key, val, path) dl.Info3(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
// TODO: set line correctly.
Offset: 1,
Text: fmt.Sprintf("'%v' permission set to '%v'", key, val),
// TODO: set Snippet.
})
return nil return nil
} }
@ -113,15 +134,37 @@ func validatePermissions(permissions interface{}, path string,
// Empty string is nil type. // Empty string is nil type.
// It defaults to 'none' // It defaults to 'none'
case nil: case nil:
dl.Info("permissions set to 'none' in %v", path) dl.Info3(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
// TODO: set line correctly.
Offset: 1,
Text: "permissions set to 'none'",
// TODO: set Snippet.
})
// String type. // String type.
case string: case string:
if !strings.EqualFold(val, "read-all") && val != "" { if !strings.EqualFold(val, "read-all") && val != "" {
dl.Warn("permissions set to '%v' in %v", val, path) dl.Warn3(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
// TODO: set line correctly.
Offset: 1,
Text: fmt.Sprintf("permissions set to '%v'", val),
// TODO: set Snippet.
})
recordAllPermissionsWrite(pPermissions) recordAllPermissionsWrite(pPermissions)
return nil return nil
} }
dl.Info("permission set to '%v' in %v", val, path)
dl.Info3(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
// TODO: set line correctly.
Offset: 1,
Text: fmt.Sprintf("permissions set to '%v'", val),
// TODO: set Snippet.
})
// Map type. // Map type.
case map[interface{}]interface{}: case map[interface{}]interface{}:
@ -142,7 +185,12 @@ func validateTopLevelPermissions(config map[interface{}]interface{}, path string
// Check if permissions are set explicitly. // Check if permissions are set explicitly.
permissions, ok := config["permissions"] permissions, ok := config["permissions"]
if !ok { if !ok {
dl.Warn("no permission defined in %v", path) dl.Warn3(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
Offset: 1,
Text: "no permission defined",
})
recordAllPermissionsWrite(pdata.topLevelWritePermissions) recordAllPermissionsWrite(pdata.topLevelWritePermissions)
return nil return nil
} }
@ -178,7 +226,12 @@ func validateRunLevelPermissions(config map[interface{}]interface{}, path string
// so only top-level read-only permissions need to be declared. // so only top-level read-only permissions need to be declared.
permissions, ok := job["permissions"] permissions, ok := job["permissions"]
if !ok { if !ok {
dl.Debug("no permission defined in %v", path) dl.Debug3(&checker.LogMessage{
Path: path,
Type: checker.FileTypeSource,
Offset: 1,
Text: "no permission defined",
})
continue continue
} }
err := validatePermissions(permissions, path, dl, err := validatePermissions(permissions, path, dl,
@ -396,10 +449,22 @@ func isSARIFUploadWorkflow(s, fp string, dl checker.DetailLogger) bool {
// CodeQl run externally and SARIF file uploaded. // CodeQl run externally and SARIF file uploaded.
func isSARIFUploadAction(s, fp string, dl checker.DetailLogger) bool { func isSARIFUploadAction(s, fp string, dl checker.DetailLogger) bool {
if strings.Contains(s, "github/codeql-action/upload-sarif@") { if strings.Contains(s, "github/codeql-action/upload-sarif@") {
dl.Debug("codeql SARIF upload workflow detected: %v", fp) dl.Debug3(&checker.LogMessage{
Path: fp,
Type: checker.FileTypeSource,
// TODO: set line.
Offset: 1,
Text: "codeql SARIF upload workflow detected",
// TODO: set Snippet.
})
return true return true
} }
dl.Debug("not a codeql upload SARIF workflow: %v", fp) dl.Debug3(&checker.LogMessage{
Path: fp,
Type: checker.FileTypeSource,
Offset: 1,
Text: "not a codeql upload SARIF workflow",
})
return false return false
} }
@ -409,10 +474,22 @@ func isSARIFUploadAction(s, fp string, dl checker.DetailLogger) bool {
// https://docs.github.com/en/code-security/secure-coding/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning. // https://docs.github.com/en/code-security/secure-coding/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning.
func isCodeQlAnalysisWorkflow(s, fp string, dl checker.DetailLogger) bool { func isCodeQlAnalysisWorkflow(s, fp string, dl checker.DetailLogger) bool {
if strings.Contains(s, "github/codeql-action/analyze@") { if strings.Contains(s, "github/codeql-action/analyze@") {
dl.Debug("codeql workflow detected: %v", fp) dl.Debug3(&checker.LogMessage{
Path: fp,
Type: checker.FileTypeSource,
// TODO: set line.
Offset: 1,
Text: "codeql workflow detected",
// TODO: set Snippet.
})
return true return true
} }
dl.Debug("not a codeql workflow: %v", fp) dl.Debug3(&checker.LogMessage{
Path: fp,
Type: checker.FileTypeSource,
Offset: 1,
Text: "not a codeql workflow",
})
return false return false
} }

View File

@ -99,7 +99,9 @@ func HasUnfixedVulnerabilities(c *checker.CheckRequest) checker.CheckResult {
// TODO: take severity into account. // TODO: take severity into account.
vulnIDs := osvResp.getVulnerabilities() vulnIDs := osvResp.getVulnerabilities()
if len(vulnIDs) > 0 { if len(vulnIDs) > 0 {
c.Dlogger.Warn("HEAD is vulnerable to %s", strings.Join(vulnIDs, ", ")) c.Dlogger.Warn3(&checker.LogMessage{
Text: fmt.Sprintf("HEAD is vulnerable to %s", strings.Join(vulnIDs, ", ")),
})
return checker.CreateMinScoreResult(CheckVulnerabilities, "existing vulnerabilities detected") return checker.CreateMinScoreResult(CheckVulnerabilities, "existing vulnerabilities detected")
} }