anchore / scan-action

Anchore container analysis and scan provided as a GitHub Action
MIT License
197 stars 75 forks source link

Automatic feedback of scan results as PR comment #162

Open harmw opened 2 years ago

harmw commented 2 years ago

I'm looking at ways go improve engagement (around security) and one way is to involve my devs a little more in everything security.

Currently the results of a scan (can) go to the GitHub security dashboard, which is fine, but how about a sub action to create a little markdown comment inside the PR?

Simply this:

anchore/scan-action/pr-comment@0

This would look for a sarif file, extract the required bits and post a comment in the PR thread. There's a markdown field inside this file which we may want to use, or write out a different message altogether.

Thoughts?

kzantow commented 2 years ago

@harmw sorry I missed this earlier. This is a great idea! I think this could probably be a part of the main action, though, instead of a sub-action, just with an option; would that work too?

harmw commented 2 years ago

yeah totally, just figured having it separate would make it easier to develop (separation of concerns, decoupling) 🙂 (but my JS skills are sub-par)

kzantow commented 2 years ago

The distinction between a sub-action and the main one to me is where you would generally use it in a workflow. For PR comments, you'd use it when the action runs and wouldn't need a separate action. I really do like this idea and was hoping to do something similar for the sbom-action, I guess I'll have to read up on how to accomplish this in the GitHub API!

harmw commented 2 years ago

just as an FYI, here's something (completely unrelated) that I'm using inside some actions:

const ok_output = `#### Linter: \`${{ steps.linter.outcome }}\``;
const fail_output = `
\`\`\`
${process.env.RESULTS}
\`\`\`
`;
let output = ok_output;

if ("${{ steps.linter.outcome }}" != "success") {
  output += fail_output;
};

github.rest.issues.createComment({
  issue_number: context.issue.number,
  owner: context.repo.owner,
  repo: context.repo.repo,
  body: output
})
guizmaii commented 1 year ago

Any news? I'm very interested in this feature :)

tgerla commented 1 year ago

Hi @guizmaii, we don't have this on our short term roadmap right now but maybe the solution above from @harmw would be sufficient? If not, we would definitely be happy to look at a pull request for this as a built in feature. Thanks!

mortenhauberg commented 1 year ago

Hi,

This was exactly what I was looking for. But apparently, I'm too dumb to make the suggestion by @harmw work 🤦🏻‍♂️

I have a feeling that there's something obvious I'm missing. How do I capture the table output?

I've tried something like this, and I do get the output, but as a single line

# I have other steps to generate the SBOM and download Grype and it works as expected
 - name: Scan SBOM
    id: scan
    run: echo table=$(${{ steps.grype.outputs.cmd }} -o table --fail-on medium sbom:sbom.spdx.json) >> $GITHUB_OUTPUT

My Google-foo is failing me. Can someone educate me 😅?

harmw commented 1 year ago

this is how we're doing the scan-sbom-and-write-a-comment:

    - name: Scan SBOM
      id: scanner
      uses: anchore/scan-action@v3
      continue-on-error: true
      with:
        fail-build: true
        severity-cutoff: critical
        sbom: "${{ github.event.repository.name }}_sbom.spdx.json"

    - run: mv ${{ steps.scanner.outputs.sarif }} ${{ github.event.repository.name }}.sarif

    - name: Update PR with vulnerability scan results
      uses: actions/github-script@v6
      if: github.event_name == 'pull_request'
      with:
        script: |
          let fs = require('fs')
          let sarif_file = '${{ github.event.repository.name }}.sarif'
          let sarif

          try {
            sarif = JSON.parse(fs.readFileSync(sarif_file, 'utf8'));
          } catch(e) {
            console.log(e)
          }

          let output = `:microscope: vulnerability scan result: **failure in parsing report**`

          if (typeof(sarif) == 'object') {
            const issues = sarif.runs[0].results.length > 0 ? sarif.runs[0].results.length : 0
            output = `:microscope: vulnerability scan result: \`${issues}\` issue(s) found `

            if (issues > 0) {
              let table = '\n'
              table += '<details><summary>View details...</summary>\n'
              table += '\n'
              table += '| Severity | Description | Resolution |\n'
              table += '|----------|-------------|------------|\n'

              let criticals = 0
              let highs = 0
              for (run of sarif.runs) {
                  for (rule of run.tool.driver.rules) {
                      let description = rule.shortDescription.text
                      let resolution = rule.fullDescription.text
                      let severity = rule.properties['security-severity']
                      table += `| ${severity} | ${description} | ${resolution} |\n`
                      if (description.toLowerCase().indexOf('critical vulnerability') > -1) {
                        criticals++
                      }
                      if (description.toLowerCase().indexOf('high vulnerability') > -1) {
                        highs++
                      }
                  }
              }
              output += highs > 0 ? ':warning:' : ''

              if (criticals > 0) {
                output += `\n:pause_button: **one or more vulnerabilities found with label _critical_, pausing build. Please [resolve](https://insert.url.here/how-to-guides/code-scanning) these to continue.** :rotating_light:`
              }

              output += table
              output += '</details>\n\n'

              output += `<sub>:bulb: _please check the [developers hub](https://insert.url.here/) on how to work with the \`grype\` vulnerability scanner_</sub>`

              github.rest.issues.createComment({
                issue_number: context.issue.number,
                owner: context.repo.owner,
                repo: context.repo.repo,
                body: output
              })
            }
          }

I now know there are better options out there, but this got us moving in the right direction 😅

mortenhauberg commented 1 year ago

Perfect - thanks! I thought there would be a way to capture the table output. This will do the trick.

Thanks a lot

MPV commented 8 months ago

Another idea, annotations:

MPV commented 8 months ago

Or opening issues, as done by many actions for Trivy:

kkovaletp commented 4 weeks ago

This is how I've implemented the comment functionality for PRs: (workflow, example of the comment)

Key features: