redhat-plumbers-in-action / differential-shellcheck

🐚 GitHub Action for running ShellCheck differentially
GNU General Public License v3.0
55 stars 9 forks source link
bash differential-scans docker full-scans github-action linter sarif sast shell shellcheck shellcheck-action

Differential ShellCheck

Differential ShellCheck

GitHub Marketplace Lint Code Base Unit Tests

OSSF-Scorecard Score OpenSSF Best Practices codecov

This repository hosts code for running Differential ShellCheck in GitHub Actions. Idea of having something like a Differential ShellCheck was first introduced in @fedora-sysv/initscripts. Initscripts needed some way to verify incoming Pull Requests without getting warnings and errors about already merged and for years working code. Therefore, Differential ShellCheck was born.

How does it work

First Differential ShellCheck gets a list of changed shell scripts based on file extensions, shebangs and script list, if provided. Then it calls @koalaman/shellcheck on those scripts where it stores ShellCheck output for later use. Then it switches from HEAD to provided BASE and runs ShellCheck on the same files as before and stores output to a separate file.

To evaluate results, Differential ShellCheck uses utilities csdiff and csgrep from @csutils/csdiff. First csdiff is used to get a list/number of fixed and added errors. And then csgrep is used to output the results in a nice colorized way to console and optionally into GitHub GUI as a security alert.

Features

Usage

Example of running Differential ShellCheck:

name: Differential ShellCheck
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

permissions:
  contents: read

jobs:
  lint:
    runs-on: ubuntu-latest

    permissions:
      # required for all workflows
      security-events: write

      # only required for workflows in private repositories
      actions: read
      contents: read

    steps:
      - name: Repository checkout
        uses: actions/checkout@v4
        with:
          # Differential ShellCheck requires full git history
          fetch-depth: 0

      - id: ShellCheck
        name: Differential ShellCheck
        uses: redhat-plumbers-in-action/differential-shellcheck@v5
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

      - if: always()
        name: Upload artifact with ShellCheck defects in SARIF format
        uses: actions/upload-artifact@v4
        with:
          name: Differential ShellCheck SARIF
          path: ${{ steps.ShellCheck.outputs.sarif }}

[!IMPORTANT]

fetch-depth: 0 is required to run differential-shellcheck successfully. It fetches all git history.

Console output example

Console output example

Example of Job Summary

Example of Job Summary

Example of output in Changed files tab

Example of output in Changed files tab

Example of @github-code-scanning bot review comment

Example of @github-code-scanning bot review comment

Real life examples of usage

Configuration options

Action currently accepts following options:

# ...

- name: Differential ShellCheck
  uses: redhat-plumbers-in-action/differential-shellcheck@v5
  with:
    triggering-event: <name of triggering event>
    base: <sha1>
    head: <sha1>
    pull-request-base: <sha1>
    pull-request-head: <sha1>
    push-event-base: <sha1>
    push-event-head: <sha1>
    diff-scan: <true or false>
    strict-check-on-push: <true or false>
    external-sources: <true or false>
    severity: <minimal severity level>
    scan-directory: <list of paths>
    exclude-path: <list of paths>
    include-path: <list of paths>
    token: <GitHub token>

# ...

triggering-event

The name of the event that triggered the workflow run. Supported values are: merge_group, pull_request, push and manual.

base

SHA1 of the commit which will be used as the base when performing differential ShellCheck. Input is used only when triggering-event is set to manual.

head

SHA1 of the commit which refers to the HEAD of changes. Input is used only when triggering-event is set to manual.

merge-group-base

SHA1 of the merge group's parent commit. Input is used when triggering-event is set to merge_group.

merge-group-head

SHA1 of the merge group commit. Input is used when triggering-event is set to merge_group.

pull-request-base

SHA1 of the top commit on the base branch. Input is used when triggering-event is set to pull_request.

pull-request-head

SHA1 of the latest commit in Pull Request. Input is used when triggering-event is set to pull_request.

push-event-base

SHA1 of the last commit before the push. Input is used when triggering-event is set to push.

push-event-head

SHA1 of the last commit after push. Input is used when triggering-event is set to push.

diff-scan

Input allows requesting a specific type of scan. Input is considered only if triggering-event is set to manual.

Default types of scans based on triggering-event input:

triggering-event type of scan
merge_group differential
pull_request differential
push full
manual based on diff-scan input

strict-check-on-push

Differential ShellCheck performs full scans when running on a push event, but the Action fails only when new defects are added. This option allows overwriting this behavior. Hence when strict-check-on-push is set to true it will fail when any defect is discovered.

external-sources

Enable following of source statements even when the file is not specified as input. By default, ShellCheck will only follow files specified on the command-line (plus /dev/null). This option allows following any file the script may source. This option may also be enabled using external-sources=true in .shellcheckrc.

severity

Minimal severity level of detected errors that will be reported. Valid values in order of severity are error, warning, info and style.

scan-directory

List of relative paths to directories that will be scanned for shell scripts. Globbing is supported. The list is a multi-line string, not a YAML list.

By default the whole repository is scanned. This feature is useful when you want to scan only a subset of the repository.

This feature is fully compatible with exclude-path and include-path options.

exclude-path

List of relative paths excluded from ShellCheck scanning. Globbing is supported. The list is a multi-line string, not a YAML list.

include-path

List of file paths that will be scanned by ShellCheck. Globbing is supported. The list is a multi-line string, not a YAML list.

display-engine

Tool used to display the defects and fixes in the console output. Currently supported tools are csgrep and sarif-fmt.

csgrep output example

`display-engine: csgrep`

sarif-fmt output example

`display-engine: sarif-fmt`

token

The token is used to upload findings in SARIF format to GitHub.

The token needs to have the following permissions:

[!TIP]

When the token isn't passed, the SARIF file won't be uploaded (the GitHub Security Dashboard won't be updated), but the Action will work as expected. SARIF file can also be uploaded manually using sarif from outputs and github/codeql-action/upload-sarif GitHub Action.

Outputs

Differential ShellCheck exposes following outputs.

sarif

Relative path to the SARIF file containing detected defects. Example of how to use sarif output within the workflow:

- id: ShellCheck
  name: Differential ShellCheck
  uses: redhat-plumbers-in-action/differential-shellcheck@v5

- if: always()
  name: Upload artifact with ShellCheck defects in SARIF format
  uses: actions/upload-artifact@v4
  with:
    name: Differential ShellCheck SARIF
    path: ${{ steps.ShellCheck.outputs.sarif }}

- if: always()
  name: Upload SARIF to GitHub using github/codeql-action/upload-sarif
  uses: github/codeql-action/upload-sarif@v2
  with:
    sarif_file: ${{ steps.ShellCheck.outputs.sarif }}

[!TIP]

sarif output can be used together with tools like microsoft/sarif-tools to convert SARIF to other formats like codeclimate, csv, docx and more. Example of use.

html

Relative path to the HTML file containing detected defects. Example of how to use html output within the workflow:

- id: ShellCheck
  name: Differential ShellCheck
  uses: redhat-plumbers-in-action/differential-shellcheck@v5

- if: always()
  name: Upload artifact with ShellCheck defects in HTML format
  uses: actions/upload-artifact@v4
  with:
    name: Differential ShellCheck HTML
    path: ${{ steps.ShellCheck.outputs.html }}

Example of HTML output:

HTML output example

Using with Private repositories

Differential ShellCheck GitHub Action could be used in private repositories by any user. But code scanning-related features are available only for GitHub Enterprise users, as mentioned in GitHub Documentation:

Code scanning is available for all public repositories on GitHub.com. Code scanning is also available for private repositories owned by organizations that use GitHub Enterprise Cloud and have a license for GitHub Advanced Security. For more information, see "About GitHub Advanced Security".

Using with Visual Studio Code

Differential ShellCheck doesn't have a Visual Studio Code plugin, but results can be accessed by using SARIF Viewer Visual Studio Code extension provided by Microsoft. Once installed, you have to connect your GitHub account with Visual Studio Code. Then, if you open a repository that uses Differential ShellCheck, you will see reported defects directly in your Visual Studio Code IDE.

Visual Studio Code SARIF connect

Visual Studio Code SARIF results

Limitations


Useful documents: CHANGELOG | ARCHITECTURE | CONTRIBUTING | _CODE_OF_CONDUCT | SECURITY | LICENSE_