diff --git a/tasks/managed/embargo-check/README.md b/tasks/managed/embargo-check/README.md index ead6c7ef5..59e57de45 100644 --- a/tasks/managed/embargo-check/README.md +++ b/tasks/managed/embargo-check/README.md @@ -1,7 +1,9 @@ # embargo-check Tekton task to check if any issues or CVEs in the releaseNotes key of the data.json are embargoed. It checks the issues -by server using curl and checks the CVEs via an InternalRequest. If any issue or CVE is embargoed, the task will fail. +by server using curl and checks the CVEs via an InternalRequest. If any issue does not exist or any CVE is embargoed, +the task will fail. The task will also fail if a Jira issue listed is for a component that does not exist in the +releaseNotes.content.images section or if said component does not list the CVE from the issue. ## Parameters @@ -13,6 +15,14 @@ by server using curl and checks the CVEs via an InternalRequest. If any issue or | taskGitUrl | The url to the git repo where the release-service-catalog tasks to be used are stored | No | - | | taskGitRevision | The revision in the taskGitUrl repo to be used | No | - | +## Changes in 1.0.0 +* Authentication is added to checking issues in issues.redhat.com +* If the issue exists and is not of type `Vulnerability`, the task will pass on that issue +* If the issue is of type `Vulnerability` + * If the `Downstream Component Name` from the issue is not listed in the `releaseNotes.content.images` + section, the task will fail + * If the `CVE ID` from the issue is not present in its component's fixed cves section, the task will fail + ## Changes in 0.5.0 * Added taskGiturl and taskGitRevision parameters to be passed to the internalRequest * The pipeline is called via git resolver now instead of cluster resolver diff --git a/tasks/managed/embargo-check/embargo-check.yaml b/tasks/managed/embargo-check/embargo-check.yaml index 511250bd5..4af1c4556 100644 --- a/tasks/managed/embargo-check/embargo-check.yaml +++ b/tasks/managed/embargo-check/embargo-check.yaml @@ -4,7 +4,7 @@ kind: Task metadata: name: embargo-check labels: - app.kubernetes.io/version: "0.5.0" + app.kubernetes.io/version: "1.0.0" annotations: tekton.dev/pipelines.minVersion: "0.12.1" tekton.dev/tags: release @@ -12,7 +12,10 @@ spec: description: > Tekton task to check if any issues or CVEs in the releaseNotes key of the data.json are embargoed. It checks the issues by server using curl and checks the CVEs via an - InternalRequest. If any issue or CVE is embargoed, the task will fail. + InternalRequest. If any issue does not exist or any CVE is embargoed, the task will + fail. The task will also fail if a Jira issue listed is for a component that does + not exist in the releaseNotes.content.images section or if said component does not + list the CVE from the issue. params: - name: dataPath description: Path to the JSON string of the merged data to use in the data workspace @@ -36,6 +39,12 @@ spec: steps: - name: check-issues image: quay.io/konflux-ci/release-service-utils:0f82be4be43294b6a96846d87ef7f7c0b9e34267 + env: + - name: ACCESS_TOKEN + valueFrom: + secretKeyRef: + name: todo + key: TODO script: | #!/usr/bin/env bash set -x @@ -62,6 +71,12 @@ spec: exit 1 fi + set +x # There are a lot of custom fields, don't want to pollute the logs or leak the ACCESS_TOKEN + CUSTOM_RH_FIELDS="$(curl -H "Authorization: Bearer $ACCESS_TOKEN" https://issues.redhat.com/rest/api/2/field)" + set -x + CVE_FIELD="$(jq -r '.[] | select(.name=="CVE ID") | .id' <<< "$CUSTOM_RH_FIELDS")" + COMPONENT_FIELD="$(jq -r '.[] | select(.name=="Downstream Component Name") | .id' <<< "$CUSTOM_RH_FIELDS")" + RC=0 NUM_ISSUES=$(jq -cr '.releaseNotes.issues.fixed | length' "${DATA_FILE}") @@ -69,12 +84,45 @@ spec: issue=$(jq -c --argjson i "$i" '.releaseNotes.issues.fixed[$i]' "${DATA_FILE}") server=$(jq -r '.source' <<< "$issue") API=$(jq -r '.[] | select(.servers[] | contains("'"$server"'")) | .api' <<< "$SUPPORTED_ISSUE_TRACKERS") - API_URL="https://$(jq -r '.source' <<< "$issue")/${API}/$(jq -r '.id' <<< "$issue")" - curl --fail --output /dev/null "${API_URL}" + API_URL="https://" + # Add authentication for issues.redhat.com + if [ "$server" = "issues.redhat.com" ] ; then + API_URL="-H \"Authorization: Bearer $ACCESS_TOKEN\" ${API_URL}" + fi + API_URL="${API_URL}$(jq -r '.source' <<< "$issue")/${API}/$(jq -r '.id' <<< "$issue")" + OUTPUT=$(curl --retry 3 --fail "${API_URL}") if [ $? -ne 0 ] ; then - echo "${issue} is not publicly visible. Assuming it is embargoed and stopping pipelineRun execution." + echo "${issue} is not visible. Assuming it is embargoed and stopping pipelineRun execution." RC=1 + continue + fi + + # Perform additional checks only for Vulnerability issues for issues.redhat.com + if [ "$server" != "issues.redhat.com" ] ; then + continue + fi + ISSUE_TYPE=$(jq -r '.fields.issuetype.name' <<< "$OUTPUT") + if [ "$ISSUE_TYPE" != "Vulnerability" ] ; then + continue + fi + CVE_ID=$(jq -r --arg field "$CVE_FIELD" '.fields[$field]' <<< "$OUTPUT") + COMPONENT_NAME=$(jq -r --arg field "$COMPONENT_FIELD" '.fields[$field]' <<< "$OUTPUT") + if ! COMPONENT_IN_RELEASE_NOTES="$(jq -ec --arg name "$COMPONENT_NAME" \ + '.releaseNotes.content.images[] | select(.component==$name)' "$DATA_FILE")"; then + echo -n "Issue $issue lists 'Downstream Component Name' $COMPONENT_NAME but that component does" + echo " not appear in releaseNotes.content.images. Failing" + RC=2 + fi + + if [ "$(jq --arg CVE "$CVE_ID" '.cves.fixed | has($CVE)' <<< "$COMPONENT_IN_RELEASE_NOTES")" != "true" ] + then + echo -n "Issue $issue lists 'Downstream Component Name' $COMPONENT_NAME and 'CVE ID' $CVE_ID" + echo " but that CVE is not present in the releaseNotes.content.images section for that component." + echo "Failing" + RC=3 + continue fi + done exit $RC diff --git a/tasks/managed/embargo-check/tests/mocks.sh b/tasks/managed/embargo-check/tests/mocks.sh index a46e1c47d..52b2da7a5 100644 --- a/tasks/managed/embargo-check/tests/mocks.sh +++ b/tasks/managed/embargo-check/tests/mocks.sh @@ -6,15 +6,27 @@ function curl() { echo Mock curl called with: $* >&2 echo $* >> $(workspaces.data.path)/mock_curl.txt - if [[ "$*" == "--fail --output /dev/null https://jira.atlassian.com/rest/api/2/issue/ISSUE-123" ]] + if [[ "$*" == "--fail https://jira.atlassian.com/rest/api/2/issue/ISSUE-123" ]] then : - elif [[ "$*" == "--fail --output /dev/null https://bugzilla.redhat.com/rest/bug/12345" ]] + elif [[ "$*" == "--fail https://bugzilla.redhat.com/rest/bug/12345" ]] then : - elif [[ "$*" == "--fail --output /dev/null https://jira.atlassian.com/rest/api/2/issue/EMBARGOED-987" ]] + elif [[ "$*" == "--fail https://jira.atlassian.com/rest/api/2/issue/EMBARGOED-987" ]] then exit 1 + elif [[ "$*" == *"Authorization: Bearer"*"https://issues.redhat.com/rest/api/2/issue/MISSINGRH-123" ]] + then + exit 1 + elif [[ "$*" == *"Authorization: Bearer"*"https://issues.redhat.com/rest/api/2/issue/FEATURE-123" ]] # Not a Vulnerability + then + echo '{"fields":{"issuetype":{"name":"Feature"}}}' + elif [[ "$*" == *"Authorization: Bearer"*"https://issues.redhat.com/rest/api/2/issue/CVE-123" ]] # Vulnerability + then + echo '{"fields":{"issuetype":{"name":"Vulnerability"},"customfield_123":"CVE-123","customfield_456":"my-component"}}' + elif [[ "$*" == *"issues.redhat.com/rest/api/2/field" ]] + then + echo '[{"id":"customfield_123","name":"CVE ID","custom":true},{"id":"customfield_456","name":"Downstream Component Name","custom":true},{"id":"customfield_555","name":"Some other one","custom":true}]' else echo Error: Unexpected call exit 1 diff --git a/tasks/managed/embargo-check/tests/pre-apply-task-hook.sh b/tasks/managed/embargo-check/tests/pre-apply-task-hook.sh index f24da0ca4..81f312bf8 100755 --- a/tasks/managed/embargo-check/tests/pre-apply-task-hook.sh +++ b/tasks/managed/embargo-check/tests/pre-apply-task-hook.sh @@ -5,3 +5,7 @@ TASK_PATH="$1" SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) yq -i '.spec.steps[0].script = load_str("'$SCRIPT_DIR'/mocks.sh") + .spec.steps[0].script' "$TASK_PATH" yq -i '.spec.steps[1].script = load_str("'$SCRIPT_DIR'/mocks.sh") + .spec.steps[1].script' "$TASK_PATH" + +# Create a dummy access token secret (and delete it first if it exists) +kubectl delete secret todo --ignore-not-found +kubectl create secret generic todo --from-literal=TODO=abcdefg diff --git a/tasks/managed/embargo-check/tests/test-embargo-check-non-vuln-rh-issue.yaml b/tasks/managed/embargo-check/tests/test-embargo-check-non-vuln-rh-issue.yaml new file mode 100644 index 000000000..d941cd1f9 --- /dev/null +++ b/tasks/managed/embargo-check/tests/test-embargo-check-non-vuln-rh-issue.yaml @@ -0,0 +1,83 @@ +--- +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: test-embargo-check-non-vuln-rh-issue +spec: + description: Test for embargo-check with a issues.redhat.com issue that is not of type Vulnerability + workspaces: + - name: tests-workspace + tasks: + - name: setup + workspaces: + - name: data + workspace: tests-workspace + taskSpec: + workspaces: + - name: data + steps: + - name: setup-values + image: quay.io/konflux-ci/release-service-utils:0f82be4be43294b6a96846d87ef7f7c0b9e34267 + script: | + #!/usr/bin/env sh + set -eux + + cat > "$(workspaces.data.path)"/data.json << EOF + { + "releaseNotes": { + "issues": { + "fixed": [ + { + "id": "ISSUE-123", + "source": "jira.atlassian.com" + }, + { + "id": "FEATURE-123", + "source": "issues.redhat.com" + } + ] + } + } + } + EOF + - name: run-task + taskRef: + name: embargo-check + params: + - name: dataPath + value: data.json + - name: pipelineRunUid + value: $(context.pipelineRun.uid) + - name: taskGitUrl + value: "http://localhost" + - name: taskGitRevision + value: "main" + workspaces: + - name: data + workspace: tests-workspace + runAfter: + - setup + - name: check-result + workspaces: + - name: data + workspace: tests-workspace + taskSpec: + steps: + - name: check-result + image: quay.io/konflux-ci/release-service-utils:0f82be4be43294b6a96846d87ef7f7c0b9e34267 + script: | + #!/usr/bin/env bash + set -eux + + if [ "$(wc -l < "$(workspaces.data.path)"/mock_curl.txt)" != 3 ]; then + echo Error: curl was expected to be called 3 times. Actual calls: + cat "$(workspaces.data.path)"/mock_curl.txt + exit 1 + fi + + [[ $(head -n 1 "$(workspaces.data.path)/mock_curl.txt") == *"issues.redhat.com/rest/api/2/field" ]] + [[ $(head -n 2 "$(workspaces.data.path)/mock_curl.txt" | tail -n 1) \ + == *"ISSUE-123"* ]] + [[ $(tail -n 1 "$(workspaces.data.path)/mock_curl.txt") == *"FEATURE-123"* ]] + runAfter: + - run-task diff --git a/tasks/managed/embargo-check/tests/test-embargo-check-nonexistent-rh-issue.yaml b/tasks/managed/embargo-check/tests/test-embargo-check-nonexistent-rh-issue.yaml new file mode 100644 index 000000000..14f6c442b --- /dev/null +++ b/tasks/managed/embargo-check/tests/test-embargo-check-nonexistent-rh-issue.yaml @@ -0,0 +1,61 @@ +--- +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: test-embargo-check-nonexistent-rh-issue + annotations: + test/assert-task-failure: "run-task" +spec: + description: Test for embargo-check where a issues.redhat.com issue cannot be found + workspaces: + - name: tests-workspace + tasks: + - name: setup + workspaces: + - name: data + workspace: tests-workspace + taskSpec: + workspaces: + - name: data + steps: + - name: setup-values + image: quay.io/konflux-ci/release-service-utils:0f82be4be43294b6a96846d87ef7f7c0b9e34267 + script: | + #!/usr/bin/env sh + set -eux + + cat > "$(workspaces.data.path)/data.json" << EOF + { + "releaseNotes": { + "issues": { + "fixed": [ + { + "id": "MISSINGRH-123", + "source": "issues.redhat.com" + }, + { + "id": "12345", + "source": "bugzilla.redhat.com" + } + ] + } + } + } + EOF + - name: run-task + taskRef: + name: embargo-check + params: + - name: dataPath + value: data.json + - name: pipelineRunUid + value: $(context.pipelineRun.uid) + - name: taskGitUrl + value: "http://localhost" + - name: taskGitRevision + value: "main" + workspaces: + - name: data + workspace: tests-workspace + runAfter: + - setup diff --git a/tasks/managed/embargo-check/tests/test-embargo-check-public-issues.yaml b/tasks/managed/embargo-check/tests/test-embargo-check-public-issues.yaml index a180cb2cb..e31abe3ce 100644 --- a/tasks/managed/embargo-check/tests/test-embargo-check-public-issues.yaml +++ b/tasks/managed/embargo-check/tests/test-embargo-check-public-issues.yaml @@ -69,15 +69,15 @@ spec: #!/usr/bin/env bash set -eux - if [ "$(wc -l < "$(workspaces.data.path)"/mock_curl.txt)" != 2 ]; then - echo Error: curl was expected to be called 2 times. Actual calls: + if [ "$(wc -l < "$(workspaces.data.path)"/mock_curl.txt)" != 3 ]; then + echo Error: curl was expected to be called 3 times. Actual calls: cat "$(workspaces.data.path)"/mock_curl.txt exit 1 fi - [[ $(cat $(workspaces.data.path)/mock_curl.txt | head -n 1) \ + [[ $(head -n 1 "$(workspaces.data.path)/mock_curl.txt") == *"issues.redhat.com/rest/api/2/field" ]] + [[ $(head -n 2 "$(workspaces.data.path)/mock_curl.txt" | tail -n 1) \ == *"ISSUE-123"* ]] - [[ $(cat $(workspaces.data.path)/mock_curl.txt | head -n 2 | tail -n 1) \ - == *"12345"* ]] + [[ $(tail -n 1 "$(workspaces.data.path)/mock_curl.txt") == *"12345"* ]] runAfter: - run-task diff --git a/tasks/managed/embargo-check/tests/test-embargo-check-rh-issue-missing-cve.yaml b/tasks/managed/embargo-check/tests/test-embargo-check-rh-issue-missing-cve.yaml new file mode 100644 index 000000000..662ad8949 --- /dev/null +++ b/tasks/managed/embargo-check/tests/test-embargo-check-rh-issue-missing-cve.yaml @@ -0,0 +1,80 @@ +--- +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: test-embargo-check-rh-issue-missing-cve + annotations: + test/assert-task-failure: "run-task" +spec: + description: | + Test for embargo-check where a issues.redhat.com issue has a Downstream Component Name value + that matches a component in the releaseNotes.content.images section, but said images section + does not list CVE-123, which is what the mocks return + workspaces: + - name: tests-workspace + tasks: + - name: setup + workspaces: + - name: data + workspace: tests-workspace + taskSpec: + workspaces: + - name: data + steps: + - name: setup-values + image: quay.io/konflux-ci/release-service-utils:0f82be4be43294b6a96846d87ef7f7c0b9e34267 + script: | + #!/usr/bin/env sh + set -eux + + cat > "$(workspaces.data.path)/data.json" << EOF + { + "releaseNotes": { + "issues": { + "fixed": [ + { + "id": "CVE-123", + "source": "issues.redhat.com" + }, + { + "id": "12345", + "source": "bugzilla.redhat.com" + } + ] + }, + "content": { + "images": [ + { + "component": "my-component", + "architecture": "x86_64", + "containerImage": "registry.io/org/repo", + "cves": { + "fixed": { + "CVE-987": { + "packages": [] + } + } + } + } + ] + } + } + } + EOF + - name: run-task + taskRef: + name: embargo-check + params: + - name: dataPath + value: data.json + - name: pipelineRunUid + value: $(context.pipelineRun.uid) + - name: taskGitUrl + value: "http://localhost" + - name: taskGitRevision + value: "main" + workspaces: + - name: data + workspace: tests-workspace + runAfter: + - setup diff --git a/tasks/managed/embargo-check/tests/test-embargo-check-rh-issue-wrong-component.yaml b/tasks/managed/embargo-check/tests/test-embargo-check-rh-issue-wrong-component.yaml new file mode 100644 index 000000000..9f64e3fe8 --- /dev/null +++ b/tasks/managed/embargo-check/tests/test-embargo-check-rh-issue-wrong-component.yaml @@ -0,0 +1,73 @@ +--- +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: test-embargo-check-rh-issue-wrong-component + annotations: + test/assert-task-failure: "run-task" +spec: + description: | + Test for embargo-check where a issues.redhat.com issue has a Downstream Component Name value + that does not match any component in the releaseNotes.content.images section. The mocks set the + Downstream Component Name to my-component + workspaces: + - name: tests-workspace + tasks: + - name: setup + workspaces: + - name: data + workspace: tests-workspace + taskSpec: + workspaces: + - name: data + steps: + - name: setup-values + image: quay.io/konflux-ci/release-service-utils:0f82be4be43294b6a96846d87ef7f7c0b9e34267 + script: | + #!/usr/bin/env sh + set -eux + + cat > "$(workspaces.data.path)/data.json" << EOF + { + "releaseNotes": { + "issues": { + "fixed": [ + { + "id": "CVE-123", + "source": "issues.redhat.com" + }, + { + "id": "12345", + "source": "bugzilla.redhat.com" + } + ] + }, + "content": { + "images": [ + { + "component": "not-my-component", + "architecture": "x86_64", + "containerImage": "registry.io/org/repo" + } + ] + } + } + } + EOF + - name: run-task + taskRef: + name: embargo-check + params: + - name: dataPath + value: data.json + - name: pipelineRunUid + value: $(context.pipelineRun.uid) + - name: taskGitUrl + value: "http://localhost" + - name: taskGitRevision + value: "main" + workspaces: + - name: data + workspace: tests-workspace + runAfter: + - setup diff --git a/tasks/managed/embargo-check/tests/test-embargo-check-rh-issue.yaml b/tasks/managed/embargo-check/tests/test-embargo-check-rh-issue.yaml new file mode 100644 index 000000000..093a21991 --- /dev/null +++ b/tasks/managed/embargo-check/tests/test-embargo-check-rh-issue.yaml @@ -0,0 +1,102 @@ +--- +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: test-embargo-check-rh-issue +spec: + description: | + Test for embargo-check where a issues.redhat.com issue has a Downstream Component Name value + that matches a component in the releaseNotes.content.images section and said images section + lists CVE-123, which is what the mocks return. The task should succeed + workspaces: + - name: tests-workspace + tasks: + - name: setup + workspaces: + - name: data + workspace: tests-workspace + taskSpec: + workspaces: + - name: data + steps: + - name: setup-values + image: quay.io/konflux-ci/release-service-utils:0f82be4be43294b6a96846d87ef7f7c0b9e34267 + script: | + #!/usr/bin/env sh + set -eux + + cat > "$(workspaces.data.path)/data.json" << EOF + { + "releaseNotes": { + "issues": { + "fixed": [ + { + "id": "CVE-123", + "source": "issues.redhat.com" + }, + { + "id": "12345", + "source": "bugzilla.redhat.com" + } + ] + }, + "content": { + "images": [ + { + "component": "my-component", + "architecture": "x86_64", + "containerImage": "registry.io/org/repo", + "cves": { + "fixed": { + "CVE-123": { + "packages": [] + } + } + } + } + ] + } + } + } + EOF + - name: run-task + taskRef: + name: embargo-check + params: + - name: dataPath + value: data.json + - name: pipelineRunUid + value: $(context.pipelineRun.uid) + - name: taskGitUrl + value: "http://localhost" + - name: taskGitRevision + value: "main" + workspaces: + - name: data + workspace: tests-workspace + runAfter: + - setup + - name: check-result + workspaces: + - name: data + workspace: tests-workspace + taskSpec: + steps: + - name: check-result + image: quay.io/konflux-ci/release-service-utils:0f82be4be43294b6a96846d87ef7f7c0b9e34267 + script: | + #!/usr/bin/env bash + set -eux + + if [ "$(wc -l < "$(workspaces.data.path)"/mock_curl.txt)" != 3 ]; then + echo Error: curl was expected to be called 3 times. Actual calls: + cat "$(workspaces.data.path)"/mock_curl.txt + exit 1 + fi + + [[ $(head -n 1 "$(workspaces.data.path)/mock_curl.txt") == *"issues.redhat.com/rest/api/2/field" ]] + [[ $(head -n 2 "$(workspaces.data.path)/mock_curl.txt" | tail -n 1) \ + == *"CVE-123"* ]] + [[ $(tail -n 1 "$(workspaces.data.path)/mock_curl.txt") == *"12345"* ]] + runAfter: + - run-task