Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 73 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,92 @@

Run [SpotBugs](https://spotbugs.readthedocs.io/en/latest/) as a Github action.

## Inputs

### outputType

Output type for the report. It can be 'xml', 'html', 'sarif', 'emacs'
or 'xdocs'. Default value is 'sarif' as it is the used by GitHub Advanced
Security.

> default: 'sarif' <br/>
> required: true

### packages

Comma separated list of packages to scan. It will fill the
-onlyAnalyze parameter in spotbugs. It can contain the wildcards '\*' and
'-': com.example.\* for single package or com.example.- for all
subpackages.

> If not specified, it will scan all packages.

See more at https://spotbugs.readthedocs.io/en/stable/running.html#text-ui-options

### arguments

A string with any additional command arguments to be sent to [spotbugs](https://spotbugs.readthedocs.io/en/stable/running.html#text-ui-options)

### output

The output filename. If not specified, it will use the default name 'results.[EXTENSION]'

### target

It can be a file or a directory, it is usually the ./target folder where you compiled your project.

### dependenciesPath

Path to the dependencies folder. For example, for Maven it is usually stored
in the `~/.m2` folder.

### basePath

The basePath is used as a prefix in the sarif file to help GitHub find the
right file of the issue. It is tipically something like 'src/main/java'.

## Example usage

This workflow would analyze a Java application that builds a set of
packages under the com.example package name and outputs the results in
sarif format to upload it to the GitHub Security tab:

```yaml
name: SpotBugs

on: [push, pull_request]

jobs:
spotbugs-analyze:
spotbugs-analyze:
name: Analyze
runs-on: ubuntu-latest
steps:
steps:

# checkout and build the project
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn clean package -B -Dmaven.test.skip

- name: Run SpotBugs
uses: spotbugs/spotbugs-github-action@v1
# Run SpotBugs and upload the SARIF file
- name: Run SpotBugs action
if: always()
uses: abirismyname/spotbugs-github-action@v2
with:
arguments: '-sarif'
target: './HelloWorld.jar'
output: 'results.sarif'
spotbugs-version: 'latest'
packages: com.example.-
target: ./target
dependenciesPath: ~/.m2
basePath: src/main/java

- name: Upload analysis results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v1
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: ${{github.workspace}}/results.sarif
```
51 changes: 43 additions & 8 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,47 @@ inputs:
description: 'SpotBugs version to use.'
default: 'latest'
required: false
packages:
description: >
Comma separated list of packages to scan. It will fill the
-onlyAnalyze parameter in spotbugs. It can contain the wildcards '*' and
'-': com.example.* for single package or com.example.- for all
subpackages.

If not specified, it will scan all packages.
See more: https://spotbugs.readthedocs.io/en/stable/running.html#text-ui-options
required: false
arguments:
description: 'Command arguments to be sent to SpotBugs'
required: true
default: ''
description: >
A string with any additional command arguments to be sent to spotbugs.
See more: https://spotbugs.readthedocs.io/en/stable/running.html#text-ui-options
required: false
output:
description: 'Output file name'
required: true
description: >
The output filename. If not specified, it will use the default name
'results.EXTENSION'
target:
description: 'Target of what you want to analyze'
required: true
description: >
Target of what you want to analyze. It can be a file or a directory, it
is usually the ./target folder where you compiled your project.
required: false
outputType:
description: >
Output type for the report. It can be 'xml', 'html', 'sarif', 'emacs'
or 'xdocs'. Default value is 'sarif' as it is the used by GitHub Advanced
Security.
default: 'sarif'
required: true
dependenciesPath:
description: >
Path to the dependencies folder. For Maven it is usually stored in the
'~/.m2' folder.
required: false
basePath:
description: >
The basePath is used as a prefix in the sarif file to help GitHub find the
right file of the issue. It is tipically something like 'src/main/java'.
required: false
runs:
using: "composite"
steps:
Expand All @@ -26,6 +57,10 @@ runs:
shell: bash
env:
SPOTBUGS_VERSION: ${{ inputs.spotbugs-version }}
PACKAGES: ${{ inputs.packages }}
OUTPUT: ${{ inputs.output }}
OUTPUT_TYPE: ${{ inputs.outputType }}
ARGUMENTS: ${{ inputs.arguments }}
TARGET: ${{ inputs.target }}
TARGET: ${{ inputs.target }}
DEPENDENCIES_PATH: ${{ inputs.dependenciesPath }}
BASE_PATH: ${{ inputs.basePath }}
98 changes: 94 additions & 4 deletions analyze.sh
Original file line number Diff line number Diff line change
@@ -1,16 +1,106 @@
#!/bin/bash

# set com.example.demo and all chid packages (.- means all children, .* this package only)
# PACKAGES="com.example.demo.-"
# source path to prepend to the class path
# BASEPATH="src/main/java"
# DEPENDENCIES_PATH="~/.m2"
# OUTPUT_TYPE="sarif"

# Check whether to use latest version of PMD
if [ "$SPOTBUGS_VERSION" == 'latest' ]; then
if [ "$SPOTBUGS_VERSION" == 'latest' ] || [ "$SPOTBUGS_VERSION" == "" ]; then
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way the default behavior downloads the latest version

LATEST_TAG="$(curl -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/spotbugs/spotbugs/releases/latest | jq --raw-output '.tag_name')"
SPOTBUGS_VERSION=$LATEST_TAG
fi

# Download SpotBugs
wget https://github.com/spotbugs/spotbugs/releases/download/"${SPOTBUGS_VERSION}"/spotbugs-"${SPOTBUGS_VERSION}".zip
unzip spotbugs-"${SPOTBUGS_VERSION}".zip
wget -q -N https://github.com/spotbugs/spotbugs/releases/download/"${SPOTBUGS_VERSION}"/spotbugs-"${SPOTBUGS_VERSION}".zip
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change serves to avoid a too long log that is not related to the actual SAST review

unzip -q -o spotbugs-"${SPOTBUGS_VERSION}".zip

# Run SpotBugs
SPOTBUGS_HOME=spotbugs-"${SPOTBUGS_VERSION}"
SPOTBUGS=${SPOTBUGS_HOME}/bin/spotbugs
sh $SPOTBUGS -textui -output "${OUTPUT}" "${ARGUMENTS}" "${TARGET}"

#sh $SPOTBUGS -textui -output "${OUTPUT}" "${ARGUMENTS}" "${TARGET}"

# Take care of parameter order, sometimes does not work if you change it

CMD="java -Xmx1900M -Dlog4j2.formatMsgNoLookups=true \
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we start building the SpotBugs command line depending on the provided parameters

-jar ${SPOTBUGS_HOME}/lib/spotbugs.jar -textui "

if [ "$PACKAGES" != "" ]; then
CMD="$CMD -onlyAnalyze ${PACKAGES}"
fi

CMD="$CMD -quiet -effort:max -low -noClassOk"

case $OUTPUT_TYPE in
"xml")
if [ "$OUTPUT" == "" ]; then
OUTPUT="results.xml"
fi
CMD="$CMD -xml:withMessages=./$OUTPUT"
;;
"html")
if [ "$OUTPUT" == "" ]; then
OUTPUT="results.html"
fi
CMD="$CMD -html:withMessages=./$OUTPUT"
;;
"emacs")
if [ "$OUTPUT" == "" ]; then
OUTPUT="results.emacs"
fi
CMD="$CMD -emacs:withMessages=./$OUTPUT"
;;
"xdocs")
if [ "$OUTPUT" == "" ]; then
OUTPUT="results.xdocs"
fi
CMD="$CMD -xdoc:withMessages=./$OUTPUT"
;;
*)
OUTPUT_TYPE="sarif"
if [ "$OUTPUT" == "" ]; then
OUTPUT="results.sarif"
fi
CMD="$CMD -sarif:withMessages=./resultspre.sarif"
;;
esac

if [ "$DEPENDENCIES_PATH" != "" ]; then
DEP_CMD="find ${DEPENDENCIES_PATH} -name \"*.jar\" -type f > /tmp/jardependencies.txt"
echo "Scanning jars with: ${DEP_CMD}"
eval ${DEP_CMD}
CMD="$CMD -auxclasspathFromFile /tmp/jardependencies.txt"
fi

if [ "$BASE_PATH" != "" ]; then
if [[ "$BASE_PATH" != */ ]]; then
BASE_PATH="$BASE_PATH/"
fi
# using sourcepath does not work for GitHub's sarif parser
# but keeping there just in case
CMD="$CMD -sourcepath ${BASE_PATH}"
fi

if [ "$ARGUMENTS" != "" ]; then
CMD="$CMD ${ARGUMENTS}"
fi

if [ "$TARGET" != "" ]; then
CMD="$CMD ${TARGET}"
else
CMD="$CMD ."
fi

echo "Running SpotBugs with command: $CMD"

eval ${CMD}

if [ "$OUTPUT_TYPE" == "sarif" ] && [ "$BASE_PATH" != "" ]; then
# prepend the pyhsical path
echo "Transform sarif file to include the physical path"
jq -c "(.runs[].results[].locations[].physicalLocation.artifactLocation.uri) |=\"$BASE_PATH\"+." resultspre.sarif > "$OUTPUT"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to provide compatibility with the GitHub SARIF parser that needs the base path as a prefix (typically src/main/java)

fi