diff --git a/pac/docker-build-rhtap/docker-pull-request.yaml b/pac/docker-build-rhtap/docker-pull-request.yaml index 48584c9..e9c2e73 100644 --- a/pac/docker-build-rhtap/docker-pull-request.yaml +++ b/pac/docker-build-rhtap/docker-pull-request.yaml @@ -10,12 +10,16 @@ metadata: pipelinesascode.tekton.dev/task-0: "{{values.rawUrl}}/pac/tasks/init.yaml" pipelinesascode.tekton.dev/task-1: "{{values.rawUrl}}/pac/tasks/git-clone.yaml" pipelinesascode.tekton.dev/task-2: "{{values.rawUrl}}/pac/tasks/buildah-rhtap.yaml" - pipelinesascode.tekton.dev/task-3: "{{values.rawUrl}}/pac/tasks/acs-image-check.yaml" - pipelinesascode.tekton.dev/task-4: "{{values.rawUrl}}/pac/tasks/acs-image-scan.yaml" - pipelinesascode.tekton.dev/task-5: "{{values.rawUrl}}/pac/tasks/acs-deploy-check.yaml" - pipelinesascode.tekton.dev/task-6: "{{values.rawUrl}}/pac/tasks/update-deployment.yaml" - pipelinesascode.tekton.dev/task-7: "{{values.rawUrl}}/pac/tasks/show-sbom-rhdh.yaml" - pipelinesascode.tekton.dev/task-8: "{{values.rawUrl}}/pac/tasks/summary.yaml" + pipelinesascode.tekton.dev/task-3: "{{values.rawUrl}}/pac/tasks/sast-unicode-check.yaml" + pipelinesascode.tekton.dev/task-4: "{{values.rawUrl}}/pac/tasks/apply-tags.yaml" + pipelinesascode.tekton.dev/task-5: "{{values.rawUrl}}/pac/tasks/push-dockerfile.yaml" + pipelinesascode.tekton.dev/task-6: "{{values.rawUrl}}/pac/tasks/rpms-signature-scan.yaml" + pipelinesascode.tekton.dev/task-7: "{{values.rawUrl}}/pac/tasks/acs-image-check.yaml" + pipelinesascode.tekton.dev/task-8: "{{values.rawUrl}}/pac/tasks/acs-image-scan.yaml" + pipelinesascode.tekton.dev/task-9: "{{values.rawUrl}}/pac/tasks/acs-deploy-check.yaml" + pipelinesascode.tekton.dev/task-10: "{{values.rawUrl}}/pac/tasks/update-deployment.yaml" + pipelinesascode.tekton.dev/task-11: "{{values.rawUrl}}/pac/tasks/show-sbom-rhdh.yaml" + pipelinesascode.tekton.dev/task-12: "{{values.rawUrl}}/pac/tasks/summary.yaml" spec: params: - name: dockerfile diff --git a/pac/docker-build-rhtap/docker-push.yaml b/pac/docker-build-rhtap/docker-push.yaml index fa2a9c1..9c42a96 100644 --- a/pac/docker-build-rhtap/docker-push.yaml +++ b/pac/docker-build-rhtap/docker-push.yaml @@ -10,12 +10,16 @@ metadata: pipelinesascode.tekton.dev/task-0: "{{values.rawUrl}}/pac/tasks/init.yaml" pipelinesascode.tekton.dev/task-1: "{{values.rawUrl}}/pac/tasks/git-clone.yaml" pipelinesascode.tekton.dev/task-2: "{{values.rawUrl}}/pac/tasks/buildah-rhtap.yaml" - pipelinesascode.tekton.dev/task-3: "{{values.rawUrl}}/pac/tasks/acs-image-check.yaml" - pipelinesascode.tekton.dev/task-4: "{{values.rawUrl}}/pac/tasks/acs-image-scan.yaml" - pipelinesascode.tekton.dev/task-5: "{{values.rawUrl}}/pac/tasks/acs-deploy-check.yaml" - pipelinesascode.tekton.dev/task-6: "{{values.rawUrl}}/pac/tasks/update-deployment.yaml" - pipelinesascode.tekton.dev/task-7: "{{values.rawUrl}}/pac/tasks/show-sbom-rhdh.yaml" - pipelinesascode.tekton.dev/task-8: "{{values.rawUrl}}/pac/tasks/summary.yaml" + pipelinesascode.tekton.dev/task-3: "{{values.rawUrl}}/pac/tasks/sast-unicode-check.yaml" + pipelinesascode.tekton.dev/task-4: "{{values.rawUrl}}/pac/tasks/apply-tags.yaml" + pipelinesascode.tekton.dev/task-5: "{{values.rawUrl}}/pac/tasks/push-dockerfile.yaml" + pipelinesascode.tekton.dev/task-6: "{{values.rawUrl}}/pac/tasks/rpms-signature-scan.yaml" + pipelinesascode.tekton.dev/task-7: "{{values.rawUrl}}/pac/tasks/acs-image-check.yaml" + pipelinesascode.tekton.dev/task-8: "{{values.rawUrl}}/pac/tasks/acs-image-scan.yaml" + pipelinesascode.tekton.dev/task-9: "{{values.rawUrl}}/pac/tasks/acs-deploy-check.yaml" + pipelinesascode.tekton.dev/task-10: "{{values.rawUrl}}/pac/tasks/update-deployment.yaml" + pipelinesascode.tekton.dev/task-11: "{{values.rawUrl}}/pac/tasks/show-sbom-rhdh.yaml" + pipelinesascode.tekton.dev/task-12: "{{values.rawUrl}}/pac/tasks/summary.yaml" spec: params: - name: dockerfile diff --git a/pac/pipelines/docker-build-rhtap.yaml b/pac/pipelines/docker-build-rhtap.yaml index de3bf2e..d1c98a3 100644 --- a/pac/pipelines/docker-build-rhtap.yaml +++ b/pac/pipelines/docker-build-rhtap.yaml @@ -143,6 +143,62 @@ spec: workspaces: - name: source workspace: workspace + - name: sast-unicode-check + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: sast-unicode-check + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" + workspaces: + - name: workspace + workspace: workspace + - name: apply-tags + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + runAfter: + - build-image-index + taskRef: + name: apply-tags + - name: push-dockerfile + params: + - name: IMAGE + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: IMAGE_DIGEST + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + - name: DOCKERFILE + value: $(params.dockerfile) + - name: CONTEXT + value: $(params.path-context) + runAfter: + - build-image-index + taskRef: + name: push-dockerfile + workspaces: + - name: workspace + workspace: workspace + - name: rpms-signature-scan + params: + - name: image-url + value: $(tasks.build-image-index.results.IMAGE_URL) + - name: image-digest + value: $(tasks.build-image-index.results.IMAGE_DIGEST) + runAfter: + - build-image-index + taskRef: + name: rpms-signature-scan + when: + - input: $(params.skip-checks) + operator: in + values: + - "false" - name: acs-image-check params: - name: rox-secret-name diff --git a/pac/source-repo/docker-pull-request.yaml b/pac/source-repo/docker-pull-request.yaml index 48584c9..e9c2e73 100644 --- a/pac/source-repo/docker-pull-request.yaml +++ b/pac/source-repo/docker-pull-request.yaml @@ -10,12 +10,16 @@ metadata: pipelinesascode.tekton.dev/task-0: "{{values.rawUrl}}/pac/tasks/init.yaml" pipelinesascode.tekton.dev/task-1: "{{values.rawUrl}}/pac/tasks/git-clone.yaml" pipelinesascode.tekton.dev/task-2: "{{values.rawUrl}}/pac/tasks/buildah-rhtap.yaml" - pipelinesascode.tekton.dev/task-3: "{{values.rawUrl}}/pac/tasks/acs-image-check.yaml" - pipelinesascode.tekton.dev/task-4: "{{values.rawUrl}}/pac/tasks/acs-image-scan.yaml" - pipelinesascode.tekton.dev/task-5: "{{values.rawUrl}}/pac/tasks/acs-deploy-check.yaml" - pipelinesascode.tekton.dev/task-6: "{{values.rawUrl}}/pac/tasks/update-deployment.yaml" - pipelinesascode.tekton.dev/task-7: "{{values.rawUrl}}/pac/tasks/show-sbom-rhdh.yaml" - pipelinesascode.tekton.dev/task-8: "{{values.rawUrl}}/pac/tasks/summary.yaml" + pipelinesascode.tekton.dev/task-3: "{{values.rawUrl}}/pac/tasks/sast-unicode-check.yaml" + pipelinesascode.tekton.dev/task-4: "{{values.rawUrl}}/pac/tasks/apply-tags.yaml" + pipelinesascode.tekton.dev/task-5: "{{values.rawUrl}}/pac/tasks/push-dockerfile.yaml" + pipelinesascode.tekton.dev/task-6: "{{values.rawUrl}}/pac/tasks/rpms-signature-scan.yaml" + pipelinesascode.tekton.dev/task-7: "{{values.rawUrl}}/pac/tasks/acs-image-check.yaml" + pipelinesascode.tekton.dev/task-8: "{{values.rawUrl}}/pac/tasks/acs-image-scan.yaml" + pipelinesascode.tekton.dev/task-9: "{{values.rawUrl}}/pac/tasks/acs-deploy-check.yaml" + pipelinesascode.tekton.dev/task-10: "{{values.rawUrl}}/pac/tasks/update-deployment.yaml" + pipelinesascode.tekton.dev/task-11: "{{values.rawUrl}}/pac/tasks/show-sbom-rhdh.yaml" + pipelinesascode.tekton.dev/task-12: "{{values.rawUrl}}/pac/tasks/summary.yaml" spec: params: - name: dockerfile diff --git a/pac/source-repo/docker-push.yaml b/pac/source-repo/docker-push.yaml index fa2a9c1..9c42a96 100644 --- a/pac/source-repo/docker-push.yaml +++ b/pac/source-repo/docker-push.yaml @@ -10,12 +10,16 @@ metadata: pipelinesascode.tekton.dev/task-0: "{{values.rawUrl}}/pac/tasks/init.yaml" pipelinesascode.tekton.dev/task-1: "{{values.rawUrl}}/pac/tasks/git-clone.yaml" pipelinesascode.tekton.dev/task-2: "{{values.rawUrl}}/pac/tasks/buildah-rhtap.yaml" - pipelinesascode.tekton.dev/task-3: "{{values.rawUrl}}/pac/tasks/acs-image-check.yaml" - pipelinesascode.tekton.dev/task-4: "{{values.rawUrl}}/pac/tasks/acs-image-scan.yaml" - pipelinesascode.tekton.dev/task-5: "{{values.rawUrl}}/pac/tasks/acs-deploy-check.yaml" - pipelinesascode.tekton.dev/task-6: "{{values.rawUrl}}/pac/tasks/update-deployment.yaml" - pipelinesascode.tekton.dev/task-7: "{{values.rawUrl}}/pac/tasks/show-sbom-rhdh.yaml" - pipelinesascode.tekton.dev/task-8: "{{values.rawUrl}}/pac/tasks/summary.yaml" + pipelinesascode.tekton.dev/task-3: "{{values.rawUrl}}/pac/tasks/sast-unicode-check.yaml" + pipelinesascode.tekton.dev/task-4: "{{values.rawUrl}}/pac/tasks/apply-tags.yaml" + pipelinesascode.tekton.dev/task-5: "{{values.rawUrl}}/pac/tasks/push-dockerfile.yaml" + pipelinesascode.tekton.dev/task-6: "{{values.rawUrl}}/pac/tasks/rpms-signature-scan.yaml" + pipelinesascode.tekton.dev/task-7: "{{values.rawUrl}}/pac/tasks/acs-image-check.yaml" + pipelinesascode.tekton.dev/task-8: "{{values.rawUrl}}/pac/tasks/acs-image-scan.yaml" + pipelinesascode.tekton.dev/task-9: "{{values.rawUrl}}/pac/tasks/acs-deploy-check.yaml" + pipelinesascode.tekton.dev/task-10: "{{values.rawUrl}}/pac/tasks/update-deployment.yaml" + pipelinesascode.tekton.dev/task-11: "{{values.rawUrl}}/pac/tasks/show-sbom-rhdh.yaml" + pipelinesascode.tekton.dev/task-12: "{{values.rawUrl}}/pac/tasks/summary.yaml" spec: params: - name: dockerfile diff --git a/pac/tasks/apply-tags.yaml b/pac/tasks/apply-tags.yaml new file mode 100644 index 0000000..eef712f --- /dev/null +++ b/pac/tasks/apply-tags.yaml @@ -0,0 +1,85 @@ +apiVersion: tekton.dev/v1beta1 +kind: Task +metadata: + labels: + app.kubernetes.io/version: "0.1" + annotations: + tekton.dev/pipelines.minVersion: "0.12.1" + tekton.dev/tags: "konflux" + name: apply-tags +spec: + description: >- + Applies additional tags to the built image. + params: + - name: IMAGE + description: Reference of image that was pushed to registry in the buildah task. + type: string + - name: ADDITIONAL_TAGS + description: Additional tags that will be applied to the image in the registry. + type: array + default: [] + - name: CA_TRUST_CONFIG_MAP_NAME + type: string + description: The name of the ConfigMap to read CA bundle data from. + default: trusted-ca + - name: CA_TRUST_CONFIG_MAP_KEY + type: string + description: The name of the key in the ConfigMap that contains the CA bundle data. + default: ca-bundle.crt + stepTemplate: + volumeMounts: + - name: trusted-ca + mountPath: /etc/pki/tls/certs/ca-custom-bundle.crt + subPath: ca-bundle.crt + readOnly: true + steps: + - name: apply-additional-tags-from-parameter + image: registry.access.redhat.com/ubi9/skopeo:9.4-14.1728984400@sha256:891ee232a9319ed0f675c318f9605422bde7436328e7faec7dc896a206a78e54 + args: + - $(params.ADDITIONAL_TAGS[*]) + env: + - name: IMAGE + value: $(params.IMAGE) + script: | + #!/bin/bash + + if [ "$#" -ne 0 ]; then + IMAGE_WITHOUT_TAG=$(echo "$IMAGE" | sed 's/:[^:]*$//') + for tag in "$@"; do + echo "Applying tag $tag" + skopeo copy --multi-arch index-only docker://"$IMAGE" docker://"$IMAGE_WITHOUT_TAG:$tag" + done + else + echo "No additional tags parameter specified" + fi + + - name: apply-additional-tags-from-image-label + image: registry.access.redhat.com/ubi9/skopeo:9.4-14.1728984400@sha256:891ee232a9319ed0f675c318f9605422bde7436328e7faec7dc896a206a78e54 + env: + - name: IMAGE + value: $(params.IMAGE) + script: | + #!/bin/bash + + ADDITIONAL_TAGS_FROM_IMAGE_LABEL=$(skopeo inspect --no-tags --format '{{ index .Labels "konflux.additional-tags" }}' "docker://$IMAGE") + + if [ -n "${ADDITIONAL_TAGS_FROM_IMAGE_LABEL}" ]; then + IFS=', ' read -r -a tags_array <<< "$ADDITIONAL_TAGS_FROM_IMAGE_LABEL" + + IMAGE_WITHOUT_TAG=$(echo "$IMAGE" | sed 's/:[^:]*$//') + for tag in "${tags_array[@]}" + do + echo "Applying tag $tag" + skopeo copy --multi-arch index-only docker://"$IMAGE" docker://"$IMAGE_WITHOUT_TAG:$tag" + done + else + echo "No additional tags specified in the image labels" + fi + volumes: + - name: trusted-ca + configMap: + name: $(params.CA_TRUST_CONFIG_MAP_NAME) + items: + - key: $(params.CA_TRUST_CONFIG_MAP_KEY) + path: ca-bundle.crt + optional: true diff --git a/pac/tasks/buildah-rhtap.yaml b/pac/tasks/buildah-rhtap.yaml index 91c621e..db92791 100644 --- a/pac/tasks/buildah-rhtap.yaml +++ b/pac/tasks/buildah-rhtap.yaml @@ -103,6 +103,13 @@ spec: --digestfile /tmp/files/image-digest $IMAGE \ docker://$IMAGE + # Push the image to a unique tag to avoid race conditions + buildah push \ + --tls-verify="$TLSVERIFY" \ + --retry=5 \ + --digestfile /tmp/files/image-digest "$IMAGE" \ + "docker://${IMAGE%:*}:$(context.taskRun.name)" + # Set task results buildah images --format '{{ .Name }}:{{ .Tag }}@{{ .Digest }}' | grep -v $IMAGE > $(results.BASE_IMAGES_DIGESTS.path) cat /tmp/files/image-digest | tee $(results.IMAGE_DIGEST.path) diff --git a/pac/tasks/init.yaml b/pac/tasks/init.yaml index cb7a342..93b54ac 100644 --- a/pac/tasks/init.yaml +++ b/pac/tasks/init.yaml @@ -41,7 +41,7 @@ spec: echo "Determine if Image Already Exists" # Build the image when rebuild is set to true or image does not exist # The image check comes last to avoid unnecessary, slow API calls - if [ "$REBUILD" == "true" ] || [ "$SKIP_CHECKS" == "false" ] || ! skopeo inspect --raw docker://$IMAGE_URL &>/dev/null; then + if [ "$REBUILD" == "true" ] || [ "$SKIP_CHECKS" == "false" ] || ! skopeo inspect --no-tags --raw "docker://$IMAGE_URL" &>/dev/null; then echo -n "true" > $(results.build.path) else echo -n "false" > $(results.build.path) diff --git a/pac/tasks/push-dockerfile.yaml b/pac/tasks/push-dockerfile.yaml new file mode 100644 index 0000000..ef3ab5e --- /dev/null +++ b/pac/tasks/push-dockerfile.yaml @@ -0,0 +1,100 @@ +apiVersion: tekton.dev/v1 +kind: Task +metadata: + labels: + app.kubernetes.io/version: "0.1" + build.appstudio.redhat.com/build_type: "docker" + annotations: + tekton.dev/pipelines.minVersion: "0.12.1" + tekton.dev/tags: "image-build, appstudio" + name: push-dockerfile +spec: + description: |- + Discover Dockerfile from source code and push it to registry as an OCI artifact. + params: + - name: IMAGE + description: The built binary image. The Dockerfile is pushed to the same image repository alongside. + type: string + - name: IMAGE_DIGEST + description: The built binary image digest, which is used to construct the tag of Dockerfile image. + type: string + - name: DOCKERFILE + description: Path to the Dockerfile. + type: string + default: ./Dockerfile + - name: CONTEXT + description: Path to the directory to use as context. + type: string + default: . + - name: TAG_SUFFIX + description: Suffix of the Dockerfile image tag. + type: string + default: .dockerfile + - name: ARTIFACT_TYPE + description: Artifact type of the Dockerfile image. + type: string + default: application/vnd.konflux.dockerfile + results: + - name: IMAGE_REF + description: Digest-pinned image reference to the Dockerfile image. + steps: + - name: push + image: quay.io/konflux-ci/oras:latest@sha256:b7e810730d97fe862826a048773a7539e469453df3681fd22de9754722266c69 + workingDir: $(workspaces.workspace.path) + env: + - name: IMAGE + value: $(params.IMAGE) + - name: IMAGE_DIGEST + value: $(params.IMAGE_DIGEST) + - name: TAG_SUFFIX + value: $(params.TAG_SUFFIX) + - name: DOCKERFILE + value: $(params.DOCKERFILE) + - name: CONTEXT + value: $(params.CONTEXT) + - name: ARTIFACT_TYPE + value: $(params.ARTIFACT_TYPE) + - name: IMAGE_REF_RESULT + value: $(results.IMAGE_REF.path) + script: | + set -eu + set -o pipefail + + # Same discovery logic used in buildah task + SOURCE_CODE_DIR=source + if [ -e "$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$CONTEXT/$DOCKERFILE" + elif [ -e "$SOURCE_CODE_DIR/$DOCKERFILE" ]; then + dockerfile_path="$(pwd)/$SOURCE_CODE_DIR/$DOCKERFILE" + elif echo "$DOCKERFILE" | grep -q "^https\?://"; then + echo "Fetch Dockerfile from $DOCKERFILE" + dockerfile_path=$(mktemp --suffix=-dockerfile) + http_code=$(curl -s -L -w "%{http_code}" --output "$dockerfile_path" "$DOCKERFILE") + if [ $http_code != 200 ]; then + echo "No Dockerfile is fetched. Server responds $http_code" + exit 1 + fi + else + echo "Cannot find Dockerfile $DOCKERFILE" + exit 1 + fi + + echo "Selecting auth for $IMAGE" + auth_json=$(mktemp) + select-oci-auth $IMAGE >"$auth_json" + + dockerfile_image=${IMAGE%:*}:${IMAGE_DIGEST/:/-}${TAG_SUFFIX} + + dockerfile_for_upload_path=/tmp/Dockerfile + cp "$dockerfile_path" "$dockerfile_for_upload_path" + cd "$(dirname $dockerfile_for_upload_path)" + retry oras push --no-tty \ + --format json \ + --registry-config "$auth_json" \ + --artifact-type "$ARTIFACT_TYPE" \ + "$dockerfile_image" "$(basename $dockerfile_for_upload_path)" \ + | yq '.reference' | tr -d '\r\n' >"$IMAGE_REF_RESULT" + + workspaces: + - name: workspace + description: Workspace containing the source code from where the Dockerfile is discovered. diff --git a/pac/tasks/rpms-signature-scan.yaml b/pac/tasks/rpms-signature-scan.yaml new file mode 100644 index 0000000..dd7211e --- /dev/null +++ b/pac/tasks/rpms-signature-scan.yaml @@ -0,0 +1,96 @@ +--- +apiVersion: tekton.dev/v1 +kind: Task +metadata: + name: rpms-signature-scan +spec: + params: + - name: image-url + type: string + description: Image URL + - name: image-digest + type: string + description: Image digest to scan + - name: workdir + type: string + default: /tmp + description: | + Directory that will be used for storing temporary + files produced by this task. + - name: ca-trust-config-map-name + type: string + description: The name of the ConfigMap to read CA bundle data from. + default: trusted-ca + - name: ca-trust-config-map-key + type: string + description: The name of the key in the ConfigMap that contains the CA bundle data. + default: ca-bundle.crt + results: + - name: TEST_OUTPUT + description: Tekton task test output. + - name: RPMS_DATA + description: Information about signed and unsigned RPMs + - name: IMAGES_PROCESSED + description: Images processed in the task. + volumes: + - name: workdir + emptyDir: {} + - name: trusted-ca + configMap: + name: $(params.ca-trust-config-map-name) + items: + - key: $(params.ca-trust-config-map-key) + path: ca-bundle.crt + optional: true + steps: + - name: rpms-signature-scan + image: quay.io/redhat-appstudio/tools@sha256:49f776c18b06cd7343103652106336c27d116dd367a7d5a2538aab0f40656d27 + volumeMounts: + - name: workdir + mountPath: "$(params.workdir)" + - name: trusted-ca + mountPath: /etc/pki/tls/certs/ca-custom-bundle.crt + subPath: ca-bundle.crt + readOnly: true + env: + - name: IMAGE_URL + value: "$(params.image-url)" + - name: IMAGE_DIGEST + value: "$(params.image-digest)" + - name: WORKDIR + value: "$(params.workdir)" + script: | + #!/bin/bash + set -ex + set -o pipefail + + rpm_verifier \ + --image-url "${IMAGE_URL}" \ + --image-digest "${IMAGE_DIGEST}" \ + --workdir "${WORKDIR}" \ + - name: output-results + image: quay.io/konflux-ci/konflux-test:v1.4.9@sha256:eee855e60b437d9a55a30e63f2eb7f95d9fd6d3b111c32cac8730c9b7a071394 + volumeMounts: + - name: workdir + mountPath: "$(params.workdir)" + env: + - name: WORKDIR + value: "$(params.workdir)" + script: | + #!/bin/bash + set -ex + + source /utils.sh + status=$(cat "${WORKDIR}"/status) + rpms_data=$(cat "${WORKDIR}"/results) + images_processed=$(cat "${WORKDIR}"/images_processed) + if [ "$status" == "ERROR" ]; then + note="Task $(context.task.name) failed to scan images. Refer to Tekton task output for details" + else + note="Task $(context.task.name) completed successfully" + fi + + TEST_OUTPUT=$(make_result_json -r "$status" -t "$note") + echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)" + echo "${rpms_data}" | tee "$(results.RPMS_DATA.path)" + echo "${images_processed}" | tee "$(results.IMAGES_PROCESSED.path)" diff --git a/pac/tasks/sast-unicode-check.yaml b/pac/tasks/sast-unicode-check.yaml new file mode 100644 index 0000000..304b485 --- /dev/null +++ b/pac/tasks/sast-unicode-check.yaml @@ -0,0 +1,268 @@ +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_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: | + Whether to record the excluded findings (defaults to false). + If `true`, 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/konflux-ci/konflux-test:v1.4.8@sha256:2224fabdb0a28a415d4af4c58ae53d7c4c53c83c315f12e07d1d7f48a80bfa70 + # 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: PROJECT_NAME + value: $(params.PROJECT_NAME) + - 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: RECORD_EXCLUDED + value: $(params.RECORD_EXCLUDED) + - name: SOURCE_CODE_DIR + value: $(workspaces.workspace.path) + - name: COMPONENT_LABEL + valueFrom: + fieldRef: + fieldPath: metadata.labels['appstudio.openshift.io/component'] + script: | + #!/usr/bin/env bash + set -exuo pipefail + + # shellcheck source=/dev/null + . /utils.sh + trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT + + if [[ -z "${PROJECT_NAME}" ]]; then + PROJECT_NAME=${COMPONENT_LABEL} + fi + + echo "The PROJECT_NAME used is: ${PROJECT_NAME}" + + 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 + + # shellcheck disable=SC2086 + LANG=en_US.utf8 ./find-unicode-control/find_unicode_control.py ${FIND_UNICODE_CONTROL_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_NAME}" ]]; then + csfilter_kfp_cmd+=(--project-nvr="${PROJECT_NAME}") + 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:7a85f12b14b9122df29450dfa60c6d035b04db1a7372f7b606a0fb74ed716844 + 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