diff --git a/.tekton/pull-request.yaml b/.tekton/pull-request.yaml index d7172aeae9..5b4271e893 100644 --- a/.tekton/pull-request.yaml +++ b/.tekton/pull-request.yaml @@ -5,7 +5,7 @@ metadata: name: build-definitions-pull-request annotations: pipelinesascode.tekton.dev/on-cel-expression: (event == "pull_request" && target_branch == "main" && ( !has(body.pull_request) || !body.pull_request.draft) ) || (event == "push" && target_branch.startsWith("gh-readonly-queue/main/")) - pipelinesascode.tekton.dev/task: "[task/git-clone/0.1/git-clone.yaml, .tekton/tasks/buildah.yaml, .tekton/tasks/task-lint.yaml, .tekton/tasks/e2e-test.yaml, task/sast-snyk-check/0.2/sast-snyk-check.yaml]" + pipelinesascode.tekton.dev/task: "[task/git-clone/0.1/git-clone.yaml, .tekton/tasks/buildah.yaml, .tekton/tasks/task-lint.yaml, .tekton/tasks/e2e-test.yaml, task/sast-snyk-check/0.2/sast-snyk-check.yaml, task/sast-unicode-check/0.1/sast-unicode-check.yaml]" pipelinesascode.tekton.dev/max-keep-runs: "5" spec: params: @@ -52,6 +52,14 @@ spec: workspaces: - name: workspace workspace: workspace + - name: sast-unicode-check + runAfter: + - build-container + taskRef: + name: sast-unicode-check + workspaces: + - name: workspace + workspace: workspace - name: build-container runAfter: - task-lint-check diff --git a/task/sast-unicode-check/0.1/README.md b/task/sast-unicode-check/0.1/README.md new file mode 100644 index 0000000000..5a295dd68c --- /dev/null +++ b/task/sast-unicode-check/0.1/README.md @@ -0,0 +1,29 @@ +# sast-unicode-check task + +## Description: + +The sast-unicode-check task uses [find-unicode-control](https://github.com/siddhesh/find-unicode-control.git) tool to perform Static Application Security Testing (SAST) to look for non-printable unicode characters in all text files in a source tree. + +## Parameters: + +| name | description | Default Value | Required | +|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|----------| +| FIND_UNICODE_CONTROL_GIT_URL | URL from repository to find unicode control. | "https://github.com/siddhesh/find-unicode-control.git#c2accbfbba7553a8bc1ebd97089ae08ad8347e58" | No | +| FIND_UNICODE_CONTROL_ARGS | arguments for find-unicode-control command. | "-p bidi -v -d -t" | 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_NVR | Name-Version-Release (NVR) of the scanned project. It is used to find path exclusions (it is optional). | "" | No | +| RECORD_EXCLUDED | Whether to record the excluded findings (defaults to false). If `true`, the the excluded findings will be stored in `excluded-findings.json`. | "false" | No | + +## Results: + +| name | description | +|---------------|------------------------------------------| +| TEST_OUTPUT | Tekton task test output. | + +## Source repository for image: + +https://github.com/konflux-ci/konflux-test + +## Additional links: + +* https://github.com/siddhesh/find-unicode-control.git diff --git a/task/sast-unicode-check/0.1/sast-unicode-check.yaml b/task/sast-unicode-check/0.1/sast-unicode-check.yaml new file mode 100644 index 0000000000..cacdaa61ca --- /dev/null +++ b/task/sast-unicode-check/0.1/sast-unicode-check.yaml @@ -0,0 +1,259 @@ +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-unicode-check +spec: + description: >- + Scans source code for non-printable unicode characters in all text files. + results: + - description: Tekton task test output. + name: TEST_OUTPUT + params: + - name: image-url + type: string + description: Image URL. + default: "" + - name: FIND_UNICODE_CONTROL_GIT_URL + type: string + description: URL from repository to find unicode control. + default: "https://github.com/siddhesh/find-unicode-control.git#c2accbfbba7553a8bc1ebd97089ae08ad8347e58" + - name: FIND_UNICODE_CONTROL_ARGS + type: string + description: arguments for find-unicode-control command. + default: "-p bidi -v -d -t" + - 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_NVR + type: string + description: | + Name-Version-Release (NVR) of the scanned project. + It is used to find path exclusions (it is optional). + default: "" + - name: RECORD_EXCLUDED + type: string + description: | + Whether to record the excluded findings (defaults to false). + If `true`, the the excluded findings will be stored in `excluded-findings.json`. + default: "false" + - 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 + volumes: + - name: trusted-ca + configMap: + name: $(params.caTrustConfigMapName) + items: + - key: $(params.caTrustConfigMapKey) + path: ca-bundle.crt + optional: true + steps: + - name: sast-unicode-check + image: quay.io/redhat-appstudio/konflux-test:v1.4.6@sha256:5f298d8d990dfa82023e50029b71b08e19c3c9cedb181dfc4bc86c9ecad8700c + # 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: + - mountPath: /mnt/trusted-ca + name: trusted-ca + readOnly: true + env: + - name: KFP_GIT_URL + value: $(params.KFP_GIT_URL) + - name: FIND_UNICODE_CONTROL_GIT_URL + value: $(params.FIND_UNICODE_CONTROL_GIT_URL) + - name: FIND_UNICODE_CONTROL_ARGS + value: $(params.FIND_UNICODE_CONTROL_ARGS) + - name: PROJECT_NVR + value: $(params.PROJECT_NVR) + - name: RECORD_EXCLUDED + value: $(params.RECORD_EXCLUDED) + - name: SOURCE_CODE_DIR + value: $(workspaces.workspace.path) + script: | + #!/usr/bin/env bash + set -exuo pipefail + + # shellcheck source=/dev/null + . /utils.sh + trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT + + SCAN_PROP="" + + 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 + + # Clone the source code from upstream repo + GIT_URL=$(echo "${FIND_UNICODE_CONTROL_GIT_URL}" | awk -F'#' '{print $1}') + REV=$(echo "${FIND_UNICODE_CONTROL_GIT_URL}" | awk -F'#' '{print $2}') + + # Clone find-unicode-control repository + if ! git clone "${GIT_URL}" find-unicode-control; then + echo "Failed to clone the repository: ${GIT_URL}" >&2 + note="Task $(context.task.name) failed: For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 1 + fi + + if [[ -n "${REV}" ]]; then + if ! git -C ./find-unicode-control/ checkout "${REV}"; then + echo "Failed to checkout the repository: ${GIT_URL} to ${REV}" >&2 + note="Task $(context.task.name) failed: For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 1 + fi + SCAN_PROP="find-unicode-control-git-url:${FIND_UNICODE_CONTROL_GIT_URL}" + else + git_url_suffix=$(git -C ./find-unicode-control/ rev-parse HEAD) + SCAN_PROP="find-unicode-control-git-url:${FIND_UNICODE_CONTROL_GIT_URL}#${git_url_suffix}" + fi + + # Find unicode control + FUC_EXIT_CODE=0 + mapfile -t fuc_args <<< "${FIND_UNICODE_CONTROL_ARGS}" + LANG=en_US.utf8 ./find-unicode-control/find_unicode_control.py "${fuc_args[@]}" "${SOURCE_CODE_DIR}/source" \ + >raw_sast_unicode_check_out.txt \ + 2>raw_sast_unicode_check_out.log \ + || FUC_EXIT_CODE=$? + if [[ "${FUC_EXIT_CODE}" -ne 0 ]] && [[ "${FUC_EXIT_CODE}" -ne 1 ]]; then + echo "Failed to run find-unicode-control command" >&2 + cat raw_sast_unicode_check_out.log + note="Task $(context.task.name) failed: For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 1 + fi + + # Translate the output format + if ! sed -i raw_sast_unicode_check_out.txt -E -e 's|(.*:[0-9]+)(.*)|\1: warning:\2|' -e 's|^|Error: UNICONTROL_WARNING:\n|'; then + echo "Error: failed to translate the unicontrol output format" >&2 + note="Task $(context.task.name) failed: For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 1 + fi + + # Process all results as configured with CSGERP_OPTS + CSGERP_OPTS=( + --mode=json + --remove-duplicates + --embed-context=3 + --set-scan-prop="${SCAN_PROP}" + --strip-path-prefix="${SOURCE_CODE_DIR}"/source/ + ) + # In order to generate csdiff/v1, we need to add the whole path of the source code as + # sast-unicode-check only provides an URI to embed the context + if ! csgrep "${CSGERP_OPTS[@]}" raw_sast_unicode_check_out.txt > processed_sast_unicode_check_out.json 2> processed_sast_unicode_check_out.err; then + echo "Error occurred while running csgrep with CSGERP_OPTS:" + cat processed_sast_unicode_check_out.err + note="Task $(context.task.name) failed: For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 1 + fi + + csgrep --mode=evtstat processed_sast_unicode_check_out.json + + # Filter known false positives if KFP_GIT_URL is set + if [ -n "${KFP_GIT_URL}" ]; then + echo "Filtering false positives in results files using ${KFP_GIT_URL}..." >&2 + + # Build initial csfilter-kfp command + csfilter_kfp_cmd=( + csfilter-kfp + --verbose + --kfp-git-url="${KFP_GIT_URL}" + ) + + # Append --project-nvr option if PROJECT_NVR is set + if [[ -n "${PROJECT_NVR}" ]]; then + csfilter_kfp_cmd+=(--project-nvr="${PROJECT_NVR}") + fi + + # Append --record-excluded option if RECORD_EXCLUDED is true + if [[ "${RECORD_EXCLUDED}" == "true" ]]; then + csfilter_kfp_cmd+=(--record-excluded="excluded-findings.json") + fi + + if ! "${csfilter_kfp_cmd[@]}" processed_sast_unicode_check_out.json > sast_unicode_check_out.json 2> sast_unicode_check_out.error; then + echo "Failed to filter known false positives" >&2 + cat sast_unicode_check_out.error + note="Task $(context.task.name) failed: For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + echo "${ERROR_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + exit 1 + fi + else + echo "KFP_GIT_URL is not set. Skipping false positive filtering." >&2 + mv processed_sast_unicode_check_out.json sast_unicode_check_out.json + fi + + # Generate sarif report + csgrep --mode=sarif sast_unicode_check_out.json > sast_unicode_check_out.sarif + if [[ "${FUC_EXIT_CODE}" -eq 0 ]]; then + note="Task $(context.task.name) success: No finding was detected" + ERROR_OUTPUT=$(make_result_json -r SUCCESS -t "$note") + elif [[ "${FUC_EXIT_CODE}" -eq 1 ]] && [[ ! -s sast_unicode_check_out.sarif ]]; then + note="Task $(context.task.name) success: Some findings were detected, but filtered by known false positive" + ERROR_OUTPUT=$(make_result_json -r SUCCESS -t "$note") + else + echo "sast-unicode-check test failed because of the following issues:" + cat sast_unicode_check_out.json + TEST_OUTPUT= + parse_test_output "$(context.task.name)" sarif sast_unicode_check_out.sarif || true + note="Task $(context.task.name) failed: For details, check Tekton task log." + ERROR_OUTPUT=$(make_result_json -r ERROR -t "$note") + fi + echo "${TEST_OUTPUT:-${ERROR_OUTPUT}}" | tee "$(results.TEST_OUTPUT.path)" + - name: upload + image: quay.io/konflux-ci/oras:latest@sha256:f4b891ee3038a5f13cd92ff4f473faad5601c2434d1c6b9bccdfc134d9d5f820 + workingDir: $(workspaces.workspace.path)/hacbs/$(context.task.name) + env: + - name: IMAGE_URL + value: $(params.image-url) + script: | + #!/usr/bin/env bash + + if [ -z "${IMAGE_URL}" ]; then + echo 'No image-url param provided. Skipping upload.' + exit 0; + fi + + UPLOAD_FILES="sast_unicode_check_out.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-unicode-check/OWNERS b/task/sast-unicode-check/OWNERS new file mode 100644 index 0000000000..27203edec2 --- /dev/null +++ b/task/sast-unicode-check/OWNERS @@ -0,0 +1,5 @@ +# See the OWNERS docs: https://go.k8s.io/owners +approvers: + - integration-team +reviewers: + - integration-team