diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/_unsupported/README-polaris-unsupported.md b/_unsupported/README-polaris-unsupported.md new file mode 100644 index 0000000..f1fd7cd --- /dev/null +++ b/_unsupported/README-polaris-unsupported.md @@ -0,0 +1,80 @@ +## Coverity on Polaris + +Run a Coverity SAST scan on the Polaris platform as part of your pipeline. There are two instances of this recipe: + +- coverity-on-polaris-github-hosted.yml - Runs Coverity on a GitHub-hosted agent. **Note: The overhead of running an incremental analysis in this way can be prohibitive due to the Coverity tools download size. If you plan to use incremental analysis it is recommended that you use a self-hosted runner.** +- coverity-on-polaris-self-hosted.yml - Runs Coverity on a self-hosted runner. **Note: This requires a specific docker image (provided in this repository) to be used as the self-hosted runner. Instructions for using this are included below.** + +The following secrets must be set in your repository or organization: + +| Secret name | Description | +| --- | --- | +| POLARIS_URL | Set this to your individual customer Polaris URL (e.g. customer.polaris.synopsys.com) | +| POLARIS_URL | Set this to your Polaris access token | + +The following environment variables may be set in your instance of the workflows: + +| Variable | Description | +| --- | --- | +| SECURITY_GATE_ARGS | The default value is "--new" which will return all newly introduced security issues. TODO: Explain options here | + +These templates both us the Polaris command line utility to perform an "auto capture" of your source code (no need to understand how the software is built) and uploads the source code and dependencies to Polaris for analysis. They are configured with different behavior for different scenarios: + +### Build for master branch + +When performing a build for the master branch, a full Coverity analysis will be run and SARIF will be generated for all issues found. SARIF is only supported if you are using a public repository or have licensed GitHub Advanced Security including CodeQL and Dependabot. If new issues are found matching the "security gate" parameter, an exit code will be returned to indicate the pipeline has failed. + +TODO: Set a GitHub-specific "status" code of success or error based on the seucrity testing results and security gate policy. + +![Screen shot showing Coverity results imported into GitHub](artifacts/coverity-on-polaris-sarif-master.png) +![Screen shot showing Coverity results imported into GitHub](artifacts/coverity-on-polaris-sarif-master2.png) + +### Build for a pull request + +When performing a build to validate a pull request, an incremental analysis will be run on only the changed files, SARIF will be generated if you are using GitHub Advanced Security, and the pull request will be annotated with comments to direct the developer to issues that may prevent a merge (this is available even if you are not using GitHub Advanced Security). + +TODO: Set a GitHub-specific "status" code of success or error based on the seucrity testing results and security gate policy in case SARIF is not being used. + +![Screen shot showing Coverity results annotated in a pull request](artifacts/coverity-on-polaris-comment-on-pr.png) + +### Using the Self-Hosted Runner + +Polaris incremental analysis is a technology preview, and at this time requires the analysis to be run locally. When using an ephemeral GitHub-hosted runner, the Coverity analysis tools must be downloaded and installed at a cost of ~ 1.5 GB for each job invocation which greatly reduces the performance gains of using incremental analysis, and places a great burden on the network infrastructure. In theory GitHub caching could be used to store a local copy, but this is not reliable as when the cache reaches its quota limit files may begin to be deleted at GtiHub's discretion. + +To work around this and provide the best performance, it is recommended to host your own self-hosted runners. We provide [a Docker image](docker/coverity-on-polaris-runner/) that is configured with the necessary tools to run the GitHub runner software, plus on invocation will prime itself with the latest version of the Coverity analysis tools. This means that each instance of the runner you initailize, a copy of the 1.5 GB software will be downloaded, but it will be re-used for every job the runner services. If a new version of the Coverity tools is published while the runner is in service, it will be downlaoded on the fly. The job currently being serviced will take a hit, but each subseqent job will run quickly. + +The Docker image may be used as follows. + +First, build the image. For example: +``` +docker build -t coverity-on-polaris-runner . +``` + +Next, launch the runner with the following environment variables to connect it to your GitHub environment. This example will run it by hand, but it is expected this would be run from a management infrastructure. + +To use the runner for a specific repository: + +``` +docker run --name coverity-on-polaris-runner \ + -e GITHUB_OWNER=[username or organization] \ + -e GITHUB_REPOSITORY=[repository name] \ + -e GITHUB_PAT=[GitHub Personal Access Token] \ + -e POLARIS_URL=[Your individual customer Polaris URL - this will ONLY be used for initialzation] \ + -e POLARIS_ACCESS_TOKEN=[Your Polaris Access Token - this will ONLY be used for initialzation] \ + coverity-on-polaris-runner +``` + +To use the runner organization-wide, leave out the repository: + +``` +docker run --name coverity-on-polaris-runner \ + -e GITHUB_OWNER=[username or organization] \ + -e GITHUB_PAT=[GitHub Personal Access Token] \ + -e POLARIS_URL=[Your individual customer Polaris URL - this will ONLY be used for initialzation] \ + -e POLARIS_ACCESS_TOKEN=[Your Polaris Access Token - this will ONLY be used for initialzation] \ + coverity-on-polaris-runner +``` + +### Support + +For questions and comments, please contact us via the [Polaris Integrations Forum](https://community.synopsys.com/s/topic/0TO2H000000gM3oWAE/polaris-integrations). diff --git a/_unsupported/coverity-on-polaris-github-hosted.yml b/_unsupported/coverity-on-polaris-github-hosted.yml new file mode 100644 index 0000000..a4675d3 --- /dev/null +++ b/_unsupported/coverity-on-polaris-github-hosted.yml @@ -0,0 +1,75 @@ +name: Coverity on Polaris with Self-Hosted Runner + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + env: + POLARIS_URL: ${{ secrets.POLARIS_URL }} + POLARIS_ACCESS_TOKEN: ${{ secrets.POLARIS_ACCESS_TOKEN }} + SECURITY_GATE_ARGS: --new + SYNOPSYS_GITHUB_TOOLS_REPO: https://github.com/synopsys-sig-community/synopsys-github-tools + + steps: + - uses: actions/checkout@v2 + + - name: Download and configure Polaris CLI + run: | + curl -LsS -o polaris.zip $POLARIS_URL/api/tools/polaris_cli-linux64.zip + unzip -j -d polaris-cli polaris.zip + export CI_COMMIT_REF_NAME=$GITHUB_REF + ./polaris-cli/polaris --persist-config --co capture.build.buildCommands="null" --co capture.build.cleanCommands="null" --co capture.fileSystem="null" --co capture.coverity.autoCapture="enable" --co serverUrl=$POLARIS_URL configure + + - name: Coverity Scan (Full analysis) + if: ${{github.event_name == 'push'}} + run: ./polaris-cli/polaris analyze -w + + - id: changeset + if: ${{ github.event_name == 'pull_request' }} + name: Get Pull Request Changeset + uses: jitterbit/get-changed-files@v1 + with: + format: json + + - name: Coverity Scan (Incremental) + if: ${{ github.event_name == 'pull_request' }} + run: | + echo ${{ steps.changeset.outputs.added_modified }} + readarray -t changed_files <<<"$(jq -r '.[]' <<<'${{ steps.changeset.outputs.added_modified }}')" + for changed_file in ${changed_files[@]}; do + echo ${changed_file} >> polaris-files-to-scan.txt + echo "Scan changed file ${changed_file}." + done + export POLARIS_FF_ENABLE_COVERITY_INCREMENTAL=true + export CI_COMMIT_REF_NAME=$GITHUB_REF + ./polaris-cli/polaris analyze -w --incremental polaris-files-to-scan.txt + + - name: Get Synopsys GitHub Tools + run: | + git clone -q --depth 1 $SYNOPSYS_GITHUB_TOOLS_REPO + pip3 install --upgrade pandas requests==2.26.0 urllib3==1.26.7 jsonapi-requests==0.6.2 tenacity==6.2.0 pygithub + + - name: Export Coverity Results to SARIF (Full) + run: python3 ./synopsys-github-tools/github-export-polaris-issues.py + if: ${{github.event_name == 'push'}} + + - name: Export Coverity Results to SARIF (Incremental) + run: python3 ./synopsys-github-tools/github-export-coverity-issues.py --coverity-json ./.synopsys/polaris/data/coverity/*/idir/incremental-results/incremental-results.json --polaris --comment-on-github-pr + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: ${{github.event_name == 'pull_request'}} + continue-on-error: true + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v1 + with: + # Path to SARIF file relative to the root of the repository + sarif_file: synopsys-coverity-github-sarif.json diff --git a/_unsupported/coverity-on-polaris-self-hosted.yml b/_unsupported/coverity-on-polaris-self-hosted.yml new file mode 100644 index 0000000..a7ca0ac --- /dev/null +++ b/_unsupported/coverity-on-polaris-self-hosted.yml @@ -0,0 +1,75 @@ +name: Coverity on Polaris with Self-Hosted Runner + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + + workflow_dispatch: + +jobs: + build: + runs-on: [self-hosted, coverity] + + env: + POLARIS_URL: ${{ secrets.POLARIS_URL }} + POLARIS_ACCESS_TOKEN: ${{ secrets.POLARIS_ACCESS_TOKEN }} + SECURITY_GATE_ARGS: --new + SYNOPSYS_GITHUB_TOOLS_REPO: https://github.com/synopsys-sig-community/synopsys-github-tools + + steps: + - uses: actions/checkout@v2 + + - name: Download and configure Polaris CLI + run: | + curl -LsS -o polaris.zip $POLARIS_URL/api/tools/polaris_cli-linux64.zip + unzip -j -d polaris-cli polaris.zip + export CI_COMMIT_REF_NAME=$GITHUB_REF + ./polaris-cli/polaris --persist-config --co capture.build.buildCommands="null" --co capture.build.cleanCommands="null" --co capture.fileSystem="null" --co capture.coverity.autoCapture="enable" --co serverUrl=$POLARIS_URL configure + + - name: Coverity Scan (Full analysis) + if: ${{github.event_name == 'push'}} + run: ./polaris-cli/polaris analyze -w + + - id: changeset + if: ${{ github.event_name == 'pull_request' }} + name: Get Pull Request Changeset + uses: jitterbit/get-changed-files@v1 + with: + format: json + + - name: Coverity Scan (Incremental) + if: ${{ github.event_name == 'pull_request' }} + run: | + echo ${{ steps.changeset.outputs.added_modified }} + readarray -t changed_files <<<"$(jq -r '.[]' <<<'${{ steps.changeset.outputs.added_modified }}')" + for changed_file in ${changed_files[@]}; do + echo ${changed_file} >> polaris-files-to-scan.txt + echo "Scan changed file ${changed_file}." + done + export POLARIS_FF_ENABLE_COVERITY_INCREMENTAL=true + export CI_COMMIT_REF_NAME=$GITHUB_REF + ./polaris-cli/polaris analyze -w --incremental polaris-files-to-scan.txt + + - name: Get Synopsys GitHub Tools + run: | + git clone -q --depth 1 $SYNOPSYS_GITHUB_TOOLS_REPO + pip3 install --upgrade pandas requests==2.26.0 urllib3==1.26.7 jsonapi-requests==0.6.2 tenacity==6.2.0 pygithub + + - name: Export Coverity Results to SARIF (Full) + run: python3 ./synopsys-github-tools/github-export-polaris-issues.py + if: ${{github.event_name == 'push'}} + + - name: Export Coverity Results to SARIF (Incremental) + run: python3 ./synopsys-github-tools/github-export-coverity-issues.py --coverity-json ./.synopsys/polaris/data/coverity/*/idir/incremental-results/incremental-results.json --polaris --comment-on-github-pr + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: ${{github.event_name == 'pull_request'}} + continue-on-error: true + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v1 + with: + # Path to SARIF file relative to the root of the repository + sarif_file: synopsys-coverity-github-sarif.json diff --git a/_unsupported/docker/coverity-on-polaris-runner/Dockerfile b/_unsupported/docker/coverity-on-polaris-runner/Dockerfile new file mode 100644 index 0000000..580c28d --- /dev/null +++ b/_unsupported/docker/coverity-on-polaris-runner/Dockerfile @@ -0,0 +1,35 @@ +#FROM openjdk:slim +#COPY --from=python:3.6-slim / / + +FROM ubuntu:18.04 + +ENV GITHUB_PAT "" +ENV GITHUB_TOKEN "" +ENV GITHUB_OWNER "" +ENV GITHUB_REPOSITORY "" +ENV RUNNER_WORKDIR "_work" +ENV RUNNER_LABELS "coverity" +ENV POLARIS_ACCESS_TOKEN "" +ENV POLARIS_URL "" + +RUN apt-get update \ + && apt-get install -y openjdk-8-jdk python3 python3-pip \ + && apt-get install -y curl unzip git jq sudo \ + && useradd -m runner \ + && usermod -aG sudo runner \ + && echo "%sudo ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +#RUN python3 -m ensurepip +RUN pip3 install --upgrade pip && pip3 install requests==2.26.0 urllib3==1.26.7 jsonapi-requests==0.6.2 tenacity==6.2.0 pygithub + +USER runner +WORKDIR /home/runner + +RUN GITHUB_RUNNER_VERSION=$(curl --silent "https://api.github.com/repos/actions/runner/releases/latest" | jq -r '.tag_name[1:]') \ + && curl -Ls https://github.com/actions/runner/releases/download/v${GITHUB_RUNNER_VERSION}/actions-runner-linux-x64-${GITHUB_RUNNER_VERSION}.tar.gz | tar xz \ + && sudo ./bin/installdependencies.sh + +COPY --chown=runner:runner entrypoint.sh run.sh ./ +RUN sudo chmod u+x ./entrypoint.sh ./run.sh + +ENTRYPOINT ["/home/runner/entrypoint.sh"] diff --git a/_unsupported/docker/coverity-on-polaris-runner/build.sh b/_unsupported/docker/coverity-on-polaris-runner/build.sh new file mode 100644 index 0000000..bb56fe7 --- /dev/null +++ b/_unsupported/docker/coverity-on-polaris-runner/build.sh @@ -0,0 +1,2 @@ +#!/bin/sh -x +docker build -t coverity-on-polaris-runner . diff --git a/_unsupported/docker/coverity-on-polaris-runner/entrypoint.sh b/_unsupported/docker/coverity-on-polaris-runner/entrypoint.sh new file mode 100644 index 0000000..65dbede --- /dev/null +++ b/_unsupported/docker/coverity-on-polaris-runner/entrypoint.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +registration_url="https://github.com/${GITHUB_OWNER}" +token_url="https://api.github.com/orgs/${GITHUB_OWNER}/actions/runners/registration-token" + +if [ -n "${GITHUB_TOKEN}" ]; then + echo "Using given GITHUB_TOKEN" + + if [ -z "${GITHUB_REPOSITORY}" ]; then + echo "When using GITHUB_TOKEN, the GITHUB_REPOSITORY must be set" + return + fi + + registration_url="https://github.com/${GITHUB_OWNER}/${GITHUB_REPOSITORY}" + export RUNNER_TOKEN=$GITHUB_TOKEN + +else + if [ -n "${GITHUB_REPOSITORY}" ]; then + token_url="https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPOSITORY}/actions/runners/registration-token" + registration_url="${registration_url}/${GITHUB_REPOSITORY}" + fi + + echo "Requesting token at '${token_url}'" + + payload=$(curl -sX POST -H "Authorization: token ${GITHUB_PAT}" ${token_url}) + export RUNNER_TOKEN=$(echo $payload | jq .token --raw-output) + +fi + +if [ -z "${RUNNER_NAME}" ]; then + RUNNER_NAME=$(hostname) +fi + +./config.sh \ + --name "${RUNNER_NAME}" \ + --token "${RUNNER_TOKEN}" \ + --url "${registration_url}" \ + --work "${RUNNER_WORKDIR}" \ + --labels "${RUNNER_LABELS}" \ + --unattended \ + --replace + +# Initialize Polaris + +POLARIS_DOWNLOAD=$POLARIS_URL/api/tools/polaris_cli-linux64.zip +curl -LsS -o polaris.zip $POLARIS_DOWNLOAD +unzip -j -d polaris-cli polaris.zip + +mkdir temp-src && cd temp-src && ../polaris-cli/polaris --persist-config --co capture.build.buildCommands="null" --co capture.build.cleanCommands="null" --co capture.fileSystem="null" --co serverUrl=$POLARIS_URL configure && cd .. +export POLARIS_FF_ENABLE_COVERITY_INCREMENTAL=true +cd temp-src && echo Foo.java > changeset.txt && ../polaris-cli/polaris analyze -w --coverity-ignore-capture-failure --incremental ./changeset.txt || cd .. || true + +cleanup() { + if [ -n "${GITHUB_TOKEN}" ]; then + export REMOVE_TOKEN=$GITHUB_TOKEN + else + payload=$(curl -sX POST -H "Authorization: token ${GITHUB_PAT}" ${token_url%/registration-token}/remove-token) + export REMOVE_TOKEN=$(echo $payload | jq .token --raw-output) + fi + + ./config.sh remove --unattended --token "${REMOVE_TOKEN}" +} + +trap 'cleanup; exit 130' INT +trap 'cleanup; exit 143' TERM + +./run.sh "$*" & + +wait $! diff --git a/_unsupported/docker/coverity-on-polaris-runner/run.sh b/_unsupported/docker/coverity-on-polaris-runner/run.sh new file mode 100644 index 0000000..d429d07 --- /dev/null +++ b/_unsupported/docker/coverity-on-polaris-runner/run.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +trap 'kill -INT $PID' TERM INT + +if [ -f ".path" ]; then + export PATH=`cat .path` + echo ".path=${PATH}" +fi + +# Insert Sunopsys PATH variables here +# TODO + +# run the host process which keep the listener alive +./externals/node12/bin/node ./bin/RunnerService.js & +PID=$! +wait $PID +trap - TERM INT +wait $PID diff --git a/artifacts/coverity-on-polaris-comment-on-pr.png b/artifacts/coverity-on-polaris-comment-on-pr.png new file mode 100644 index 0000000..9644f00 Binary files /dev/null and b/artifacts/coverity-on-polaris-comment-on-pr.png differ diff --git a/artifacts/coverity-on-polaris-sarif-master.png b/artifacts/coverity-on-polaris-sarif-master.png new file mode 100644 index 0000000..64f8e45 Binary files /dev/null and b/artifacts/coverity-on-polaris-sarif-master.png differ diff --git a/artifacts/coverity-on-polaris-sarif-master2.png b/artifacts/coverity-on-polaris-sarif-master2.png new file mode 100644 index 0000000..15c7d8c Binary files /dev/null and b/artifacts/coverity-on-polaris-sarif-master2.png differ diff --git a/blackduck-intelligent-scheduled.yml b/blackduck-intelligent-scheduled.yml new file mode 100644 index 0000000..3177edd --- /dev/null +++ b/blackduck-intelligent-scheduled.yml @@ -0,0 +1,39 @@ +name: Black Duck Intelligent Scan + +on: + # Run intelligent scans on schedule at 11:05 pm every night + schedule: + - cron: '5 23 * * *' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + # Set up JDK 11 in runtime environment + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + + # Run Synopsys Detect + - name: Run Synopsys Detect + uses: synopsys-sig/detect-action@main + # This server does not require a local CA Cert + #env: + # NODE_EXTRA_CA_CERTS: ${{ secrets.LOCAL_CA_CERT_PATH }} + with: + scan-mode: INTELLIGENT + github-token: ${{ secrets.GITHUB_TOKEN }} + detect-version: 7.12.0 + blackduck-url: ${{ secrets.BLACKDUCK_URL }} + blackduck-api-token: ${{ secrets.BLACKDUCK_API_TOKEN }} \ No newline at end of file diff --git a/blackduck.yml b/blackduck.yml new file mode 100644 index 0000000..cc4387a --- /dev/null +++ b/blackduck.yml @@ -0,0 +1,70 @@ +name: Black Duck + +on: + # Run Black Duck on pushes to main branches, and pull requests that are going + # to be merged to main branches. + push: + branches: [ master, main ] + pull_request: + branches: [ master, main ] + + # Allow ad-hoc, manual invocations of the Coverity workflow. + workflow_dispatch: + +jobs: + build: + # The type of runner that the job will run on - Black Duck can also run on Windows and macOS + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + # Set up JDK 11 in runtime environment, necessary for running the Detect tool (invoked by the + # detect plugin) + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + + - name: Black Duck (Rapid scan) + if: ${{ github.event_name == 'pull_request' }} + uses: synopsys-sig/detect-action@main + # For pull requests, limit the feedback to policy violations newly introduced by code changes + # within the PR. This option will compare the results found in the PR scan to the results found + # in the last central full/intelligent scan, and only report the new violations. This is helpful + # to keep developers focused in their day to day on their code changes and not overwhelm them with + # findings they may not be responsible for. + # + # See https://community.synopsys.com/s/document-item?bundleId=integrations-detect&topicId=properties%2Fconfiguration%2Fblackduck-server.html&_LANG=enus + # and search for COMPARE_MODE for more details. + env: + DETECT_BLACKDUCK_RAPID_COMPARE_MODE: BOM_COMPARE_STRICT + # You can use this option to configure a CA cert if using a self-hosted runner and self-signed + # certificates on your Hub server. This option should point to a file on the local filesystem + # with the CA chain cert + #env: + # NODE_EXTRA_CA_CERTS: ${{ secrets.LOCAL_CA_CERT_PATH }} + with: + scan-mode: RAPID + github-token: ${{ secrets.GITHUB_TOKEN }} + detect-version: 7.12.0 + blackduck-url: ${{ secrets.BLACKDUCK_URL }} + blackduck-api-token: ${{ secrets.BLACKDUCK_API_TOKEN }} + + - name: Black Duck (Full scan) + if: ${{ github.event_name != 'pull_request' }} + uses: synopsys-sig/detect-action@main + # You can use this option to configure a CA cert if using a self-hosted runner and self-signed + # certificates on your Hub server. This option should point to a file on the local filesystem + # with the CA chain cert + #env: + # NODE_EXTRA_CA_CERTS: ${{ secrets.LOCAL_CA_CERT_PATH }} + with: + scan-mode: INTELLIGENT + github-token: ${{ secrets.GITHUB_TOKEN }} + detect-version: 7.12.0 + blackduck-url: ${{ secrets.BLACKDUCK_URL }} + blackduck-api-token: ${{ secrets.BLACKDUCK_API_TOKEN }} \ No newline at end of file diff --git a/coverity-simple.yml b/coverity-simple.yml new file mode 100644 index 0000000..9497c8e --- /dev/null +++ b/coverity-simple.yml @@ -0,0 +1,64 @@ +name: Coverity with Self-Hosted Runner +on: + push: + branches: [ master, main ] + + pull_request: + branches: [ master, main ] + +jobs: + build: + runs-on: [self-hosted] + + env: + COVERITY_CHECKERS: --webapp-security + COVERITY_URL: ${{ secrets.COVERITY_URL }} + COV_USER: ${{ secrets.COVERITY_USER }} + COVERITY_PASSPHRASE: ${{ secrets.COVERITY_PASSPHRASE }} + + steps: + - uses: actions/checkout@v2 + + - name: Coverity Scan (Full analysis) + if: ${{ github.event_name != 'pull_request' }} + shell: bash + run: | + export COVERITY_STREAM_NAME=${GITHUB_REPOSITORY##*/}-${GITHUB_REF##*/} + cov-capture --dir idir --project-dir . + cov-analyze --dir idir --strip-path `pwd` $COVERITY_CHECKERS + cov-commit-defects --dir idir --ticker-mode none --url ${{ secrets.COVERITY_URL }} --on-new-cert trust --stream \ + $COVERITY_STREAM_NAME --scm git --description "GitHub Workflow $GITHUB_WORKFLOW for $GITHUB_REPO" --version $GITHUB_SHA + cov-format-errors --dir idir --json-output-v7 coverity-results.json + - name: Get Pull Request Changeset + if: ${{ github.event_name == 'pull_request' }} + id: changeset + uses: jitterbit/get-changed-files@v1 + + - name: Coverity Scan (Incremental analysis) + if: ${{github.event_name == 'pull_request'}} + run: | + export COVERITY_STREAM_NAME=${GITHUB_REPOSITORY##*/}-${{ github.base_ref }} + for changed_file in ${{ steps.changeset.outputs.added_modified }}; do + echo ${changed_file} >> coverity-files-to-scan.txt + echo "Scan changed file ${changed_file}." + done + cov-capture --dir idir --project-dir . + cov-run-desktop --dir idir --strip-path `pwd` --url ${{ secrets.COVERITY_URL }} --stream $COVERITY_STREAM_NAME --present-in-reference false \ + --ignore-uncapturable-inputs true \ + --json-output-v7 coverity-results.json \ + $COVERITY_CHECKERS \ + ${{ steps.changeset.outputs.added_modified }} + + - name: Coverity Pull Request Feedback + uses: synopsys-sig/coverity-report-output-v7-json@v0.0.1 + with: + # The following parameters are REQUIRED + json-file-path: ./coverity-results.json + github-token: ${{ secrets.GITHUB_TOKEN }} + # If the following optional parameters are specified, the results from the JSON output will be + # compared to the baseline issues in the specified project, and only NEW issues will be reported + # in the pull request. + coverity-url: ${{ secrets.COVERITY_URL }} + coverity-project-name: ${{ github.event.repository.name }} + coverity-username: ${{ secrets.COV_USER }} + coverity-password: ${{ secrets.COVERITY_PASSPHRASE }} \ No newline at end of file diff --git a/coverity/README.md b/coverity/README.md new file mode 100644 index 0000000..090dabf --- /dev/null +++ b/coverity/README.md @@ -0,0 +1,169 @@ +# Coverity GitHub Template + +The Coverity GitHub Template provides a fully functioning reference implementation of Coverity running within a GitHub +workflow, implemented as a fully documented composite GitHub Action. This means that you can use this template as an +Action to easily implement it in your workflows, but because it contains only GitHub configuration as code, you can +also copy it and modify it to suit your needs. + +In this template we have attempted to document all of our known Coverity & GitHub best practices, and provide options +so that you can easily enable/disable various components. However, Coverity supports a very wide range of deployment +scenarios, and it is possible that this will not meet your needs as-is. In such case, you are welcome to clone or fork +this repo and modify to suit your needs - you can then reference your copy from your pipeline! + +## Quick Start + +To get started, you can include the template in your pipelines by referencing it as a GitHub Action. +Replace `` with the version of the template you would like to use. You can use `master` to reference the latest version, +but be advised that this is dangerous as it may introduce changes that disrupt your pipeline. + +To start using this action, add the following step to your existing GitHub workflow. This enables assumes the specified +Coverity credentials and some reasonable options, which are described in depth in following sections: + +``` + - uses: synopsys-sig-community/synopsys-github-templates/coverity@ + with: + coverity-url: ${{ secrets.COVERITY_URL }} + coverity-user: ${{ secrets.COVERITY_USER }} + coverity-passphrase: ${{ secrets.COVERITY_PASSPHRASE }} + coverity-project-name: ${{ github.event.repository.name }} + coverity-checker-options: --webapp-security + create-stream-and-project: true + github-token: ${{ secrets.GITHUB_TOKEN }} +``` + +The template assumes the following secrets to be set, providing the location and credentials for your Coverity +Connect instance: + +| Variable | Description | +| --- | --- | +| COVERITY_URL | URL to your Coverity Connect instance | +| COVERITY_USER | User name for authenticating to Coverity Connect | +| COVERITY_PASSPHRASE | Password for authenticating to Coverity Connect | + +The GITHUB_TOKEN secret does not need to be set by you - this is automatically set during workflow +execution to a token suitable for workflow automation to use. + +**Note:** This template assumes that it will be running in the context of a self-hosted GitHub runner, with the Coverity tools +installed, configured and available in the PATH. Due to the large footprint of the traditional Coverity toolchain, we +recommend using a self-hosted runner. A separate template will be made available for use with the new Coverity Scan Service. + +## Capabilities + +The Coverity GitHub Template supports the following capabilities: + +### Synthesize Stream and Project Names + +In order to speed deployment to new projects, the template can create stream and project names automatically based on the +GitHub environment. The names are derived as follows: + +| Name | Value | +| --- | --- | +| **Stream Name** | The stream name will be derived from the repository name AND the branch name, e.g.:
`[repo name]-[branch-name]`

Specifically, this is set to:
`${GITHUB_REPOSITORY##*/}-${GITHUB_REF##*/}`

For pull requests, the base ref is used:
`${GITHUB_REPOSITORY##*/}-${{ github.base_ref }}`| +| **Project Name** | The project name will be derived from the repository name only, e.g.
`[repo-name]`

Specifically, this is set to:
`${GITHUB_REPOSITORY##*/}` | + +You can override these synthetic names by specifying the following options to the template: + +| Option | Description | +| --- | --- | +| **coverity-stream-name** | Specify the name of the stream to commit to | +| **coverity-project-name** | Specify the name of the project where results will be available | + +### Automatically Create Streams and Projects + +Also in order to speed deployment to new projects, the template can create the streams and projects if they do not exist +on the Coverity Connect server. This can be very helpful for on-boarding new projects - you can deploy without having any +project specific data in the GitHub workflows, and without doing any pre-preparation on the server. Simply install the template +and go! + +The Coverity Connect credentials used must have permission to manage streams and projects. + +Automatic stream and project creation is off by default and must be enabled with the following option: + +| Option | Description | +| --- | --- | +| **create-stream-and-project** | Enable automatic stream and project creation using either the names specified or synthesized.

Must be `true` or `false` (Case sensitive) | + +### Build Capture or Auto Capture + +Coverity has the ability to analyze your code with or without a build. Running with a build can provide +more accurate results, as we identify precisely the components that go into your shipping software, whereeas +if we run without a build we will leverage the source code and package manager for information about how your +software is composed. This can lead to assumptions and less accurate or complete results. + +The default behavior of the template, if no build command is specified, is to automatically find the source +code and dependencies based on source code and package manager files. + +To enable build capture, please use the following option: + +| Option | Description | +| --- | --- | +| **build-command** | Enable build capture, using the value provided as the build command. This is passed directly to cov-build. For example:

`mvn clean && mvn package` | +| **cov-build-options** | Pass command line parameters to cov-build. These are passed directly to cov-build, before providing the build command | + +### Incremental Analysis and Developer Feedback for Pull Requests + +The template will apply Coverity incremental analysis (cov-run-desktop) instead of a full analysis (cov-analyze) when running +in the context of a pull request. This will provide fast turnaround time, and focus developers on the newly introduced issues +rather than everything in the project. + +The [Coverity Report Output V7 JSON Action](https://github.com/synopsys-sig/coverity-report-output-v7-json) is used to +provide feedback to developers. + +![Coverity Review Feedback Screenshot](reviewComment.png) + +**Note:** A full capture is still used during pull requests - only the analysis step is incremental. For many projects +this is a good trade-off - Java for example tends to have fast build times, and by capturing the entire project the incremental +analysis will have to make fewer assumptions about the code being analyzed, leading to higher confidence results. + +### Integrate with GitHub Advanced Security + +GitHub Advanced Security provides native support for static analysis findings through SARIF import. If you are using public +repos, or have licensed GitHub Advanced Security in addition to Coverity, this can provide a nice native workflow for managing +Coverity SAST findings within GitHub. These results are available for pushes as well as pull requests. + +To enable this integration with GitHub Advanced Security, please use the following option: + +| Option | Description | +| --- | --- | +| **generate-sarif** | Enable SARIF export for GitHub, set to `true` or `false` (or leave out) | + + +If you are not using public repos or are licensed for GitHub Advanced Security, the Coverity GitHub Action invoked on pull requests +provides the same level of detail and remediation advice for developers. + +![Code Scanning Alert Screenshot](codeScanningAlert.png) + +### Security Gate + +The template has the ability to "fail the build" if a security policy is not met. Rather than generate an exit code that will +mark the entire pipeline as failed, a GitHub status check is created called "Coverity Policy Check" and this is set according to +the security gate. This allows your build and other workflows to each have their own status, and a branch protection policy can be +set to block merges if the policy is not met. + +![Security Gate Screenshot](securityGate.png) + +#### Pushes and Full Scans + +To enable a security gate on pushes with full scans, first configure a saved view in Coverity Connect that represents your security +policy. Any defects that match the saved view's filters, for the project being tested, will be counted as policy violations: + +![Coverity Saved View Screenshot](coveritySavedView.png) + +Then specify the name of the saved view in the template options: + +| Option | Description | +| --- | --- | +| **security-gate-view-name** | Specify the name of the saved view in Coverity Connect to use for the push security gate | + +#### Pull Requests and Incremental Analysis + +Due to the typically low volume of findings returned from an incremental analysis, the security policy for pull requests +with incremental analysis is any *new* issues. + +### Other options + +The template has a few additional options that do not fit in the above categories: + +| Option | Description | +| --- | --- | +| **cov-analyze-options** | Pass command line parameters to cov-analyze and cov-run-desktop. | diff --git a/coverity/action.yml b/coverity/action.yml new file mode 100644 index 0000000..016114b --- /dev/null +++ b/coverity/action.yml @@ -0,0 +1,333 @@ + name: "coverity-github-template" + description: "Template for running Coverity in GitHub" + + inputs: + coverity-url: + description: "URL to Coverity Connect instance" + required: true + coverity-user: + description: "Coverity Connect user with permission to create streams and commit results" + required: true + coverity-passphrase: + description: "Coverity Connect user password" + required: true + build-command: + description: "Optional build command - if not specified, auto capture will be used" + required: false + cov-build-options: + description: "Optional command line options for cov-build" + required: false + cov-analyze-options: + description: "Optional command line arguments for cov-analyze" + required: false + security-gate-view-name: + description: "Name of saved view in Coverity Connect to use for security gate" + required: false + coverity-checker-options: + description: "Command line arguments to pass to Coverity analysis" + required: false + coverity-stream-name: + description: "Name of Coverity stream" + required: false + coverity-project-name: + description: "Name of Coverity project" + required: false + generate-sarif: + description: "Optional SARIF output step - true or false" + required: false + github-token: + description: "GitHub Access Token" + required: true + diagnostic-mode: + description: "Attach diagnostics to workflow run" + required: false + create-stream-and-project: + description: "Create stream and project - true or false" + required: false + + runs: + using: "composite" + steps: + + # Initialize a status check to Pending using the third party + # commit-status-updater action. + + - uses: ouzi-dev/commit-status-updater@v1.1.0 + with: + name: "Coverity Policy Check" + + # Create streams and projects in Coverity Connect if they do not + # already exist. This allows your workflow to be neutral and not contain + # any project specific details, which in turn can allow you to on- + # board new projects simply by installing this template. + # + # The project name will be derived from the repository name, e.g.: + # project-name + # The stream name will be derived from the repository name AND the + # branch name. e.g.: + # project-name-branch-name + # + # This requires that the user credentials used has permission to create + # streams. + # + # A project is necessary for the security gate to work, as it must + # reference a project when opening the saved view. + + - name: Create Coverity Stream + if: ${{ github.event_name != 'pull_request' }} + shell: bash + run: | + if [ "${{ inputs.create-stream-and-project }}" == "true" ]; then + echo + echo ======================================================================================== + echo == Initialize Coverity project and stream + echo ======================================================================================== + echo + if [ "${{ inputs.coverity-stream-name }}" == "" ]; then + export COVERITY_STREAM_NAME=${GITHUB_REPOSITORY##*/}-${GITHUB_REF##*/} + else + export COVERITY_STREAM_NAME=${{ inputs.coverity-stream-name }} + fi + if [ "${{ inputs.coverity-project-name }}" == "" ]; then + export COVERITY_PROJECT_NAME=${GITHUB_REPOSITORY##*/} + else + export COVERITY_PROJECT_NAME=${{ inputs.coverity-project-name }} + fi + echo Ensure that project "$COVERITY_PROJECT_NAME" exists + cov-manage-im --url ${{ inputs.coverity-url }} --on-new-cert trust --mode projects --add --set name:"$COVERITY_PROJECT_NAME" || true + echo Ensure that stream "$COVERITY_STREAM_NAME" exists + cov-manage-im --url ${{ inputs.coverity-url }} --on-new-cert trust --mode streams --add -set name:"$COVERITY_STREAM_NAME" || true + cov-manage-im --url ${{ inputs.coverity-url }} --on-new-cert trust --mode projects --update --name "$COVERITY_PROJECT_NAME" --insert stream:"$COVERITY_STREAM_NAME" || true + fi + env: + COV_USER: ${{ inputs.coverity-user }} + COVERITY_PASSPHRASE: ${{ inputs.coverity-passphrase }} + + # Always run either a full build or auto-capture. A partial capture is possible for incremental analysis, + # but this will impact the results further. For C/C++ and large Java projects however this may be required + # and is left as an exercise to the reader. + - name: Coverity Capture + shell: bash + run: | + if [ "${{ inputs.build-command }}" == "" ]; then + echo + echo ======================================================================================== + echo == Run Coverity AUTO Capture + echo ======================================================================================== + echo + cov-capture --dir idir --project-dir . + else + echo + echo ======================================================================================== + echo == Run Coverity BUILD Capture: ${{ inputs.build-command }} + echo ======================================================================================== + echo + cov-build --dir idir ${{ inputs.cov-build-options }} ${{ inputs.build-command }} + fi + + # On pushes to a main branch, run a Full Coverity analysis using auto + # capture. This will scan the filesystem to determine what kind of + # project(s) are present and how to capture them for analysis. + # + # The results will be committed to the stream specified in the + # previous step. + # + # Results will be saved into coverity-full-results.json for + # potential processing. + + - name: Coverity Scan (Full analysis) + if: ${{ github.event_name != 'pull_request' }} + shell: bash + run: | + echo + echo ======================================================================================== + echo == Run FULL Coverity analysis for ${GITHUB_REPOSITORY##*/}-${GITHUB_REF##*/} + echo ======================================================================================== + echo + if [ "${{ inputs.coverity-stream-name }}" == "" ]; then + export COVERITY_STREAM_NAME=${GITHUB_REPOSITORY##*/}-${GITHUB_REF##*/} + else + export COVERITY_STREAM_NAME=${{ inputs.coverity-stream-name }} + fi + cov-analyze --dir idir --strip-path `pwd` ${{ inputs.coverity-checker-options }} ${{ inputs.cov-analyze-options }} + cov-commit-defects --dir idir --ticker-mode none --url ${{ inputs.coverity-url }} --on-new-cert trust --stream \ + $COVERITY_STREAM_NAME --scm git --description "GitHub Workflow $GITHUB_WORKFLOW for $GITHUB_REPO" --version $GITHUB_SHA + cov-format-errors --dir idir --json-output-v7 coverity-results.json + env: + COV_USER: ${{ inputs.coverity-user }} + COVERITY_PASSPHRASE: ${{ inputs.coverity-passphrase }} + + # Use the helpful third party Jitterbit Get Changed Files action to + # determine what files have been added or modified. + + - name: Get Pull Request Changeset + if: ${{ github.event_name == 'pull_request' }} + id: changeset + uses: jitterbit/get-changed-files@v1 + + # On a pull request, run an incremental analysis. This uses auto + # capture as well, and references a stream that was presumably + # created in advance by a push to this repo, using the stream name + # based on the repository and main branch name (e.g. "master"). + # + # This implementation uses a full capture rather than a partial + # capure, as the partial capture will cause increased variability + # in the Coverity results compared to full analysis. + # + # If maximum speed is desired in favor of complete resulsts, you may + # add: + # --source-list coverity-files-to-scan.txt + # to the cov-capture invokation. + # + # Results are saved into coverity-results.json. + + - name: Coverity Scan (Incremental analysis) + if: ${{github.event_name == 'pull_request'}} + shell: bash + run: | + echo + echo ======================================================================================== + echo == Run INCREMENTAL Coverity analysis for ${GITHUB_REPOSITORY##*/}-$BASE_BRANCH + echo ======================================================================================== + echo + if [ "${{ inputs.coverity-stream-name }}" == "" ]; then + export COVERITY_STREAM_NAME=${GITHUB_REPOSITORY##*/}-$BASE_BRANCH + else + export COVERITY_STREAM_NAME=${{ inputs.coverity-stream-name }} + fi + for changed_file in ${{ steps.changeset.outputs.added_modified }}; do + echo ${changed_file} >> coverity-files-to-scan.txt + echo "Scan changed file ${changed_file}." + done + cov-run-desktop --dir idir --strip-path `pwd` --url ${{ inputs.coverity-url }} --stream $COVERITY_STREAM_NAME --present-in-reference false \ + --ignore-uncapturable-inputs true \ + --json-output-v7 coverity-results.json \ + ${{ inputs.cov-analyze-options }} \ + ${{ steps.changeset.outputs.added_modified }} + env: + BASE_BRANCH: ${{ github.base_ref }} + COV_USER: ${{ inputs.coverity-user }} + COVERITY_PASSPHRASE: ${{ inputs.coverity-passphrase }} + + # The coverity-report-output-v7-json action will take the results + # from Coverity and present them as review feedback on pull request. + # + # Only NEW defects will be reported to the user. + + - name: Coverity Pull Request Feedback + if: ${{github.event_name == 'pull_request'}} + uses: synopsys-sig/coverity-report-output-v7-json@v0.0.1 + with: + # The following parameters are REQUIRED + json-file-path: ./coverity-results.json + github-token: ${{ inputs.github-token }} + # If the following optional parameters are specified, the results from the JSON output will be + # compared to the baseline issues in the specified project, and only NEW issues will be reported + # in the pull request. + coverity-url: ${{ inputs.coverity-url }} + coverity-project-name: ${{ github.event.repository.name }} + coverity-username: ${{ inputs.coverity-user }} + coverity-password: ${{ inputs.coverity-passphrase }} + + # Generate SARIF using Coverity's built-in tool. Note that this is not + # included in the regular Coverity PATH, so the full path must be + # specified here. + # + # This can be used for both pushes and pull requests, but note the + # GitHub limitation that it is only available for public repos and for + # if you have a subscription to GitHub Security. + + - name: Export Coverity Results to SARIF + if: ${{ inputs.generate-sarif == 'true' }} + shell: bash + run: | + echo + echo ======================================================================================== + echo == Generate SARIF for Coverity results + echo ======================================================================================== + echo + COV_ANALYZE_PATH=`which cov-analyze` + COVERITY_HOME=`dirname $COV_ANALYZE_PATH` + node $COVERITY_HOME/../SARIF/cov-format-sarif-for-github.js \ + --inputFile coverity-results.json \ + --repoName $GITHUB_REPOSITORY \ + --checkoutPath $GITHUB_REPOSITORY `pwd` $GITHUB_SHA \ + --outputFile synopsys-coverity-github-sarif.json + + - name: Upload SARIF file + if: ${{ inputs.generate-sarif == 'true' }} + uses: github/codeql-action/upload-sarif@v1 + with: + # Path to SARIF file relative to the root of the repository + sarif_file: synopsys-coverity-github-sarif.json + + - name: Coverity Security Gate (Full analysis) + if: ${{ github.event_name != 'pull_request' }} and ${{ inputs.security-gate-view-name }} != "" + shell: bash + run: | + echo + echo ======================================================================================== + echo == Check security gate for project ${GITHUB_REPOSITORY##*/}: $SECURITY_GATE_VIEW + echo ======================================================================================== + echo + if [ "${{ inputs.coverity-project-name }}" == "" ]; then + export COVERITY_PROJECT_NAME=${GITHUB_REPOSITORY##*/} + else + export COVERITY_PROJECT_NAME=${{ inputs.coverity-project-name }} + fi + COVERITY_VIEW_ESCAPED=`jq -rn --arg x "${{ inputs.security-gate-view-name }}" '$x|@uri'` + curl -kfLsS --user $COV_USER:$COVERITY_PASSPHRASE ${{ inputs.coverity-url }}/api/viewContents/issues/v1/$COVERITY_VIEW_ESCAPED?projectId=$COVERITY_PROJECT_NAME > security-gate-results.json + if [ $(cat security-gate-results.json | jq .viewContentsV1.totalRows) -ne 0 ]; then + echo Security gate found policy violations + cat security-gate-results.json | jq .viewContentsV1.rows + echo "SECURITY_GATE_STATUS=failure" >> $GITHUB_ENV + else + echo "SECURITY_GATE_STATUS=success" >> $GITHUB_ENV + fi + env: + COV_USER: ${{ inputs.coverity-user }} + COVERITY_PASSPHRASE: ${{ inputs.coverity-passphrase }} + + # For a pull request, until the GitHub action supports this, we + # will run a preview commit and check how many newly introduced + # issues are being reported. + + - name: Coverity Security Gate (Incremental analysis) + if: ${{github.event_name == 'pull_request'}} + shell: bash + run: | + echo + echo ======================================================================================== + echo == Check security gate for local analysis results + echo ======================================================================================== + echo + if [ "${{ inputs.coverity-stream-name }}" == "" ]; then + export COVERITY_STREAM_NAME=${GITHUB_REPOSITORY##*/}-$BASE_BRANCH + else + export COVERITY_STREAM_NAME=${{ inputs.coverity-stream-name }} + fi + cov-commit-defects --dir idir --url ${{ inputs.coverity-url }} --preview-report-v2 preview-report.json --stream ${COVERITY_STREAM_NAME} + NUM_NEW_DEFECTS=`cat preview-report.json | jq -re .issueInfo[].presentInComparisonSnapshot | grep false | wc -l || true` + if [ "$NUM_NEW_DEFECTS" -eq 0 ]; then + echo No new defects found - success + echo ::save-state name=security_gate_status::success + else + echo New defects found - failure + echo ::save-state name=security_gate_status::failure + fi + env: + BASE_BRANCH: ${{ github.base_ref }} + COV_USER: ${{ inputs.coverity-user }} + COVERITY_PASSPHRASE: ${{ inputs.coverity-passphrase }} + + - name: Archive Coverity Intermediate Directory + if: ${{ inputs.diagnostic-mode == 'true' }} + uses: actions/upload-artifact@v3 + with: + name: coverity-idir + path: idir + + - uses: ouzi-dev/commit-status-updater@v1.1.0 + with: + name: "Coverity Policy Check" + status: "${{ env.SECURITY_GATE_STATUS }}" \ No newline at end of file diff --git a/coverity/codeScanningAlert.png b/coverity/codeScanningAlert.png new file mode 100644 index 0000000..d282f85 Binary files /dev/null and b/coverity/codeScanningAlert.png differ diff --git a/coverity/coveritySavedView.png b/coverity/coveritySavedView.png new file mode 100644 index 0000000..eef880d Binary files /dev/null and b/coverity/coveritySavedView.png differ diff --git a/coverity/reviewComment.png b/coverity/reviewComment.png new file mode 100644 index 0000000..24d1bcc Binary files /dev/null and b/coverity/reviewComment.png differ diff --git a/coverity/securityGate.png b/coverity/securityGate.png new file mode 100644 index 0000000..6b3c83b Binary files /dev/null and b/coverity/securityGate.png differ diff --git a/docker/coverity-github-runner/Dockerfile b/docker/coverity-github-runner/Dockerfile new file mode 100644 index 0000000..27e6e84 --- /dev/null +++ b/docker/coverity-github-runner/Dockerfile @@ -0,0 +1,37 @@ +FROM ubuntu:18.04 + +ENV GITHUB_PAT "" +ENV GITHUB_TOKEN "" +ENV GITHUB_OWNER "" +ENV GITHUB_REPOSITORY "" +ENV RUNNER_WORKDIR "_work" +ENV RUNNER_LABELS "coverity" +ENV COVERITY_VERSION=2021.12.1 +ENV COVERITY_LICENSE "" + +RUN apt-get update \ + && apt-get install -y openjdk-8-jdk python3 python3-pip \ + && apt-get install -y curl unzip git jq sudo \ + && useradd -m runner \ + && usermod -aG sudo runner \ + && echo "%sudo ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +# Need Node.js for Coverity SARIF generator +RUN curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - && apt -y install nodejs + +# Install GitHub Runner +USER runner +WORKDIR /home/runner + +RUN GITHUB_RUNNER_VERSION=$(curl --silent "https://api.github.com/repos/actions/runner/releases/latest" | jq -r '.tag_name[1:]') \ + && curl -Ls https://github.com/actions/runner/releases/download/v${GITHUB_RUNNER_VERSION}/actions-runner-linux-x64-${GITHUB_RUNNER_VERSION}.tar.gz | tar xz \ + && sudo ./bin/installdependencies.sh + +COPY --chown=runner:runner entrypoint.sh run.sh ./ +RUN sudo chmod u+x ./entrypoint.sh ./run.sh + +# Install Coverity, license, and run basic cov-configure +COPY cov-analysis-linux64-${COVERITY_VERSION}.tar.gz license.dat ./ +RUN tar xzf cov-analysis-linux64-${COVERITY_VERSION}.tar.gz && rm cov-analysis-linux64-${COVERITY_VERSION}.tar.gz && mv cov-analysis-linux64-${COVERITY_VERSION} cov-analysis-linux64 && mv license.dat cov-analysis-linux64/bin/ && ./cov-analysis-linux64/bin/cov-configure --java && ./cov-analysis-linux64/bin/cov-configure --gcc + +ENTRYPOINT ["/home/runner/entrypoint.sh"] diff --git a/docker/coverity-github-runner/README.md b/docker/coverity-github-runner/README.md new file mode 100644 index 0000000..043d0a4 --- /dev/null +++ b/docker/coverity-github-runner/README.md @@ -0,0 +1,31 @@ +# Coverity GitHub Runner + +This Docker image is provided as an example of how to construct a GitHub runner that includes the necessary bits +for running a full Coverity workflow within GitHub. For the traditional Coverity workflow, including a local cov-analyze +running on the runner itself, it is recommended to use a self-hosted runner due to the large footprint (3+ GB) of the +analysis software. + +Building the image: +1. Copy your coverity analysis kit (e.g. cov-analysis-...) and license file (license.dat) to your working directory. +2. Edit the `Dockerfile` and make sure the Coverity version is set to match your analysis kit +3. Build the image: `docker build -t coverity-github-runner .` + +The image is set up to automatically bootstrap the latest GitHub Runner software and connect to your account to start +servicing jobs. You will need to pass the following environment variables to the image: + +| Environment Variable Name | Description | +| --- | --- | +| **GITHUB_OWNER** | The GitHub "owner" -- either a username or organization | +| **GITHUB_REPOSITORY** | The GitHub repository to service - Just the name, not the full URL and do not include .git. If left out, will support the entire organization | +| **GITHUB_PAT** | A GitHub personal access token with permission to establish self-hosted runners | + +Running the image will vary depending on your platform, but for a simple command line Docker instance it may +look like: + +```bash +docker run --name coverity-github-runner \ + -e GITHUB_OWNER= \ + -e GITHUB_REPOSITORY= \ + -e GITHUB_PAT= \ + coverity-github-runner +``` diff --git a/docker/coverity-github-runner/entrypoint.sh b/docker/coverity-github-runner/entrypoint.sh new file mode 100644 index 0000000..c575fb1 --- /dev/null +++ b/docker/coverity-github-runner/entrypoint.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +registration_url="https://github.com/${GITHUB_OWNER}" +token_url="https://api.github.com/orgs/${GITHUB_OWNER}/actions/runners/registration-token" + +if [ -n "${GITHUB_TOKEN}" ]; then + echo "Using given GITHUB_TOKEN" + + if [ -z "${GITHUB_REPOSITORY}" ]; then + echo "When using GITHUB_TOKEN, the GITHUB_REPOSITORY must be set" + return + fi + + registration_url="https://github.com/${GITHUB_OWNER}/${GITHUB_REPOSITORY}" + export RUNNER_TOKEN=$GITHUB_TOKEN + +else + if [ -n "${GITHUB_REPOSITORY}" ]; then + token_url="https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPOSITORY}/actions/runners/registration-token" + registration_url="${registration_url}/${GITHUB_REPOSITORY}" + fi + + echo "Requesting token at '${token_url}'" + + payload=$(curl -sLX POST -H "Authorization: token ${GITHUB_PAT}" ${token_url}) + export RUNNER_TOKEN=$(echo $payload | jq .token --raw-output) + +fi + +if [ -z "${RUNNER_NAME}" ]; then + RUNNER_NAME=$(hostname) +fi + +./config.sh \ + --name "${RUNNER_NAME}" \ + --token "${RUNNER_TOKEN}" \ + --url "${registration_url}" \ + --work "${RUNNER_WORKDIR}" \ + --labels "${RUNNER_LABELS}" \ + --unattended \ + --replace + +export PATH=~/cov-analysis-linux64/bin:$PATH + +cleanup() { + if [ -n "${GITHUB_TOKEN}" ]; then + export REMOVE_TOKEN=$GITHUB_TOKEN + else + payload=$(curl -sLX POST -H "Authorization: token ${GITHUB_PAT}" ${token_url%/registration-token}/remove-token) + export REMOVE_TOKEN=$(echo $payload | jq .token --raw-output) + fi + + ./config.sh remove --unattended --token "${REMOVE_TOKEN}" +} + +trap 'cleanup; exit 130' INT +trap 'cleanup; exit 143' TERM + +./run.sh "$*" & + +wait $! diff --git a/docker/coverity-github-runner/run.sh b/docker/coverity-github-runner/run.sh new file mode 100644 index 0000000..4e1f05b --- /dev/null +++ b/docker/coverity-github-runner/run.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +trap 'kill -INT $PID' TERM INT + +if [ -f ".path" ]; then + export PATH=`cat .path` + echo ".path=${PATH}" +fi + +# Insert Sunopsys PATH variables here +export PATH=~/cov-analysis-linux64/bin:$PATH + +# run the host process which keep the listener alive +./externals/node12/bin/node ./bin/RunnerService.js & +PID=$! +wait $PID +trap - TERM INT +wait $PID