From 499a725c758e00c3cba9bcdff495507b657585df Mon Sep 17 00:00:00 2001 From: jperezde Date: Fri, 15 Nov 2024 20:34:09 +0100 Subject: [PATCH] sast: initial task for Coverity Buildless Solves: https://issues.redhat.com/browse/OSH-740 Initial version of the Coverity Buildless task. In introduces two different tasks: A task checking the availability of Coverity license and authentication token, and a task for scanning the code. The code will be scanned using coverity buildless mode, then the results are processing using csgrep and the results are later filtered using csfilter-kfp. --- pipelines/template-build/template-build.yaml | 39 +++ .../0.1/README.md | 18 ++ .../coverity-availability-check-oci-ta.yaml | 111 +++++++ .../0.1/recipe.yaml | 12 + .../coverity-availability-check-oci-ta/OWNERS | 6 + .../coverity-availability-check/0.1/README.md | 26 ++ .../0.1/coverity-availability-check.yaml | 92 ++++++ task/coverity-availability-check/OWNERS | 6 + task/sast-coverity-check-oci-ta/0.1/README.md | 27 ++ .../0.1/recipe.yaml | 12 + .../0.1/sast-coverity-check-oci-ta.yaml | 303 ++++++++++++++++++ task/sast-coverity-check-oci-ta/OWNERS | 6 + task/sast-coverity-check/0.1/README.md | 46 +++ .../0.1/sast-coverity-check.yaml | 282 ++++++++++++++++ task/sast-coverity-check/OWNERS | 6 + 15 files changed, 992 insertions(+) create mode 100644 task/coverity-availability-check-oci-ta/0.1/README.md create mode 100644 task/coverity-availability-check-oci-ta/0.1/coverity-availability-check-oci-ta.yaml create mode 100644 task/coverity-availability-check-oci-ta/0.1/recipe.yaml create mode 100644 task/coverity-availability-check-oci-ta/OWNERS create mode 100644 task/coverity-availability-check/0.1/README.md create mode 100644 task/coverity-availability-check/0.1/coverity-availability-check.yaml create mode 100644 task/coverity-availability-check/OWNERS create mode 100644 task/sast-coverity-check-oci-ta/0.1/README.md create mode 100644 task/sast-coverity-check-oci-ta/0.1/recipe.yaml create mode 100644 task/sast-coverity-check-oci-ta/0.1/sast-coverity-check-oci-ta.yaml create mode 100644 task/sast-coverity-check-oci-ta/OWNERS create mode 100644 task/sast-coverity-check/0.1/README.md create mode 100644 task/sast-coverity-check/0.1/sast-coverity-check.yaml create mode 100644 task/sast-coverity-check/OWNERS diff --git a/pipelines/template-build/template-build.yaml b/pipelines/template-build/template-build.yaml index 9d339b178..68546587f 100644 --- a/pipelines/template-build/template-build.yaml +++ b/pipelines/template-build/template-build.yaml @@ -223,6 +223,45 @@ spec: value: $(tasks.build-image-index.results.IMAGE_DIGEST) - name: image-url value: $(tasks.build-image-index.results.IMAGE_URL) + - name: sast-coverity-check + when: + - input: $(params.skip-checks) + operator: in + values: ["false"] + - input: $(tasks.coverity-availability-check.results.STATUS) + operator: in + values: ["success"] + runAfter: + - coverity-availability-check + taskRef: + name: sast-coverity-check + version: "0.1" + params: + - name: image-digest + value: $(tasks.build-container.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-container.results.IMAGE_URL) + workspaces: + - name: workspace + workspace: workspace + - name: coverity-availability-check + when: + - input: $(params.skip-checks) + operator: in + values: ["false"] + runAfter: + - build-image-index + taskRef: + name: coverity-availability-check + version: "0.1" + params: + - name: image-digest + value: $(tasks.build-container.results.IMAGE_DIGEST) + - name: image-url + value: $(tasks.build-container.results.IMAGE_URL) + workspaces: + - name: workspace + workspace: workspace - name: clamav-scan when: - input: $(params.skip-checks) diff --git a/task/coverity-availability-check-oci-ta/0.1/README.md b/task/coverity-availability-check-oci-ta/0.1/README.md new file mode 100644 index 000000000..0a9ad5836 --- /dev/null +++ b/task/coverity-availability-check-oci-ta/0.1/README.md @@ -0,0 +1,18 @@ +# sast-coverity-availability-check-oci-ta task + +This task performs needed checks in order to use Coverity image in the pipeline. It will check for a Coverity license secret and an authentication secret for pulling the image. + +## Parameters +|name|description|default value|required| +|---|---|---|---| +|AUTH_TOKEN_COVERITY_IMAGE|Name of secret which contains the authentication token for pulling the Coverity image.|auth-token-coverity-image|false| +|CACHI2_ARTIFACT|The Trusted Artifact URI pointing to the artifact with the prefetched dependencies.|""|false| +|COV_LICENSE|Name of secret which contains the Coverity license|cov-license|false| +|SOURCE_ARTIFACT|The Trusted Artifact URI pointing to the artifact with the application source code.||true| + +## Results +|name|description| +|---|---| +|STATUS|Tekton task simple status to be later checked| +|TEST_OUTPUT|Tekton task result output.| + diff --git a/task/coverity-availability-check-oci-ta/0.1/coverity-availability-check-oci-ta.yaml b/task/coverity-availability-check-oci-ta/0.1/coverity-availability-check-oci-ta.yaml new file mode 100644 index 000000000..91ef28ddc --- /dev/null +++ b/task/coverity-availability-check-oci-ta/0.1/coverity-availability-check-oci-ta.yaml @@ -0,0 +1,111 @@ +--- +apiVersion: tekton.dev/v1 +kind: Task +metadata: + name: sast-coverity-availability-check-oci-ta + annotations: + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: konflux + labels: + app.kubernetes.io/version: "0.1" +spec: + description: This task performs needed checks in order to use Coverity image + in the pipeline. It will check for a Coverity license secret and an authentication + secret for pulling the image. + params: + - name: AUTH_TOKEN_COVERITY_IMAGE + description: Name of secret which contains the authentication token + for pulling the Coverity image. + default: auth-token-coverity-image + - name: CACHI2_ARTIFACT + description: The Trusted Artifact URI pointing to the artifact with + the prefetched dependencies. + type: string + default: "" + - name: COV_LICENSE + description: Name of secret which contains the Coverity license + default: cov-license + - name: SOURCE_ARTIFACT + description: The Trusted Artifact URI pointing to the artifact with + the application source code. + type: string + results: + - name: STATUS + description: Tekton task simple status to be later checked + - name: TEST_OUTPUT + description: Tekton task result output. + volumes: + - name: auth-token-coverity-image + secret: + optional: true + secretName: $(params.AUTH_TOKEN_COVERITY_IMAGE) + - name: cov-license + secret: + optional: true + secretName: $(params.COV_LICENSE) + - name: workdir + emptyDir: {} + stepTemplate: + volumeMounts: + - mountPath: /var/workdir + name: workdir + steps: + - name: use-trusted-artifact + image: quay.io/redhat-appstudio/build-trusted-artifacts:latest@sha256:81c4864dae6bb11595f657be887e205262e70086a05ed16ada827fd6391926ac + args: + - use + - $(params.SOURCE_ARTIFACT)=/var/workdir/source + - $(params.CACHI2_ARTIFACT)=/var/workdir/cachi2 + - name: coverity-availability-check + image: quay.io/redhat-appstudio/konflux-test:v1.4.7@sha256:cf6808a3bd605630a5d9f20595ff7c43f8645c00381219d32f5a11e88fe37072 + workingDir: /var/workdir/source + volumeMounts: + - mountPath: /etc/secrets/cov + name: cov-license + readOnly: true + - mountPath: /etc/secrets/auth/config.json + name: auth-token-coverity-image + subPath: .dockerconfigjson + env: + - name: COV_LICENSE + value: $(params.COV_LICENSE) + - name: AUTH_TOKEN_COVERITY_IMAGE + value: $(params.AUTH_TOKEN_COVERITY_IMAGE) + script: | + #!/usr/bin/env bash + set -eo pipefail + # shellcheck source=/dev/null + . /utils.sh + trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT + + # Checking Coverity license + COV_LICENSE_PATH=/etc/secrets/cov/cov-license + if [ -f "${COV_LICENSE_PATH}" ] && [ -s "${COV_LICENSE_PATH}" ]; then + echo "Coverity license detected!" + else + echo 'No license file for Coverity was detected. Coverity scan will not be executed...' + echo 'Please, create a secret called 'cov-license' with a key called 'cov-license' and the value containing the Coverity license' + note="Task $(context.task.name) failed: No license file for Coverity was detected. Please, create a secret called 'cov-license' with a key called 'cov-license' and the value containing the Coverity license" + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo -n "failed" | tee "$(results.STATUS.path)" + exit 0 + fi + + # Checking authentication token for downloading coverity image + AUTH_TOKEN_COVERITY_IMAGE_PATH=/etc/secrets/auth/config.json + if [ -f "${AUTH_TOKEN_COVERITY_IMAGE_PATH}" ] && [ -s "${AUTH_TOKEN_COVERITY_IMAGE_PATH}" ]; then + echo "Authentication token detected!" + else + echo 'No authentication token for downloading Coverity image detected. Coverity scan will not be executed...' + echo 'Please, create an imagePullSecret named 'auth-token-coverity-image' with the authentication token for pulling the Coverity image' + note="Task $(context.task.name) failed: No authentication token for downloading Coverity image detected. Please, create an imagePullSecret named 'auth-token-coverity-image' with the authentication token for pulling the Coverity image" + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo -n "failed" | tee "$(results.STATUS.path)" + exit 0 + fi + + note="Task $(context.task.name) completed: Coverity availability checks under /var/workdir/hacbs/$(context.task.name) finished succesfully." + # shellcheck disable=SC2034 + TEST_OUTPUT=$(make_result_json -r SUCCESS -s 1 -t "$note") + echo -n "success" | tee "$(results.STATUS.path)" + echo "${TEST_OUTPUT:-${ERROR_OUTPUT}}" | tee "$(results.TEST_OUTPUT.path)" diff --git a/task/coverity-availability-check-oci-ta/0.1/recipe.yaml b/task/coverity-availability-check-oci-ta/0.1/recipe.yaml new file mode 100644 index 000000000..1627c4de8 --- /dev/null +++ b/task/coverity-availability-check-oci-ta/0.1/recipe.yaml @@ -0,0 +1,12 @@ +--- +base: ../../coverity-availability-check/0.1/coverity-availability-check.yaml +add: + - use-source + - use-cachi2 +preferStepTemplate: true +removeWorkspaces: + - workspace +replacements: + workspaces.workspace.path: /var/workdir +regexReplacements: + hacbs/\$\(context.task.name\): source diff --git a/task/coverity-availability-check-oci-ta/OWNERS b/task/coverity-availability-check-oci-ta/OWNERS new file mode 100644 index 000000000..4f4bc81c0 --- /dev/null +++ b/task/coverity-availability-check-oci-ta/OWNERS @@ -0,0 +1,6 @@ +# See the OWNERS docs: https://go.k8s.io/owners +approvers: + - integration-team +reviewers: + - integration-team + - kdudka diff --git a/task/coverity-availability-check/0.1/README.md b/task/coverity-availability-check/0.1/README.md new file mode 100644 index 000000000..9565f411b --- /dev/null +++ b/task/coverity-availability-check/0.1/README.md @@ -0,0 +1,26 @@ +# coverity-availability-check task + +## Description: + +This task performs needed checks in order to use Coverity image in the pipeline. It will check for a Coverity license secret and an authentication secret for pulling the image. + +The characteristics of these tasks are: + +- It will check for a secret called "auth-token-coverity-image" where the authentication token for pulling Coverity image is pulled. +- It will check for a secret called "cov-license" where the Coverity license is stored. + +> NOTE: If any of these tasks fails, the sast-coverity-task check won't be executed. The Coverity license can be used by Red Hat employees only and it needs to be protected such that external users cannot access the license. + +## Params: + +| name | description | default value | required | +|-----------------------------|----------------------------------------------------------------------------------------|----------------------------|----------| +| AUTH_TOKEN_COVERITY_IMAGE | Name of secret which contains the authentication token for pulling the Coverity image | auth-token-coverity-image | yes | +| COV_LICENSE | Name of secret which contains the Coverity license | cov-license | yes | + +## Results: + +| name | description | +|-------------|-----------------------------------------------------------------------------------| +| STATUS | Tekton task simple status to be later checked by the sast-coverity-check task | +| TEST_OUTPUT | Tekton task test output. | diff --git a/task/coverity-availability-check/0.1/coverity-availability-check.yaml b/task/coverity-availability-check/0.1/coverity-availability-check.yaml new file mode 100644 index 000000000..f2d0f64ae --- /dev/null +++ b/task/coverity-availability-check/0.1/coverity-availability-check.yaml @@ -0,0 +1,92 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + labels: + app.kubernetes.io/version: "0.1" + annotations: + tekton.dev/pipelines.minVersion: "0.12.1" + tekton.dev/tags: "konflux" + name: sast-coverity-availability-check +spec: + description: >- + This task performs needed checks in order to use Coverity image in the pipeline. It will check for a Coverity license secret and an authentication secret for pulling the image. + results: + - description: Tekton task result output. + name: TEST_OUTPUT + - description: Tekton task simple status to be later checked + name: STATUS + params: + - name: COV_LICENSE + description: Name of secret which contains the Coverity license + default: cov-license + - name: AUTH_TOKEN_COVERITY_IMAGE + description: Name of secret which contains the authentication token for pulling the Coverity image. + default: "auth-token-coverity-image" + volumes: + - name: cov-license + secret: + secretName: $(params.COV_LICENSE) + optional: true + - name: auth-token-coverity-image + secret: + secretName: $(params.AUTH_TOKEN_COVERITY_IMAGE) + optional: true + steps: + - name: coverity-availability-check + image: quay.io/redhat-appstudio/konflux-test:v1.4.7@sha256:cf6808a3bd605630a5d9f20595ff7c43f8645c00381219d32f5a11e88fe37072 + # per https://kubernetes.io/docs/concepts/containers/images/#imagepullpolicy-defaulting + # the cluster will set imagePullPolicy to IfNotPresent + workingDir: $(workspaces.workspace.path)/hacbs/$(context.task.name) + volumeMounts: + - name: cov-license + mountPath: "/etc/secrets/cov" + readOnly: true + - name: auth-token-coverity-image + mountPath: "/etc/secrets/auth/config.json" + subPath: .dockerconfigjson + env: + - name: COV_LICENSE + value: $(params.COV_LICENSE) + - name: AUTH_TOKEN_COVERITY_IMAGE + value: $(params.AUTH_TOKEN_COVERITY_IMAGE) + script: | + #!/usr/bin/env bash + set -eo pipefail + # shellcheck source=/dev/null + . /utils.sh + trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT + + # Checking Coverity license + COV_LICENSE_PATH=/etc/secrets/cov/cov-license + if [ -f "${COV_LICENSE_PATH}" ] && [ -s "${COV_LICENSE_PATH}" ]; then + echo "Coverity license detected!" + else + echo 'No license file for Coverity was detected. Coverity scan will not be executed...' + echo 'Please, create a secret called 'cov-license' with a key called 'cov-license' and the value containing the Coverity license' + note="Task $(context.task.name) failed: No license file for Coverity was detected. Please, create a secret called 'cov-license' with a key called 'cov-license' and the value containing the Coverity license" + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo -n "failed" | tee "$(results.STATUS.path)" + exit 0 + fi + + # Checking authentication token for downloading coverity image + AUTH_TOKEN_COVERITY_IMAGE_PATH=/etc/secrets/auth/config.json + if [ -f "${AUTH_TOKEN_COVERITY_IMAGE_PATH}" ] && [ -s "${AUTH_TOKEN_COVERITY_IMAGE_PATH}" ]; then + echo "Authentication token detected!" + else + echo 'No authentication token for downloading Coverity image detected. Coverity scan will not be executed...' + echo 'Please, create an imagePullSecret named 'auth-token-coverity-image' with the authentication token for pulling the Coverity image' + note="Task $(context.task.name) failed: No authentication token for downloading Coverity image detected. Please, create an imagePullSecret named 'auth-token-coverity-image' with the authentication token for pulling the Coverity image" + TEST_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo -n "failed" | tee "$(results.STATUS.path)" + exit 0 + fi + + note="Task $(context.task.name) completed: Coverity availability checks under $(workspaces.workspace.path)/hacbs/$(context.task.name) finished succesfully." + # shellcheck disable=SC2034 + TEST_OUTPUT=$(make_result_json -r SUCCESS -s 1 -t "$note") + echo -n "success" | tee "$(results.STATUS.path)" + echo "${TEST_OUTPUT:-${ERROR_OUTPUT}}" | tee "$(results.TEST_OUTPUT.path)" + + workspaces: + - name: workspace diff --git a/task/coverity-availability-check/OWNERS b/task/coverity-availability-check/OWNERS new file mode 100644 index 000000000..4f4bc81c0 --- /dev/null +++ b/task/coverity-availability-check/OWNERS @@ -0,0 +1,6 @@ +# See the OWNERS docs: https://go.k8s.io/owners +approvers: + - integration-team +reviewers: + - integration-team + - kdudka diff --git a/task/sast-coverity-check-oci-ta/0.1/README.md b/task/sast-coverity-check-oci-ta/0.1/README.md new file mode 100644 index 000000000..b3e4b8bf6 --- /dev/null +++ b/task/sast-coverity-check-oci-ta/0.1/README.md @@ -0,0 +1,27 @@ +# sast-coverity-check-oci-ta task + +Scans source code for security vulnerabilities, including common issues such as SQL injection, cross-site scripting (XSS), and code injection attacks using Coverity. At the moment, this task only uses the buildless mode, which does not build the project in order to analyze it. + +## Parameters +|name|description|default value|required| +|---|---|---|---| +|AUTH_TOKEN_COVERITY_IMAGE|Name of secret which contains the authentication token for pulling the Coverity image.|auth-token-coverity-image|false| +|CACHI2_ARTIFACT|The Trusted Artifact URI pointing to the artifact with the prefetched dependencies.|""|false| +|COV_ANALYZE_ARGS|Arguments to be appended to the cov-analyze command|--enable HARDCODED_CREDENTIALS --security --concurrency --spotbugs-max-mem=4096|false| +|COV_CAPTURE_ARGS|Arguments to be appended to the coverity capture command|""|false| +|COV_LICENSE|Name of secret which contains the Coverity license|cov-license|false| +|IMP_FINDINGS_ONLY|Report only important findings. Default is true. To report all findings, specify "false"|true|false| +|KFP_GIT_URL|URL from repository to download known false positives files|""|false| +|PROJECT_NAME|Name of the scanned project, used to find path exclusions. By default, the Konflux component name will be used.|""|false| +|RECORD_EXCLUDED|Write excluded records in file. Useful for auditing (defaults to false).|false|false| +|SOURCE_ARTIFACT|The Trusted Artifact URI pointing to the artifact with the application source code.||true| +|caTrustConfigMapKey|The name of the key in the ConfigMap that contains the CA bundle data.|ca-bundle.crt|false| +|caTrustConfigMapName|The name of the ConfigMap to read CA bundle data from.|trusted-ca|false| +|image-digest|Image digest to report findings for.||true| +|image-url|Image URL.||true| + +## Results +|name|description| +|---|---| +|TEST_OUTPUT|Tekton task test output.| + diff --git a/task/sast-coverity-check-oci-ta/0.1/recipe.yaml b/task/sast-coverity-check-oci-ta/0.1/recipe.yaml new file mode 100644 index 000000000..d8413063c --- /dev/null +++ b/task/sast-coverity-check-oci-ta/0.1/recipe.yaml @@ -0,0 +1,12 @@ +--- +base: ../../sast-coverity-check/0.1/sast-coverity-check.yaml +add: + - use-source + - use-cachi2 +preferStepTemplate: true +removeWorkspaces: + - workspace +replacements: + workspaces.workspace.path: /var/workdir +regexReplacements: + hacbs/\$\(context.task.name\): source diff --git a/task/sast-coverity-check-oci-ta/0.1/sast-coverity-check-oci-ta.yaml b/task/sast-coverity-check-oci-ta/0.1/sast-coverity-check-oci-ta.yaml new file mode 100644 index 000000000..7553d1a69 --- /dev/null +++ b/task/sast-coverity-check-oci-ta/0.1/sast-coverity-check-oci-ta.yaml @@ -0,0 +1,303 @@ +--- +apiVersion: tekton.dev/v1 +kind: Task +metadata: + name: sast-coverity-check-oci-ta + annotations: + tekton.dev/pipelines.minVersion: 0.12.1 + tekton.dev/tags: konflux + labels: + app.kubernetes.io/version: "0.1" +spec: + description: Scans source code for security vulnerabilities, including common + issues such as SQL injection, cross-site scripting (XSS), and code injection + attacks using Coverity. At the moment, this task only uses the buildless + mode, which does not build the project in order to analyze it. + params: + - name: AUTH_TOKEN_COVERITY_IMAGE + description: Name of secret which contains the authentication token + for pulling the Coverity image. + default: auth-token-coverity-image + - name: CACHI2_ARTIFACT + description: The Trusted Artifact URI pointing to the artifact with + the prefetched dependencies. + type: string + default: "" + - name: COV_ANALYZE_ARGS + description: Arguments to be appended to the cov-analyze command + type: string + default: --enable HARDCODED_CREDENTIALS --security --concurrency --spotbugs-max-mem=4096 + - name: COV_CAPTURE_ARGS + description: Arguments to be appended to the coverity capture command + type: string + default: "" + - name: COV_LICENSE + description: Name of secret which contains the Coverity license + default: cov-license + - name: IMP_FINDINGS_ONLY + description: Report only important findings. Default is true. To report + all findings, specify "false" + type: string + default: "true" + - name: KFP_GIT_URL + description: URL from repository to download known false positives files + type: string + default: "" + - name: PROJECT_NAME + description: Name of the scanned project, used to find path exclusions. + By default, the Konflux component name will be used. + type: string + default: "" + - name: RECORD_EXCLUDED + description: Write excluded records in file. Useful for auditing (defaults + to false). + type: string + default: "false" + - name: SOURCE_ARTIFACT + description: The Trusted Artifact URI pointing to the artifact with + the application source code. + type: string + - name: caTrustConfigMapKey + description: The name of the key in the ConfigMap that contains the + CA bundle data. + type: string + default: ca-bundle.crt + - name: caTrustConfigMapName + description: The name of the ConfigMap to read CA bundle data from. + type: string + default: trusted-ca + - name: image-digest + description: Image digest to report findings for. + type: string + - name: image-url + description: Image URL. + type: string + results: + - name: TEST_OUTPUT + description: Tekton task test output. + volumes: + - name: auth-token-coverity-image + secret: + optional: false + secretName: $(params.AUTH_TOKEN_COVERITY_IMAGE) + - name: cov-license + secret: + optional: false + secretName: $(params.COV_LICENSE) + - name: trusted-ca + configMap: + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + name: $(params.caTrustConfigMapName) + optional: true + - name: workdir + emptyDir: {} + stepTemplate: + volumeMounts: + - mountPath: /var/workdir + name: workdir + steps: + - name: use-trusted-artifact + image: quay.io/redhat-appstudio/build-trusted-artifacts:latest@sha256:81c4864dae6bb11595f657be887e205262e70086a05ed16ada827fd6391926ac + args: + - use + - $(params.SOURCE_ARTIFACT)=/var/workdir/source + - $(params.CACHI2_ARTIFACT)=/var/workdir/cachi2 + - name: sast-coverity-check + image: quay.io/redhat-services-prod/sast/coverity@sha256:0d1b96fb08a901b2d0e340599c7fee7e1de25e2d6ba58f3d95db4983f32b5a3c + workingDir: /var/workdir/source + volumeMounts: + - mountPath: /etc/secrets/cov + name: cov-license + readOnly: true + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + env: + - name: COV_ANALYZE_ARGS + value: $(params.COV_ANALYZE_ARGS) + - name: COV_CAPTURE_ARGS + value: $(params.COV_CAPTURE_ARGS) + - name: KFP_GIT_URL + value: $(params.KFP_GIT_URL) + - name: COV_LICENSE + value: $(params.COV_LICENSE) + - name: IMP_FINDINGS_ONLY + value: $(params.IMP_FINDINGS_ONLY) + - name: PROJECT_NAME + value: $(params.PROJECT_NAME) + - name: RECORD_EXCLUDED + value: $(params.RECORD_EXCLUDED) + - name: COMPONENT_LABEL + valueFrom: + fieldRef: + fieldPath: metadata.labels['appstudio.openshift.io/component'] + script: | + #!/usr/bin/env bash + set -eo pipefail + # shellcheck source=/dev/null + . /usr/local/share/konflux-test/utils.sh + trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT + + echo 'Starting Coverity buildless scan' + + export HOME="/var/tmp/coverity/home" + + if [[ -z "${PROJECT_NAME}" ]]; then + PROJECT_NAME=${COMPONENT_LABEL} + fi + echo "The PROJECT_NAME used is: ${PROJECT_NAME}" + + COVERITY_DIR=/var/tmp/coverity/idir + COVERITY_RESULTS_FILE=/var/workdir/coverity-buildless-results.json + COV_LICENSE_PATH=/etc/secrets/cov/cov-license + SOURCE_CODE_DIR=/var/workdir + + # Installing Coverity license + cp "$COV_LICENSE_PATH" /opt/coverity/bin/license.dat + + # Installation of Red Hat certificates for cloning Red Hat internal repositories + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + # Create configuration file for coverity buildless + echo -e 'capture:\n build-command-inference: false' >"$SOURCE_CODE_DIR"/coverity.yml + + set +e -x + # Buildless scan + # shellcheck disable=SC2086 + env COV_HOST=konflux /opt/coverity/bin/coverity capture $COV_CAPTURE_ARGS --project-dir "$SOURCE_CODE_DIR" --dir "$COVERITY_DIR" + COV_CAPTURE_EXIT_CODE=$? + set -x + + if [[ "$COV_CAPTURE_EXIT_CODE" -eq 0 ]]; then + echo "Coverity capture scan finished successfully" + else + echo "Coverity capture command failed with exit code ${COV_CAPTURE_EXIT_CODE}. Exiting..." + note="Task $(context.task.name) failed: For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + exit 1 + fi + + # Analysis phase + set -x + /opt/coverity/bin/cov-manage-emit --dir $COVERITY_DIR reset-host-name + # shellcheck disable=SC2086 + /opt/coverity/bin/cov-analyze $COV_ANALYZE_ARGS --dir="$COVERITY_DIR" + COV_ANALYZE_EXIT_CODE=$? + set +x + + if [[ "$COV_ANALYZE_EXIT_CODE" -eq 0 ]]; then + echo "cov-analyze scan finished successfully" + else + echo "cov-analyze scan failed with exit code ${COV_ANALYZE_EXIT_CODE}. Exiting..." + note="Task $(context.task.name) failed: For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + exit 1 + fi + set -e + + /opt/coverity/bin/cov-format-errors --dir="$COVERITY_DIR" --json-output-v10 "$COVERITY_RESULTS_FILE" + # We parse the results, embed context, remove duplicates and store them in SARIF format. + IMP_LEVEL=1 + if [ "${IMP_FINDINGS_ONLY}" == "false" ]; then + IMP_LEVEL=0 + fi + + (cd "$SOURCE_CODE_DIR" && csgrep --mode=json --imp-level="$IMP_LEVEL" --remove-duplicates --embed-context=3 "$COVERITY_RESULTS_FILE") | + csgrep --mode=json --strip-path-prefix="$SOURCE_CODE_DIR"/source/ | + csgrep --mode=json --strip-path-prefix="$HOME" \ + >sast_coverity_buildless_check_all_findings.json + + echo "Results:" + (set -x && csgrep --mode=evtstat sast_coverity_buildless_check_all_findings.json) + + # We check if the KFP_GIT_URL variable is set to apply the filters or not + if [[ -z "${KFP_GIT_URL}" ]]; then + echo "KFP_GIT_URL variable not defined. False positives won't be filtered" + mv sast_coverity_buildless_check_all_findings.json filtered_sast_coverity_buildless_check_all_findings.json + else + echo "Filtering false positives in results files using csfilter-kfp..." + CMD=( + csfilter-kfp + --verbose + --kfp-git-url="${KFP_GIT_URL}" + --project-nvr="${PROJECT_NAME}" + ) + + if [ "${RECORD_EXCLUDED}" == "true" ]; then + CMD+=(--record-excluded="excluded-findings.json") + fi + + "${CMD[@]}" sast_coverity_buildless_check_all_findings.json >filtered_sast_coverity_buildless_check_all_findings.json + status=$? + if [ "$status" -ne 0 ]; then + echo "Error: failed to filter known false positives" >&2 + return 1 + else + echo "Message: Succeed to filter known false positives" >&2 + fi + + echo "Results after filtering:" + (set -x $$ csgrep --mode=evtstat filtered_sast_coverity_buildless_check_all_findings.json) + fi + + csgrep --mode=sarif filtered_sast_coverity_buildless_check_all_findings.json >"/var/workdir"/coverity-results.sarif + + if [[ -z "$(csgrep --mode=evtstat filtered_sast_coverity_buildless_check_all_findings.json)" ]]; then + note="Task $(context.task.name) success: No finding was detected" + ERROR_OUTPUT=$(make_result_json -r SUCCESS -t "$note") + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + else + TEST_OUTPUT= + parse_test_output "$(context.task.name)" sarif "/var/workdir"/coverity-results.sarif || true + note="Task $(context.task.name) failed: For details, check Tekton task log." + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + fi + + echo "${TEST_OUTPUT:-${ERROR_OUTPUT}}" | tee "$(results.TEST_OUTPUT.path)" + computeResources: + limits: + cpu: "16" + memory: 32Gi + requests: + cpu: "8" + memory: 16Gi + - name: upload + image: quay.io/konflux-ci/oras:latest@sha256:99737f436051e6d3866eb8a8706463c35abf72c87f05090ff42ff642f6729661 + workingDir: /var/workdir + env: + - name: IMAGE_URL + value: $(params.image-url) + - name: IMAGE_DIGEST + value: $(params.image-digest) + script: | + #!/usr/bin/env bash + + if [ -z "${IMAGE_URL}" ] || [ -z "${IMAGE_DIGEST}" ]; then + echo 'No image-url or image-digest param provided. Skipping upload.' + exit 0 + fi + UPLOAD_FILES="coverity-results.sarif excluded-findings.json" + + for UPLOAD_FILE in ${UPLOAD_FILES}; do + if [ ! -f "${UPLOAD_FILE}" ]; then + echo "No ${UPLOAD_FILE} exists. Skipping upload." + continue + fi + if [ "${UPLOAD_FILES}" == "excluded-findings.json" ]; then + MEDIA_TYPE=application/json + else + MEDIA_TYPE=application/sarif+json + fi + + echo "Selecting auth" + select-oci-auth "${IMAGE_URL}" >"${HOME}/auth.json" + echo "Attaching to ${IMAGE_URL}" + oras attach --no-tty --registry-config "$HOME/auth.json" --artifact-type "${MEDIA_TYPE}" "${IMAGE_URL}" "${UPLOAD_FILE}:${MEDIA_TYPE}" + done diff --git a/task/sast-coverity-check-oci-ta/OWNERS b/task/sast-coverity-check-oci-ta/OWNERS new file mode 100644 index 000000000..4f4bc81c0 --- /dev/null +++ b/task/sast-coverity-check-oci-ta/OWNERS @@ -0,0 +1,6 @@ +# See the OWNERS docs: https://go.k8s.io/owners +approvers: + - integration-team +reviewers: + - integration-team + - kdudka diff --git a/task/sast-coverity-check/0.1/README.md b/task/sast-coverity-check/0.1/README.md new file mode 100644 index 000000000..5ce205aba --- /dev/null +++ b/task/sast-coverity-check/0.1/README.md @@ -0,0 +1,46 @@ +# sast-coverity-check task + +## Description: + +The sast-coverity-check task uses Coverity tool to perform Static Application Security Testing (SAST). In this task, we use the buildless mode, where Coverity has the ability to capture source code without the need of building the product. + +The documentation for this mode can be found here: https://sig-product-docs.synopsys.com/bundle/coverity-docs/page/commands/topics/coverity_capture.html + +The characteristics of these tasks are: + +- Perform buildless scanning with Coverity +- The whole source code is scanned (by scanning `$(workspaces.source.path)` ) +- Only important findings are reported by default. A parameter ( `IMP_FINDINGS_ONLY`) is provided to override this configuration. +- The csdiff/v1 SARIF fingerprints are provided for all findings +- A parameter ( `KFP_GIT_URL`) is provided to remove false positives providing a known false positives repository. By default, no repository is provided. + +> NOTE: This task is executed only if there is a Coverity license set up in the environment. Please check coverity-availability-check task for more information. + +## Params: + +| name | description | default value | required | +|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------|---------------------------|----------| +| COV_CAPTURE_ARGS | Append arguments to the Coverity Capture CLI command | "" | no | +| COV_ANALYZE_ARGS | Append arguments to the cov-analyze CLI command | "" | no | +| COV_LICENSE | Name of secret which contains the Coverity license | cov-license | no | +| AUTH_TOKEN_COVERITY_IMAGE | Name of secret which contains the authentication token for pulling the Coverity image | auth-token-coverity-image | no | +| IMP_FINDINGS_ONLY | Report only important findings. Default is true. To report all findings, specify "false" | true | no | +| KFP_GIT_URL | Known False Positives git URL, optionally taking a revision delimited by #; If empty, filtering of known false positives is disabled. | "" | no | +| PROJECT_NAME | Name of the scanned project, used to find path exclusions. By default, the Konflux component name will be used. | "" | no | +| RECORD_EXCLUDED | If set to `true`, excluded findings will be written to a file named `excluded-findings.json` for auditing purposes. | false | no | + +## Results: + +| name | description | +|-------------------|--------------------------| +| TEST_OUTPUT | Tekton task test output. | + +## Source repository for image: + +// TODO: Add reference to private repo for the container image once the task is migrated to repo + + +## Additional links: + +* https://sig-product-docs.synopsys.com/bundle/coverity-docs/page/commands/topics/coverity_capture.html +* https://sig-product-docs.synopsys.com/bundle/coverity-docs/page/cli/topics/options_reference.html diff --git a/task/sast-coverity-check/0.1/sast-coverity-check.yaml b/task/sast-coverity-check/0.1/sast-coverity-check.yaml new file mode 100644 index 000000000..cff99bca9 --- /dev/null +++ b/task/sast-coverity-check/0.1/sast-coverity-check.yaml @@ -0,0 +1,282 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + labels: + app.kubernetes.io/version: "0.1" + annotations: + tekton.dev/pipelines.minVersion: "0.12.1" + tekton.dev/tags: "konflux" + name: sast-coverity-check +spec: + description: >- + Scans source code for security vulnerabilities, including common issues such as SQL injection, cross-site scripting (XSS), and code injection attacks using Coverity. At the moment, this task only uses the buildless mode, which does not build the project in order to analyze it. + results: + - description: Tekton task test output. + name: TEST_OUTPUT + params: + - description: Image URL. + name: image-url + type: string + - description: Image digest to report findings for. + name: image-digest + type: string + - name: caTrustConfigMapName + type: string + description: The name of the ConfigMap to read CA bundle data from. + default: trusted-ca + - name: caTrustConfigMapKey + type: string + description: The name of the key in the ConfigMap that contains the CA bundle data. + default: ca-bundle.crt + - description: Arguments to be appended to the coverity capture command + name: COV_CAPTURE_ARGS + type: string + default: "" + - description: Arguments to be appended to the cov-analyze command + name: COV_ANALYZE_ARGS + type: string + default: "--enable HARDCODED_CREDENTIALS --security --concurrency --spotbugs-max-mem=4096" + - name: COV_LICENSE + description: Name of secret which contains the Coverity license + default: cov-license + - name: AUTH_TOKEN_COVERITY_IMAGE + description: Name of secret which contains the authentication token for pulling the Coverity image. + default: "auth-token-coverity-image" + - name: IMP_FINDINGS_ONLY + type: string + description: Report only important findings. Default is true. To report all findings, specify "false" + default: "true" + - name: KFP_GIT_URL + type: string + description: URL from repository to download known false positives files + # FIXME: Red Hat internal projects will default to https://gitlab.cee.redhat.com/osh/known-false-positives.git when KONFLUX-4530 is resolved + default: "" + - name: PROJECT_NAME + description: Name of the scanned project, used to find path exclusions. + By default, the Konflux component name will be used. + type: string + default: "" + - name: RECORD_EXCLUDED + type: string + description: Write excluded records in file. Useful for auditing (defaults to false). + default: "false" + volumes: + - name: cov-license + secret: + secretName: $(params.COV_LICENSE) + optional: false + - name: auth-token-coverity-image + secret: + secretName: $(params.AUTH_TOKEN_COVERITY_IMAGE) + optional: false + - name: trusted-ca + configMap: + name: $(params.caTrustConfigMapName) + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + optional: true + steps: + - name: sast-coverity-check + # TODO: Change image + # image: $(steps.secrets-check.results.image) + image: quay.io/redhat-services-prod/sast/coverity@sha256:0d1b96fb08a901b2d0e340599c7fee7e1de25e2d6ba58f3d95db4983f32b5a3c + computeResources: + requests: + memory: "16Gi" + cpu: "8" + limits: + memory: "32Gi" + cpu: "16" + # per https://kubernetes.io/docs/concepts/containers/images/#imagepullpolicy-defaulting + # the cluster will set imagePullPolicy to IfNotPresent + workingDir: $(workspaces.workspace.path)/hacbs/$(context.task.name) + volumeMounts: + - name: cov-license + mountPath: "/etc/secrets/cov" + readOnly: true + - name: trusted-ca + mountPath: /mnt/trusted-ca + readOnly: true + env: + - name: COV_ANALYZE_ARGS + value: $(params.COV_ANALYZE_ARGS) + - name: COV_CAPTURE_ARGS + value: $(params.COV_CAPTURE_ARGS) + - name: KFP_GIT_URL + value: $(params.KFP_GIT_URL) + - name: COV_LICENSE + value: $(params.COV_LICENSE) + - name: IMP_FINDINGS_ONLY + value: $(params.IMP_FINDINGS_ONLY) + - name: PROJECT_NAME + value: $(params.PROJECT_NAME) + - name: RECORD_EXCLUDED + value: $(params.RECORD_EXCLUDED) + - name: COMPONENT_LABEL + valueFrom: + fieldRef: + fieldPath: metadata.labels['appstudio.openshift.io/component'] + script: | + #!/usr/bin/env bash + set -eo pipefail + # shellcheck source=/dev/null + . /usr/local/share/konflux-test/utils.sh + trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT + + echo 'Starting Coverity buildless scan' + + export HOME="/var/tmp/coverity/home" + + if [[ -z "${PROJECT_NAME}" ]]; then + PROJECT_NAME=${COMPONENT_LABEL} + fi + echo "The PROJECT_NAME used is: ${PROJECT_NAME}" + + COVERITY_DIR=/var/tmp/coverity/idir + COVERITY_RESULTS_FILE=$(workspaces.workspace.path)/coverity-buildless-results.json + COV_LICENSE_PATH=/etc/secrets/cov/cov-license + SOURCE_CODE_DIR=$(workspaces.workspace.path) + + # Installing Coverity license + cp "$COV_LICENSE_PATH" /opt/coverity/bin/license.dat + + # Installation of Red Hat certificates for cloning Red Hat internal repositories + ca_bundle=/mnt/trusted-ca/ca-bundle.crt + if [ -f "$ca_bundle" ]; then + echo "INFO: Using mounted CA bundle: $ca_bundle" + cp -vf $ca_bundle /etc/pki/ca-trust/source/anchors + update-ca-trust + fi + + # Create configuration file for coverity buildless + echo -e 'capture:\n build-command-inference: false' > "$SOURCE_CODE_DIR"/coverity.yml + + set +e -x + # Buildless scan + # shellcheck disable=SC2086 + env COV_HOST=konflux /opt/coverity/bin/coverity capture $COV_CAPTURE_ARGS --project-dir "$SOURCE_CODE_DIR" --dir "$COVERITY_DIR" + COV_CAPTURE_EXIT_CODE=$? + set -x + + if [[ "$COV_CAPTURE_EXIT_CODE" -eq 0 ]]; then + echo "Coverity capture scan finished successfully" + else + echo "Coverity capture command failed with exit code ${COV_CAPTURE_EXIT_CODE}. Exiting..." + note="Task $(context.task.name) failed: For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + exit 1 + fi + + # Analysis phase + set -x + /opt/coverity/bin/cov-manage-emit --dir $COVERITY_DIR reset-host-name + # shellcheck disable=SC2086 + /opt/coverity/bin/cov-analyze $COV_ANALYZE_ARGS --dir="$COVERITY_DIR" + COV_ANALYZE_EXIT_CODE=$? + set +x + + if [[ "$COV_ANALYZE_EXIT_CODE" -eq 0 ]]; then + echo "cov-analyze scan finished successfully" + else + echo "cov-analyze scan failed with exit code ${COV_ANALYZE_EXIT_CODE}. Exiting..." + note="Task $(context.task.name) failed: For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + exit 1 + fi + set -e + + /opt/coverity/bin/cov-format-errors --dir="$COVERITY_DIR" --json-output-v10 "$COVERITY_RESULTS_FILE" + # We parse the results, embed context, remove duplicates and store them in SARIF format. + IMP_LEVEL=1 + if [ "${IMP_FINDINGS_ONLY}" == "false" ]; then + IMP_LEVEL=0 + fi + + (cd "$SOURCE_CODE_DIR" && csgrep --mode=json --imp-level="$IMP_LEVEL" --remove-duplicates --embed-context=3 "$COVERITY_RESULTS_FILE") \ + | csgrep --mode=json --strip-path-prefix="$SOURCE_CODE_DIR"/source/ \ + | csgrep --mode=json --strip-path-prefix="$HOME" \ + > sast_coverity_buildless_check_all_findings.json + + echo "Results:" + (set -x && csgrep --mode=evtstat sast_coverity_buildless_check_all_findings.json) + + # We check if the KFP_GIT_URL variable is set to apply the filters or not + if [[ -z "${KFP_GIT_URL}" ]]; then + echo "KFP_GIT_URL variable not defined. False positives won't be filtered" + mv sast_coverity_buildless_check_all_findings.json filtered_sast_coverity_buildless_check_all_findings.json + else + echo "Filtering false positives in results files using csfilter-kfp..." + CMD=( + csfilter-kfp + --verbose + --kfp-git-url="${KFP_GIT_URL}" + --project-nvr="${PROJECT_NAME}" + ) + + if [ "${RECORD_EXCLUDED}" == "true" ]; then + CMD+=(--record-excluded="excluded-findings.json") + fi + + "${CMD[@]}" sast_coverity_buildless_check_all_findings.json > filtered_sast_coverity_buildless_check_all_findings.json + status=$? + if [ "$status" -ne 0 ]; then + echo "Error: failed to filter known false positives" >&2 + return 1 + else + echo "Message: Succeed to filter known false positives" >&2 + fi + + echo "Results after filtering:" + (set -x $$ csgrep --mode=evtstat filtered_sast_coverity_buildless_check_all_findings.json) + fi + + csgrep --mode=sarif filtered_sast_coverity_buildless_check_all_findings.json > "$(workspaces.workspace.path)"/coverity-results.sarif + + if [[ -z "$(csgrep --mode=evtstat filtered_sast_coverity_buildless_check_all_findings.json)" ]]; then + note="Task $(context.task.name) success: No finding was detected" + ERROR_OUTPUT=$(make_result_json -r SUCCESS -t "$note") + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + else + TEST_OUTPUT= + parse_test_output "$(context.task.name)" sarif "$(workspaces.workspace.path)"/coverity-results.sarif || true + note="Task $(context.task.name) failed: For details, check Tekton task log." + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + fi + + echo "${TEST_OUTPUT:-${ERROR_OUTPUT}}" | tee "$(results.TEST_OUTPUT.path)" + - name: upload + image: quay.io/konflux-ci/oras:latest@sha256:99737f436051e6d3866eb8a8706463c35abf72c87f05090ff42ff642f6729661 + workingDir: $(workspaces.workspace.path) + env: + - name: IMAGE_URL + value: $(params.image-url) + - name: IMAGE_DIGEST + value: $(params.image-digest) + script: | + #!/usr/bin/env bash + + if [ -z "${IMAGE_URL}" ] || [ -z "${IMAGE_DIGEST}" ]; then + echo 'No image-url or image-digest param provided. Skipping upload.' + exit 0 + fi + UPLOAD_FILES="coverity-results.sarif excluded-findings.json" + + for UPLOAD_FILE in ${UPLOAD_FILES}; do + if [ ! -f "${UPLOAD_FILE}" ]; then + echo "No ${UPLOAD_FILE} exists. Skipping upload." + continue + fi + if [ "${UPLOAD_FILES}" == "excluded-findings.json" ]; then + MEDIA_TYPE=application/json + else + MEDIA_TYPE=application/sarif+json + fi + + echo "Selecting auth" + select-oci-auth "${IMAGE_URL}" > "${HOME}/auth.json" + echo "Attaching to ${IMAGE_URL}" + oras attach --no-tty --registry-config "$HOME/auth.json" --artifact-type "${MEDIA_TYPE}" "${IMAGE_URL}" "${UPLOAD_FILE}:${MEDIA_TYPE}" + done + workspaces: + - name: workspace diff --git a/task/sast-coverity-check/OWNERS b/task/sast-coverity-check/OWNERS new file mode 100644 index 000000000..4f4bc81c0 --- /dev/null +++ b/task/sast-coverity-check/OWNERS @@ -0,0 +1,6 @@ +# See the OWNERS docs: https://go.k8s.io/owners +approvers: + - integration-team +reviewers: + - integration-team + - kdudka