Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
f58a6e3
fix(acs-ci): use absolute gcloud path
robbycochran Sep 17, 2026
235a584
fix(acs-ci): pass provider token to gcloud
robbycochran Sep 17, 2026
78b76ab
fix(acs-ci): avoid workflow interpolation in token check
robbycochran Sep 17, 2026
5cc6dc4
docs(acs-ci): describe bounded GCS discovery
robbycochran Sep 17, 2026
0d494ef
feat(acs-ci): configure triage lookback window
robbycochran Sep 17, 2026
5bc1a4f
test(acs-ci): probe nightly GCS objects in preflight
robbycochran Sep 17, 2026
7fb8943
fix(acs-ci): avoid workflow interpolation in probe
robbycochran Sep 17, 2026
4e03525
test(acs-ci): probe GCS objects with JSON API
robbycochran Sep 17, 2026
90482c4
fix(acs-ci): read nightly build results from GCS
robbycochran Sep 17, 2026
627bbdb
fix(acs-ci): use direct GCS object endpoint
robbycochran Sep 17, 2026
0afa08e
cleanup(acs-ci): remove temporary object probe
robbycochran Sep 17, 2026
ec68895
fix(acs-ci): remove obsolete write permission
robbycochran Sep 17, 2026
54d0593
fix(acs-ci): configure gcloud storage auth once
robbycochran Sep 17, 2026
373442e
chore(acs-ci): keep default triage window
robbycochran Sep 17, 2026
1b8efb6
docs(acs-ci): use gcloud for nightly discovery
robbycochran Sep 17, 2026
09fd43b
docs(acs-ci): document bounded job discovery
robbycochran Sep 17, 2026
e3ea815
fix(acs-ci): add trusted gsutil compatibility wrapper
robbycochran Sep 17, 2026
e6de088
fix(acs-ci): export gsutil shim to agent shells
robbycochran Sep 17, 2026
c9621f8
fix(acs-ci): resolve latest build in GCS wrapper
robbycochran Sep 17, 2026
6838280
fix(acs-ci): precompute authoritative failure manifest
robbycochran Sep 18, 2026
a814c38
fix(acs-ci): avoid payload variable interpolation
robbycochran Sep 18, 2026
14af438
fix(acs-ci): handle first GCS listing page
robbycochran Sep 18, 2026
4d62c9b
fix(acs-ci): build manifest from public GCS API
robbycochran Sep 18, 2026
18c19a5
chore(acs-ci): capture manifest diagnostics
robbycochran Sep 18, 2026
9ce1bd6
fix(acs-ci): stage manifest diagnostics under sandbox
robbycochran Sep 18, 2026
09d8405
fix(acs-ci): select latest completed build
robbycochran Sep 18, 2026
56a997d
fix(acs-ci): use anonymous access for public GCS manifest
robbycochran Sep 18, 2026
c76497d
debug(acs-ci): record GCS list response shape
robbycochran Sep 18, 2026
b7abece
debug(acs-ci): inspect nested GCS listings
robbycochran Sep 18, 2026
778f1f8
fix(acs-ci): use GCS media download endpoint
robbycochran Sep 18, 2026
305bc28
fix(acs-ci): read GCS results from public object URLs
robbycochran Sep 18, 2026
340b128
fix(acs-ci): read results with gcloud storage
robbycochran Sep 18, 2026
0caa1c9
debug(acs-ci): bound GCS result reads
robbycochran Sep 18, 2026
bd166d7
fix(acs-ci): mark manifest rows as failures
robbycochran Sep 18, 2026
a900cf1
chore(acs-ci): remove temporary manifest diagnostics
robbycochran Sep 18, 2026
738941d
fix(acs-ci): read GCS artifacts through public API
robbycochran Sep 18, 2026
6e06f9c
fix(acs-ci): harden GCS compatibility reads
robbycochran Sep 18, 2026
c9c7ee1
fix(acs-ci): avoid workflow interpolation in preflight check
robbycochran Sep 18, 2026
0e06c99
fix(acs-ci): validate GCS wrapper paths
robbycochran Sep 18, 2026
e9f5abf
chore(acs-ci): remove preflight workflow
robbycochran Sep 18, 2026
996837b
fix(acs-ci): harden wrapper path validation
robbycochran Sep 18, 2026
6e225a2
Merge remote-tracking branch 'origin/main' into codex/acs-gcloud-abso…
robbycochran Sep 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion images/stackrox/sandbox-stackrox-ci/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
"Bash(timeout *)",
"Bash(while *)",
"Read",
"Write(artifacts/*)",
"Edit(artifacts/*)",
"Glob",
"Grep",
Expand Down
3 changes: 0 additions & 3 deletions tasks/acs-ci-nightly/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ validated.
- Output: `/sandbox/acs-triage-agent/artifacts/ci-triage.json`, downloaded to
the caller's output directory. The output is optional so partial diagnostics
can still be retained when analysis fails.
- Preflight: the trusted caller may run `workflow/preflight.yaml` in the same
workspace before the main task. It verifies read-only GCS and Jira access
from inside the sandbox without exposing response bodies or credentials.
- Cleanup: the sandbox and host-side source staging are removed after outputs
are downloaded. Downloaded artifacts and any external reads remain with the
caller.
Expand Down
6 changes: 6 additions & 0 deletions tasks/acs-ci-nightly/openshell/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ points Google Cloud CLI tools at OpenShell's combined CA bundle so the
sandbox verifies the proxy certificate without disabling TLS. The image keeps
standalone `gsutil` only as a temporary compatibility fallback.

The task payload sets `BASH_ENV` to a runtime-only shell fragment that maps
the provider-injected `GCP_SA_ACCESS_TOKEN` to gcloud's
`CLOUDSDK_AUTH_ACCESS_TOKEN`. The token value is never interpolated into the
workflow or payload, and gcloud therefore uses the OpenShell-managed
short-lived provider token rather than a local account or service-account key.

The `github_git` policy is intentionally unauthenticated and read-only because
the StackRox repositories used by this task are public. The Atlassian and Prow
GCS provider instances remain gateway-owned; repository source being public
Expand Down
9 changes: 7 additions & 2 deletions tasks/acs-ci-nightly/workflow/CI-NIGHTLY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ Run only the CI failure analysis portion of the ACS triage agent.
repositories only when needed for the analysis. Use unauthenticated HTTPS
`git clone` or `git fetch` for these public repositories; do not run
`gh auth login` or push to them.
4. Find failures from the last 24 hours in the Prow nightly jobs under
`gs://${GCS_BUCKET:-test-platform-results}/logs/` using `gcloud storage`.
4. Follow the repository's existing `ci-coordinator.md` instructions for the
Prow failure lookup. The trusted task provides a bounded `gsutil` compatibility
command backed by the configured bucket; do not replace it with a recursive
bucket or wildcard listing. Treat
`/tmp/openshell-nightly-failures.tsv` as authoritative; every data row is a
verified `FAILURE` and must be analyzed as such. Do not invent job, build,
or GCS path values.
5. Spawn the repository's CI analysis agents as instructed and wait for their
results.
6. Write exactly `/sandbox/acs-triage-agent/artifacts/ci-triage.json` using
Expand Down
218 changes: 218 additions & 0 deletions tasks/acs-ci-nightly/workflow/gsutil
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
#!/usr/bin/env bash
set -euo pipefail

# Compatibility shim for the existing ACS triage instructions. The public
# bucket is read through bounded GCS JSON/object requests because the image's
# gcloud object reads do not work reliably through the OpenShell proxy.
configured_bucket="${GCS_BUCKET:-test-platform-results-public}"

rewrite_uri() {
local uri=$1
local lower_uri
lower_uri=$(printf '%s' "$uri" | tr '[:upper:]' '[:lower:]')
case "$uri" in
gs://test-platform-results|gs://test-platform-results/*)
uri="gs://${configured_bucket}${uri#gs://test-platform-results}"
;;
"gs://${configured_bucket}"|"gs://${configured_bucket}"/*) ;;
*)
echo "gsutil compatibility wrapper: unsupported bucket in $uri" >&2
return 2
;;
esac

case "$lower_uri" in
*/../*|*/./*|*/..|*/.|*%2e*)
echo "gsutil compatibility wrapper: unsupported path in $uri" >&2
return 2
;;
esac
printf '%s\n' "$uri"
}

list_nightly_jobs() {
local api="https://storage.googleapis.com/storage/v1/b/${configured_bucket}/o"
local page_token=""

while :; do
local response
local -a query_args=(
--data-urlencode "prefix=logs/branch-ci-stackrox-stackrox-nightlies-"
--data-urlencode "delimiter=/"
--data-urlencode "maxResults=1000"
)
if [[ -n "$page_token" ]]; then
query_args+=(--data-urlencode "pageToken=$page_token")
fi

local -a curl_args=(
--silent --show-error --fail
--connect-timeout 10 --max-time 60
--get
"${query_args[@]}"
"$api"
)
response=$(timeout 90s curl "${curl_args[@]}")

printf '%s' "$response" \
| jq -r --arg bucket "$configured_bucket" \
'.prefixes[]? | "gs://" + $bucket + "/" + .'

page_token=$(printf '%s' "$response" | jq -r '.nextPageToken // empty')
[[ -z "$page_token" ]] && break
done
}

list_builds() {
local job=$1
local api="https://storage.googleapis.com/storage/v1/b/${configured_bucket}/o"
local object_prefix="${job#gs://${configured_bucket}/}"
local page_token=""

while :; do
local response
local -a query_args=(
--data-urlencode "prefix=$object_prefix"
--data-urlencode "delimiter=/"
--data-urlencode "maxResults=1000"
)
if [[ -n "$page_token" ]]; then
query_args+=(--data-urlencode "pageToken=$page_token")
fi

local -a curl_args=(
--silent --show-error --fail
--connect-timeout 10 --max-time 60
--get
"${query_args[@]}"
"$api"
)
response=$(timeout 90s curl "${curl_args[@]}")
printf '%s' "$response" \
| jq -r '.prefixes[]? | rtrimstr("/") | split("/") | .[-1] | select(test("^[0-9]+$"))'

page_token=$(printf '%s' "$response" | jq -r '.nextPageToken // empty')
Comment thread
robbycochran marked this conversation as resolved.
Comment thread
robbycochran marked this conversation as resolved.
Comment thread
robbycochran marked this conversation as resolved.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path traversal check at line 93 is incomplete. It does not handle URL-encoded path separators like %2F. A malicious actor could craft a URI with encoded slashes to bypass this check and access arbitrary paths in the GCS bucket. For example: gs://bucket/logs/..%2f..%2fsecret.

[[ -z "$page_token" ]] && break
done
}

list_objects() {
local uri=$1
local api="https://storage.googleapis.com/storage/v1/b/${configured_bucket}/o"
local object_prefix="${uri#gs://${configured_bucket}/}"
local -a curl_args=(
--silent --show-error --fail
--connect-timeout 10 --max-time 60
--get
--data-urlencode "prefix=$object_prefix"
--data-urlencode "delimiter=/"
--data-urlencode "maxResults=1000"
"$api"
)
local response
response=$(timeout 90s curl "${curl_args[@]}")
printf '%s' "$response" \
| jq -r --arg bucket "$configured_bucket" \
'(.items[]?.name, .prefixes[]?) | "gs://" + $bucket + "/" + .'
}

read_object() {
local uri=$1
local object_path="${uri#gs://${configured_bucket}/}"
timeout 30s curl --silent --show-error --fail \
--connect-timeout 10 --max-time 15 \
"https://storage.googleapis.com/${configured_bucket}/${object_path}"
}

read_finished() {
local gcs_path=$1
read_object "${gcs_path%/}/finished.json"
}

collect_failures() {
local cutoff
local jobs
local job
local job_prefix
local finished
local build
local gcs_path
local timestamp
local result
local candidates
cutoff=$(date -u -d '24 hours ago' +%s)
jobs=$(list_nightly_jobs)
printf '# Every data row below is a verified CI failure.\n'
printf '# job\tbuild\tgcs_path\ttimestamp\tresult\n'

while IFS= read -r job; do
[[ -z "$job" ]] && continue
job_prefix="${job%/}/"
finished=""
build=""
if ! candidates=$(list_builds "$job"); then
echo "gsutil compatibility wrapper: failed to list builds for $job" >&2
return 1
fi
while IFS= read -r candidate; do
[[ -z "$candidate" ]] && continue
candidate_path="${job_prefix}${candidate}"
if candidate_finished=$(read_finished "$candidate_path" 2>/dev/null); then
build="$candidate"
gcs_path="$candidate_path"
finished="$candidate_finished"
break
fi
done < <(printf '%s\n' "$candidates" | sort -nr)
[[ -n "$build" && -n "$finished" ]] || continue
timestamp=$(printf '%s' "$finished" | jq -r '.timestamp // 0')
result=$(printf '%s' "$finished" | jq -r '.result // ""')
if [[ "$timestamp" -ge "$cutoff" && "$result" == FAILURE ]]; then
printf '%s\t%s\t%s\t%s\tFAILURE\n' \
"${job#gs://${configured_bucket}/}" "$build" "$gcs_path" "$timestamp"
fi
done <<<"$jobs"
}

command=${1:-}
Comment thread
robbycochran marked this conversation as resolved.
shift || true

case "$command" in
Comment thread
robbycochran marked this conversation as resolved.
collect)
collect_failures
;;
ls)
if [[ $# -ne 1 ]]; then
echo "gsutil compatibility wrapper supports: gsutil ls URI" >&2
exit 2
fi
uri=$(rewrite_uri "$1")
Comment thread
robbycochran marked this conversation as resolved.
if [[ "$uri" == "gs://${configured_bucket}/logs/" ]]; then
list_nightly_jobs
else
Comment thread
robbycochran marked this conversation as resolved.
list_objects "$uri"
fi
;;
Comment thread
robbycochran marked this conversation as resolved.
cat)
if [[ $# -ne 1 ]]; then
echo "gsutil compatibility wrapper supports: gsutil cat URI" >&2
exit 2
fi
uri=$(rewrite_uri "$1")
if [[ "$uri" == gs://*/logs/*/latest-build.txt ]]; then
job_prefix="${uri%/latest-build.txt}/"
build=$(list_builds "$job_prefix" | sort -n | tail -n 1)
if [[ -z "$build" ]]; then
echo "gsutil compatibility wrapper: no numeric build for $job_prefix" >&2
exit 1
fi
printf '%s\n' "$build"
exit 0
Comment thread
robbycochran marked this conversation as resolved.
Comment thread
robbycochran marked this conversation as resolved.
fi
read_object "$uri"
;;
*)
echo "gsutil compatibility wrapper supports only ls and cat" >&2
exit 2
;;
esac
21 changes: 21 additions & 0 deletions tasks/acs-ci-nightly/workflow/harness.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ sandbox:
READ_ONLY_MODE: "true"
TRIAGE_RUN_URL: ${TRIAGE_RUN_URL}
GCS_BUCKET: test-platform-results-public
BASH_ENV: /sandbox/.bash_env
CLOUDSDK_CORE_CUSTOM_CA_CERTS_FILE: /etc/openshell-tls/ca-bundle.pem
CURL_CA_BUNDLE: /etc/openshell-tls/ca-bundle.pem

Expand All @@ -33,6 +34,26 @@ source:
payloads:
- source: CI-NIGHTLY.md
destination: /sandbox/CI-NIGHTLY.md
- source: gsutil
destination: /sandbox/tools/gsutil
- content: |
# OpenShell injects the short-lived provider token; gcloud needs this
# runtime-only compatibility mapping.
export PATH="/sandbox/tools:$PATH"
gsutil() { /bin/bash /sandbox/tools/gsutil "$@"; }
export -f gsutil
if [ -n "$GCP_SA_ACCESS_TOKEN" ]; then
export CLOUDSDK_AUTH_ACCESS_TOKEN="$GCP_SA_ACCESS_TOKEN"
Comment thread
robbycochran marked this conversation as resolved.
fi
if ! printenv OPENSHELL_GCS_MANIFEST_STARTED >/dev/null 2>&1; then
export OPENSHELL_GCS_MANIFEST_STARTED=1
/bin/bash /sandbox/tools/gsutil collect \
>/tmp/openshell-nightly-failures.tsv \
2>/tmp/openshell-nightly-failures.err || {
echo "collection failed; see /tmp/openshell-nightly-failures.err" >&2
}
fi
destination: /sandbox/.bash_env

agent:
type: claude
Expand Down
70 changes: 0 additions & 70 deletions tasks/acs-ci-nightly/workflow/preflight.yaml

This file was deleted.

Loading