refactor: move severity threshold default to harness env - #573
Conversation
Move the REVIEW_FINDING_SEVERITY_THRESHOLD default ("low") out of
agents/review.md and scripts/post-review.sh into harness/review.yaml
under env.runner and env.sandbox. This makes the review agent
consistent with the configuration pattern recommended in the feature
configuration checklist (PR #566).
The post-review script now fails hard on an invalid value instead of
silently falling back, since the harness guarantees a valid default is
always present.
Assisted-by: Claude claude-opus-4-6 <noreply@anthropic.com>
Signed-off-by: Ralph Bean <rbean@redhat.com>
PR Summary by QodoMove severity threshold default to harness env and fail fast on invalid values
AI Description
Diagram
High-Level Assessment
Files changed (5)
|
|
🤖 Finished Review · ✅ Success · Started 6:34 PM UTC · Completed 6:48 PM UTC |
Code Review by Qodo
Context used✅ Compliance rules (platform):
55 rules 1. Protected paths modified
|
ReviewFindingsMedium
Low
Previous runReviewFindingsMedium
Low
Previous run (2)ReviewFindingsMedium
Low
Previous run (3)ReviewFindingsMedium
Low
Previous run (4)ReviewFindingsMedium
Low
Previous run (5)ReviewFindingsMedium
Low
Labels: PR modifies review agent infrastructure (agents/review.md, harness/review.yaml, scripts/post-review.sh). Previous run (6)ReviewFindingsMedium
Low
|
Make REVIEW_FINDING_SEVERITY_THRESHOLD expansion nounset-safe so that an unset variable falls through to the case `*` branch with a descriptive error instead of crashing on `set -u`. Also sanitize the value before interpolating into the `::error::` workflow command to prevent command injection via newlines or `::` sequences. Assisted-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
|
🤖 Finished Review · ✅ Success · Started 7:00 PM UTC · Completed 7:16 PM UTC |
Move env: block above timeout_minutes to match the key ordering convention established in fix.yaml and scribe.yaml. Signed-off-by: Ralph Bean <rbean@redhat.com> Assisted-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
|
🤖 Finished Review · ✅ Success · Started 7:24 PM UTC · Completed 7:38 PM UTC |
waynesun09
left a comment
There was a problem hiding this comment.
Automated review pass — 5 findings below (1 critical, 1 high, 3 medium). None overlap with the existing qodo/fullsend-ai-review bot findings already addressed on this PR (injection sanitization, unset-variable crash, env-block ordering).
- Add REVIEW_FINDING_SEVERITY_THRESHOLD to required env vars header in post-review.sh - Migrate forge.github.runner_env to forge.github.env.runner in harness/review.yaml to match fix.yaml/scribe.yaml convention - Post synthetic failure result to PR on invalid threshold (consistent with missing-RESULT_FILE path) - Update docs/review.md to describe harness base override pattern instead of CI workflow env block - Document REVIEW_FINDING_SEVERITY_THRESHOLD as required in agents/review.md with note for --print/pre-push callers Signed-off-by: Ralph Bean <rbean@redhat.com> Assisted-by: Claude claude-opus-4-6 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
|
🤖 Finished Review · ✅ Success · Started 8:50 PM UTC · Completed 9:09 PM UTC |
waynesun09
left a comment
There was a problem hiding this comment.
Automated review pass — 2 findings below (1 high, 1 medium). Skipped 1 duplicate (missing test coverage for the invalid-threshold hard-failure path was already flagged by fullsend-ai-review's earlier review comment on this PR).
…erity sanitization The single-pass "::" -> ":" replace was not idempotent — an input like ":::error:::injected" collapsed to "::error::injected" in one pass, reviving a live GHA workflow-command delimiter. Strip all colons instead of collapsing pairs, and also strip URL-encoded newline variants (%0A/%0D, case-insensitive) which GHA interprets the same as raw newlines in workflow command parameters. Also note in harness/review.yaml that the runner/sandbox copies of REVIEW_FINDING_SEVERITY_THRESHOLD should be kept in sync, even though nothing enforces it. Assisted-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
|
🤖 Finished Review · ✅ Success · Started 2:16 PM UTC · Completed 2:30 PM UTC |
The %0A/%0D sanitizer stripped fixed-width literal tokens in a single pass per case, same non-idempotency class as the earlier ':::error:::' colon bug. An input like '%0%0aA' survives the case-specific passes and reassembles into a live '%0A' once the inner '%0a' is removed, which GHA decodes as a literal newline in the ::error:: annotation. Strip every '%' character outright instead, mirroring the colon fix. Also switch the invalid-threshold failure path's reason code from missing-context to tool-failure — missing-context means the agent lacked context to review the PR, not that an env var was misconfigured, and per agents/review.md this string renders verbatim in the user-facing failure comment. Assisted-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
|
🤖 Finished Review · ✅ Success · Started 6:27 PM UTC · Completed 6:42 PM UTC |
waynesun09
left a comment
There was a problem hiding this comment.
Automated review pass — 3 findings (1 high, 2 medium). None overlap with existing qodo/fullsend-ai-review bot findings or prior human review threads on this PR (all prior discussion was scoped to REVIEW_FINDING_SEVERITY_THRESHOLD sanitization/validation, not the pre-existing label_actions code path or the env/review.env host-file interaction).
Two of the findings below reference lines outside this PR's diff hunks, so GitHub won't accept them as inline comments — included here in the body instead.
[HIGH] scripts/post-review.sh:303 — label_actions sanitizer (LA_LABEL) still uses the same non-idempotent :: collapse the PR's own tests prove is bypassable
This PR replaced the single-pass ${var//::/:} collapse for REVIEW_FINDING_SEVERITY_THRESHOLD with a full character-strip specifically because the collapse is non-idempotent and bypassable (proven live: s=':::error:::injected'; s="${s//::/:}" yields ::error::injected, a fully-formed GHA workflow-command delimiter). A few lines later in the very same file, the pre-existing label-action handling still uses the exact vulnerable pattern: LA_LABEL="${LA_LABEL//::/:}" (line 303, alongside LA_ACTION at line 300 — though LA_ACTION is schema-enum-constrained to add/remove and not exploitable).
LA_LABEL comes from the agent's JSON output (label_actions.actions[].label), whose schema pattern ^[a-zA-Z0-9._/: +-]+$ (schemas/review-result.schema.json) explicitly permits colons and passes a value like :::error:::injected straight through both the regex check (post-review.sh's own [[ ! "${LA_LABEL}" =~ ^[a-zA-Z0-9._/:\ +\-]+$ ]] guard) and the vulnerable sanitizer, after which it is echoed into ::warning::...'${LA_LABEL}'... workflow-command lines in the label-validation loop.
Confirmed not in this PR's diff (pre-existing code, untouched by the diff), but it is the exact bug class this PR fixes elsewhere in the same file and was never raised in any of the review rounds on this PR.
Suggestion: Apply the same full-character-strip approach used for REVIEW_FINDING_SEVERITY_THRESHOLD (strip newlines/CR, then strip every : outright — or extract a shared sanitize_for_workflow_command() helper) to LA_LABEL (and LA_ACTION for consistency) instead of the single-pass ${var//::/:} replace, since this PR already establishes that pattern is unsafe.
[MEDIUM] docs/review.md:81 — asserts an unverified harness base: extension mechanism (including deep-merge semantics) as settled fact
See inline comment on docs/review.md line 81 for full detail.
[MEDIUM] env/review.env:7 — re-exports REVIEW_FINDING_SEVERITY_THRESHOLD from the runner-side value, risking clobber of a partial env.sandbox override
harness/review.yaml's host_files entry loads this file (untouched by this PR's diff) into the sandbox at /sandbox/workspace/.env.d/review.env with expand: true. Line 7 (export REVIEW_FINDING_SEVERITY_THRESHOLD="${REVIEW_FINDING_SEVERITY_THRESHOLD}") is expanded against the runner-side environment (i.e., env.runner.REVIEW_FINDING_SEVERITY_THRESHOLD) at host_files-expansion time, then written into the sandbox as a dotenv file.
Today this is benign because env.runner and env.sandbox both hardcode the same literal "low". But this PR's own new docs/review.md guidance (line 81) tells downstream repos to override severity by setting env.runner / env.sandbox independently via a base: extension — and harness/review.yaml's own new comment explicitly acknowledges "they're allowed to differ." If a downstream repo overrides only env.sandbox (e.g. to "high") without touching env.runner, and if .env.d/review.env is sourced inside the sandbox after the harness's own env.sandbox injection (plausible, since dotenv files are typically sourced late in shell init), this line would silently overwrite the sandbox override back to the stale env.runner value — defeating the override the new docs just told the user to make. This interaction between the new env.runner/env.sandbox mechanism and the pre-existing env/review.env host_file was not discussed anywhere in this PR's extensive review history.
Suggestion: Confirm (with the harness engine authors, or empirically via a dry run) the precedence order between env.sandbox injection and .env.d/*.env sourcing inside the sandbox for this variable. If .env.d/review.env can clobber a harness-supplied env.sandbox override, either remove the REVIEW_FINDING_SEVERITY_THRESHOLD line from env/review.env (since env.sandbox now owns it) or update it to source from the sandbox-side value instead of the runner-side one.
Now that fullsend-ai/fullsend#80 and #81 have merged, we can point at authoritative sources for the base-composition merge behavior and the CI workflow env: block boundary, instead of asserting them unverified. Assisted-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Ralph Bean <rbean@redhat.com>
|
🤖 Finished Review · ✅ Success · Started 5:58 PM UTC · Completed 6:11 PM UTC |
waynesun09
left a comment
There was a problem hiding this comment.
Reviewed the fix at 6d18709 (strip every :/% character outright rather than collapsing substrings) — this closes the GHA workflow-command injection class correctly, since removing all instances of a single character can never reassemble into that character. Confirmed no regressions in the follow-up ADR-0080/0081 doc-citation commit. CI green, all threads resolved.
|
🤖 Finished Retro · ✅ Success · Started 6:51 PM UTC · Completed 7:02 PM UTC |
Retro: PR #573 — move severity threshold default to harness envWhat happenedHuman-authored PR by ralphbean moving Review agent performanceBoth review bots (fullsend-ai-review and qodo-code-review) correctly identified the category of vulnerability — GHA workflow-command injection via unsanitized The human reviewer went deeper than the agents in three critical ways:
Evidence for existing issues
Workflow cost43 Proposals filed
|
Summary
REVIEW_FINDING_SEVERITY_THRESHOLDdefault (low) fromagents/review.mdandscripts/post-review.shintoharness/review.yamlunderenv.runnerandenv.sandboxAligns the review agent with the configuration pattern recommended in #566.
Test plan
bash scripts/post-review-test.sh— all tests passshellcheckclean on both changed shell scripts🤖 Generated with Claude Code