Skip to content

Commit f166d44

Browse files
authored
fix: scrub secret values from CI log artifacts before upload (#488)
## Summary - Add a defense-in-depth scrub step to the CI workflow that replaces actual secret env-var values with `***REDACTED***` in all log files before the `upload-artifact` step - Add a `check-secret-scrub` pre-commit hook that ensures the scrub list stays in sync with `smoke.sh` — greps for secret-looking env vars (`KEY`, `TOKEN`, `PASSWORD`, `SECRET`, `CREDENTIAL`) passed to the container and fails if any are missing from the workflow's scrub list ## Test plan - [x] `pre-commit run check-secret-scrub --all-files` passes - [x] Temporarily removing a var from the scrub list causes the hook to fail with a clear error message - [ ] CI run uploads log artifacts with redacted values 🤖 Generated with [Claude Code](https://claude.com/claude-code) ## Summary by CodeRabbit * **New Features** * Added automatic redaction of sensitive values from generated log and test-result artifacts before they’re uploaded. * Introduced CI secret-scrubbing steps across multiple response-test workflows. * Added a pre-commit hook to validate consistency between local smoke-test secret inputs and CI scrubbing configuration. * **Bug Fixes** * Reduced the risk of accidentally exposing tokens, passwords, credentials, and related secret values in uploaded artifacts. Approved-by: Artemon-line Approved-by: cdoern
2 parents 5950380 + dfd9265 commit f166d44

7 files changed

Lines changed: 128 additions & 0 deletions

.github/workflows/redhat-distro-container.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,13 @@ jobs:
302302
done
303303
fi
304304
305+
# Scrub secret values from log files before upload (CWE-532)
306+
./tests/scrub_secrets.sh 'logs/*.log' \
307+
VLLM_API_TOKEN VLLM_EMBEDDING_API_TOKEN \
308+
OPENAI_API_KEY GEMINI_API_KEY ANTHROPIC_API_KEY \
309+
POSTGRES_PASSWORD PGVECTOR_PASSWORD \
310+
GOOGLE_APPLICATION_CREDENTIALS
311+
305312
- name: Upload logs as artifacts
306313
if: always()
307314
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1

.github/workflows/responses-openai.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,15 @@ jobs:
142142
reporter: java-junit
143143
fail-on-error: 'false'
144144

145+
- name: Scrub secrets from test results
146+
if: ${{ !cancelled() }}
147+
env:
148+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
149+
TAVILY_SEARCH_API_KEY: ${{ secrets.TAVILY_SEARCH_API_KEY }}
150+
run: |
151+
./tests/scrub_secrets.sh '/tmp/test-results/*' \
152+
OPENAI_API_KEY TAVILY_SEARCH_API_KEY
153+
145154
- name: Upload artifacts
146155
if: ${{ !cancelled() }}
147156
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1

.github/workflows/responses-vertexai.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,15 @@ jobs:
158158
reporter: java-junit
159159
fail-on-error: 'false'
160160

161+
- name: Scrub secrets from test results
162+
if: ${{ !cancelled() }}
163+
env:
164+
VERTEX_AI_PROJECT: ${{ secrets.VERTEX_AI_PROJECT }}
165+
TAVILY_SEARCH_API_KEY: ${{ secrets.TAVILY_SEARCH_API_KEY }}
166+
run: |
167+
./tests/scrub_secrets.sh '/tmp/test-results/*' \
168+
VERTEX_AI_PROJECT TAVILY_SEARCH_API_KEY
169+
161170
- name: Upload artifacts
162171
if: ${{ !cancelled() }}
163172
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1

.github/workflows/responses-vllm-maas.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,16 @@ jobs:
146146
reporter: java-junit
147147
fail-on-error: 'false'
148148

149+
- name: Scrub secrets from test results
150+
if: ${{ !cancelled() }}
151+
env:
152+
VLLM_API_TOKEN: ${{ secrets.MAAS_VLLM_API_TOKEN }}
153+
VLLM_EMBEDDING_API_TOKEN: ${{ secrets.MAAS_EMBEDDING_API_TOKEN }}
154+
TAVILY_SEARCH_API_KEY: ${{ secrets.TAVILY_SEARCH_API_KEY }}
155+
run: |
156+
./tests/scrub_secrets.sh '/tmp/test-results/*' \
157+
VLLM_API_TOKEN VLLM_EMBEDDING_API_TOKEN TAVILY_SEARCH_API_KEY
158+
149159
- name: Upload artifacts
150160
if: ${{ !cancelled() }}
151161
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1

.pre-commit-config.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,10 @@ repos:
6161
files: ^(build/(build\.yaml|build\.env)|distribution/config\.yaml)$
6262
additional_dependencies:
6363
- pyyaml==6.0.2
64+
65+
- id: check-secret-scrub
66+
name: CI log-scrub list matches smoke.sh secrets
67+
entry: ./tests/check_secret_scrub_list.sh
68+
language: script
69+
pass_filenames: false
70+
files: ^(tests/smoke\.sh|\.github/workflows/redhat-distro-container\.yml)$

tests/check_secret_scrub_list.sh

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/bin/bash
2+
# Ensure every secret-looking env var passed to the OGX container in smoke.sh
3+
# is listed in the CI workflow's log-scrub step. Exits non-zero on drift.
4+
5+
set -euo pipefail
6+
7+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
8+
SMOKE="$REPO_ROOT/tests/smoke.sh"
9+
WORKFLOW="$REPO_ROOT/.github/workflows/redhat-distro-container.yml"
10+
11+
# Extract env var names passed via --env to the docker container in smoke.sh
12+
smoke_vars=$(grep -oP '(?<=--env ")([A-Z_]+)(?==)' "$SMOKE" | sort -u)
13+
14+
# Filter to secret-looking names
15+
secret_pattern='KEY|TOKEN|PASSWORD|SECRET|CREDENTIAL'
16+
smoke_secrets=$(echo "$smoke_vars" | grep -E "$secret_pattern" || true)
17+
18+
if [ -z "$smoke_secrets" ]; then
19+
echo "No secret-looking env vars found in smoke.sh (unexpected)"
20+
exit 1
21+
fi
22+
23+
# Extract the var names from the workflow's scrub_secrets.sh invocation
24+
scrub_vars=$(grep -A10 'scrub_secrets\.sh' "$WORKFLOW" \
25+
| grep -oP '\b[A-Z][A-Z_]{2,}\b' \
26+
| grep -E "$secret_pattern" \
27+
| sort -u)
28+
29+
missing=()
30+
while IFS= read -r var; do
31+
if ! echo "$scrub_vars" | grep -qx "$var"; then
32+
missing+=("$var")
33+
fi
34+
done <<< "$smoke_secrets"
35+
36+
if [ ${#missing[@]} -gt 0 ]; then
37+
echo "ERROR: Secret env var(s) passed to the OGX container in smoke.sh"
38+
echo "are missing from the CI log-scrub list in redhat-distro-container.yml:"
39+
for v in "${missing[@]}"; do
40+
echo " - $v"
41+
done
42+
echo ""
43+
echo "Add them to the Python scrub snippet in the 'Gather logs' step."
44+
exit 1
45+
fi
46+
47+
echo "All secret env vars in smoke.sh are in the CI log-scrub list."

tests/scrub_secrets.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/bin/bash
2+
# Scrub secret values from files before artifact upload (CWE-532).
3+
#
4+
# Usage: scrub_secrets.sh <glob> <ENV_VAR_NAME> [ENV_VAR_NAME ...]
5+
#
6+
# Each ENV_VAR_NAME is read from the environment. Values shorter than
7+
# 4 characters are silently skipped to avoid false-positive replacements.
8+
#
9+
# Example:
10+
# scrub_secrets.sh 'logs/*.log' VLLM_API_TOKEN OPENAI_API_KEY
11+
12+
set -euo pipefail
13+
14+
if [ $# -lt 2 ]; then
15+
echo "Usage: $0 <glob> <ENV_VAR_NAME> [ENV_VAR_NAME ...]" >&2
16+
exit 1
17+
fi
18+
19+
glob_pattern="$1"
20+
shift
21+
22+
python3 -c "
23+
import os, glob, sys
24+
25+
var_names = sys.argv[1:]
26+
secrets = [os.environ.get(v, '') for v in var_names]
27+
secrets = [s for s in secrets if len(s) >= 4]
28+
29+
for f in glob.glob(sys.argv[0]):
30+
try:
31+
with open(f, 'r', errors='replace') as fh:
32+
content = fh.read()
33+
for s in secrets:
34+
content = content.replace(s, '***REDACTED***')
35+
with open(f, 'w') as fh:
36+
fh.write(content)
37+
except IsADirectoryError:
38+
pass
39+
" "$glob_pattern" "$@"

0 commit comments

Comments
 (0)