From b9c014f6e416c90b38e02376d60a0579d50cd262 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Tue, 31 Mar 2026 10:06:14 -0600 Subject: [PATCH 1/2] fix: host-runner compatibility for tests and scripts Fix tests and scripts to work on ubuntu-latest host runners (no container) in addition to CI container environments. - rate_limited_logger.go: add /home/runner/work/ path prefix (container used /__w/) - download_zip_test.go: accept ErrPermission for /root/ paths (non-root runner returns permission denied, not not-found) - detection_test.go: delete circular test - lib.sh: escape & in bash 5.2+ replacements (verified 4.4-5.3) - helpers.bash: add yq_multidoc helper (yq 4.x adds --- separators) - roxctl-netpol-generate-*.bats: use yq_multidoc All changes are backward-compatible with container environments. Co-Authored-By: Claude Opus 4.6 (1M context) --- pkg/containers/detection_test.go | 18 ----------- pkg/logging/rate_limited_logger.go | 3 +- .../common/zipdownload/download_zip_test.go | 7 +++-- scripts/ci/lib.sh | 16 ++++++---- tests/roxctl/bats-tests/helpers.bash | 8 +++++ .../roxctl-netpol-generate-development.bats | 10 +++--- .../local/roxctl-netpol-generate-release.bats | 31 +++---------------- 7 files changed, 35 insertions(+), 58 deletions(-) delete mode 100644 pkg/containers/detection_test.go diff --git a/pkg/containers/detection_test.go b/pkg/containers/detection_test.go deleted file mode 100644 index e268c63e2a9be..0000000000000 --- a/pkg/containers/detection_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package containers - -import ( - "os" - "testing" - - "github.com/stretchr/testify/assert" -) - -// Assert that container detection is running correctly by checking if it returns true in CI. Expected to return -// false when run locally. -func TestContainerDetection(t *testing.T) { - if _, ok := os.LookupEnv("GITHUB_ACTIONS"); ok { - assert.True(t, IsRunningInContainer()) - } else { - assert.False(t, IsRunningInContainer()) - } -} diff --git a/pkg/logging/rate_limited_logger.go b/pkg/logging/rate_limited_logger.go index 34edaf1639483..35b54bef10b51 100644 --- a/pkg/logging/rate_limited_logger.go +++ b/pkg/logging/rate_limited_logger.go @@ -163,10 +163,11 @@ const ( localFilePathPrefix = "github.com/stackrox/stackrox/" filePathPrefix = "github.com/stackrox/rox/" githubPathPrefix = "/__w/stackrox/stackrox/" + githubHostPrefix = "/home/runner/work/stackrox/stackrox/" ) func getTrimmedFilePath(path string) string { - prefixes := []string{filePathPrefix, localFilePathPrefix, githubPathPrefix} + prefixes := []string{filePathPrefix, localFilePathPrefix, githubPathPrefix, githubHostPrefix} for _, prefix := range prefixes { prefixToCut := strings.Index(path, prefix) if prefixToCut >= 0 { diff --git a/roxctl/common/zipdownload/download_zip_test.go b/roxctl/common/zipdownload/download_zip_test.go index 305004110c356..e2d91e3e1308e 100644 --- a/roxctl/common/zipdownload/download_zip_test.go +++ b/roxctl/common/zipdownload/download_zip_test.go @@ -3,6 +3,7 @@ package zipdownload import ( "archive/zip" "bytes" + "errors" "io/fs" "os" "path/filepath" @@ -208,7 +209,9 @@ func TestExtractZipToFolder_PreventPathTraversal(t *testing.T) { for _, path := range checkPaths { _, err := os.Stat(path) - // Expect "no such file or directory" - meaning the file wasn't created - assert.ErrorIs(t, err, fs.ErrNotExist, "Malicious file should not exist at %s", path) + // File must not exist. On non-root runners, paths under /root/ return + // ErrPermission instead of ErrNotExist — both confirm the file wasn't written. + assert.True(t, errors.Is(err, fs.ErrNotExist) || errors.Is(err, fs.ErrPermission), + "Malicious file should not exist at %s, got: %v", path, err) } } diff --git a/scripts/ci/lib.sh b/scripts/ci/lib.sh index 718391e1e5186..f0cc9bc3d8961 100755 --- a/scripts/ci/lib.sh +++ b/scripts/ci/lib.sh @@ -2335,12 +2335,16 @@ _EO_SUITE_HEADER_ local result="${lines[1]}" local details="${lines[2]}" - # XML escape description - description="${description//&/&}" - description="${description//\"/"}" - description="${description//\'/'}" - description="${description///>}" + # XML escape description. + # \& is required: bash 5.2+ treats & in ${var//pat/repl} as the + # matched text (like sed), so without \& the & is replaced by the + # match itself. \& works on all bash versions (4.4–5.3 verified). + # CI container had bash 5.1 (UBI9); ubuntu-latest has bash 5.2+. + description="${description//&/\&}" + description="${description//\"/\"}" + description="${description//\'/\'}" + description="${description///\>}" cat << _EO_CASE_HEADER_ >> "${junit_file}" diff --git a/tests/roxctl/bats-tests/helpers.bash b/tests/roxctl/bats-tests/helpers.bash index 1a3940871a4eb..6fcb40b28cec9 100644 --- a/tests/roxctl/bats-tests/helpers.bash +++ b/tests/roxctl/bats-tests/helpers.bash @@ -9,6 +9,14 @@ fi load "${bats_helpers_root}/bats-support/load.bash" load "${bats_helpers_root}/bats-assert/load.bash" +# yq_multidoc runs yq and strips --- document separators from output. +# yq 4.x adds separators between multi-doc results which shift assert_line indices. +yq_multidoc() { + local output + output=$(yq "$@") || return $? + grep -v '^---$' <<< "$output" +} + # luname outputs uname in lowercase luname() { uname | tr '[:upper:]' '[:lower:]' diff --git a/tests/roxctl/bats-tests/local/roxctl-netpol-generate-development.bats b/tests/roxctl/bats-tests/local/roxctl-netpol-generate-development.bats index 5cfebca4c184e..119bcd2942bea 100755 --- a/tests/roxctl/bats-tests/local/roxctl-netpol-generate-development.bats +++ b/tests/roxctl/bats-tests/local/roxctl-netpol-generate-development.bats @@ -60,7 +60,7 @@ teardown() { assert_line '2' # Ensure that all yaml docs are of kind 'NetworkPolicy' - run yq e '.kind | ({"match": ., "doc": di})' "${ofile}" + run yq_multidoc e '.kind | ({"match": ., "doc": di})' "${ofile}" assert_line --index 0 'match: NetworkPolicy' assert_line --index 1 'doc: 0' assert_line --index 2 'match: NetworkPolicy' @@ -69,7 +69,7 @@ teardown() { assert_line --index 5 'doc: 2' # Ensure that all NetworkPolicies have the generated-by-stackrox label - run yq e '.metadata.labels | ({"match": ."network-policy-buildtime-generator.stackrox.io/generated", "doc": di})' "${ofile}" + run yq_multidoc e '.metadata.labels | ({"match": ."network-policy-buildtime-generator.stackrox.io/generated", "doc": di})' "${ofile}" assert_line --index 0 'match: "true"' assert_line --index 1 'doc: 0' assert_line --index 2 'match: "true"' @@ -99,7 +99,7 @@ teardown() { assert_line '2' # Ensure that all yaml docs are of kind 'NetworkPolicy' - run yq e '.kind | ({"match": ., "doc": di})' "${ofile}" + run yq_multidoc e '.kind | ({"match": ., "doc": di})' "${ofile}" assert_line --index 0 'match: NetworkPolicy' assert_line --index 1 'doc: 0' assert_line --index 2 'match: NetworkPolicy' @@ -108,7 +108,7 @@ teardown() { assert_line --index 5 'doc: 2' # Ensure that dns ports are properly set - run yq e '.spec.egress[1].ports[0].port | ({"match": ., "doc": di})' "${ofile}" + run yq_multidoc e '.spec.egress[1].ports[0].port | ({"match": ., "doc": di})' "${ofile}" assert_line --index 0 'match: null' assert_line --index 1 'doc: 0' assert_line --index 2 'match: '${dns_port} @@ -131,7 +131,7 @@ teardown() { yaml_valid "$ofile" # Ensure that dns ports are properly set - run yq e '.spec.egress[1].ports[0].port | ({"match": ., "doc": di})' "${ofile}" + run yq_multidoc e '.spec.egress[1].ports[0].port | ({"match": ., "doc": di})' "${ofile}" assert_line --index 0 'match: null' assert_line --index 1 'doc: 0' assert_line --index 2 'match: '${dns_port} diff --git a/tests/roxctl/bats-tests/local/roxctl-netpol-generate-release.bats b/tests/roxctl/bats-tests/local/roxctl-netpol-generate-release.bats index f205ccd875e76..1bb9d38114262 100755 --- a/tests/roxctl/bats-tests/local/roxctl-netpol-generate-release.bats +++ b/tests/roxctl/bats-tests/local/roxctl-netpol-generate-release.bats @@ -61,8 +61,7 @@ teardown() { assert_line '2' # Ensure that all yaml docs are of kind 'NetworkPolicy' - run yq e '.kind | ({"match": ., "doc": di})' "${ofile}" - # Github actions run yq v3 + run yq_multidoc e '.kind | ({"match": ., "doc": di})' "${ofile}" assert_line --index 0 'match: NetworkPolicy' assert_line --index 1 'doc: 0' assert_line --index 2 'match: NetworkPolicy' @@ -70,34 +69,14 @@ teardown() { assert_line --index 4 'match: NetworkPolicy' assert_line --index 5 'doc: 2' - # yq v4 assertions - # assert_line --index 0 'match: NetworkPolicy' - # assert_line --index 1 'doc: 0' - # assert_line --index 2 '---' - # assert_line --index 3 'match: NetworkPolicy' - # assert_line --index 4 'doc: 1' - # assert_line --index 5 '---' - # assert_line --index 6 'match: NetworkPolicy' - # assert_line --index 7 'doc: 2' - # Ensure that all NetworkPolicies have the generated-by-stackrox label - run yq e '.metadata.labels | ({"match": ."network-policy-buildtime-generator.stackrox.io/generated", "doc": di})' "${ofile}" + run yq_multidoc e '.metadata.labels | ({"match": ."network-policy-buildtime-generator.stackrox.io/generated", "doc": di})' "${ofile}" assert_line --index 0 'match: "true"' assert_line --index 1 'doc: 0' assert_line --index 2 'match: "true"' assert_line --index 3 'doc: 1' assert_line --index 4 'match: "true"' assert_line --index 5 'doc: 2' - - # yq v4 assertions - # assert_line --index 0 'match: "true"' - # assert_line --index 1 'doc: 0' - # assert_line --index 2 '---' - # assert_line --index 3 'match: "true"' - # assert_line --index 4 'doc: 1' - # assert_line --index 5 '---' - # assert_line --index 6 'match: "true"' - # assert_line --index 7 'doc: 2' } @test "roxctl-release netpol generate generates network policies with custom dns port" { @@ -121,7 +100,7 @@ teardown() { assert_line '2' # Ensure that all yaml docs are of kind 'NetworkPolicy' - run yq e '.kind | ({"match": ., "doc": di})' "${ofile}" + run yq_multidoc e '.kind | ({"match": ., "doc": di})' "${ofile}" assert_line --index 0 'match: NetworkPolicy' assert_line --index 1 'doc: 0' assert_line --index 2 'match: NetworkPolicy' @@ -130,7 +109,7 @@ teardown() { assert_line --index 5 'doc: 2' # Ensure that dns ports are properly set - run yq e '.spec.egress[1].ports[0].port | ({"match": ., "doc": di})' "${ofile}" + run yq_multidoc e '.spec.egress[1].ports[0].port | ({"match": ., "doc": di})' "${ofile}" assert_line --index 0 'match: null' assert_line --index 1 'doc: 0' assert_line --index 2 'match: '${dns_port} @@ -153,7 +132,7 @@ teardown() { yaml_valid "$ofile" # Ensure that dns ports are properly set - run yq e '.spec.egress[1].ports[0].port | ({"match": ., "doc": di})' "${ofile}" + run yq_multidoc e '.spec.egress[1].ports[0].port | ({"match": ., "doc": di})' "${ofile}" assert_line --index 0 'match: null' assert_line --index 1 'doc: 0' assert_line --index 2 'match: '${dns_port} From 58ea4834ab9582f09ee92d722eee3fbeb2af7b29 Mon Sep 17 00:00:00 2001 From: davdhacs <105243888+davdhacs@users.noreply.github.com> Date: Wed, 1 Apr 2026 07:32:12 -0600 Subject: [PATCH 2/2] fix: use sed instead of grep -v in yq_multidoc grep -v returns exit code 1 when no lines match (e.g. yq output is only --- separators), which would look like a yq failure. sed always returns 0 regardless of whether lines were deleted. Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/roxctl/bats-tests/helpers.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/roxctl/bats-tests/helpers.bash b/tests/roxctl/bats-tests/helpers.bash index 6fcb40b28cec9..cce2ab0d31c7c 100644 --- a/tests/roxctl/bats-tests/helpers.bash +++ b/tests/roxctl/bats-tests/helpers.bash @@ -14,7 +14,7 @@ load "${bats_helpers_root}/bats-assert/load.bash" yq_multidoc() { local output output=$(yq "$@") || return $? - grep -v '^---$' <<< "$output" + sed '/^---$/d' <<< "$output" } # luname outputs uname in lowercase