Skip to content

feat(harness): report categorized post-script failures on issue/PR#38

Open
ifireball wants to merge 1 commit into
fullsend-ai:mainfrom
ifireball:feat/post-script-failure-reporting
Open

feat(harness): report categorized post-script failures on issue/PR#38
ifireball wants to merge 1 commit into
fullsend-ai:mainfrom
ifireball:feat/post-script-failure-reporting

Conversation

@ifireball

Copy link
Copy Markdown
Member

Summary

  • Add shared scripts/lib/post-failure-report.sh with categorized failure comments, output sanitization, and workflow run links
  • post-code.sh posts detailed failure comments on the issue (push rejected, pre-commit blocked, secret scan, PR creation failed, etc.)
  • post-fix.sh posts equivalent failure comments on the PR (previously silent on many post-script failures)
  • Workflow-permission push rejections are called out explicitly as environmental limitations
  • Extend post-code-test.sh / post-fix-test.sh coverage for failure reporting, sanitization, and push categorization

Ports fullsend-ai/fullsend#2921.

Depends on #73 — the new/extended tests in this PR will not run in CI until the script-test workflow lands.

Test plan

  • bash scripts/post-code-test.sh
  • bash scripts/post-fix-test.sh
  • CI green once #73 merges

Made with Cursor

Port fullsend-ai/fullsend#2921: add shared post-failure-report.sh with
categorized failure comments, output sanitization, and workflow run links.
post-code.sh posts detailed failures on the issue; post-fix.sh posts
equivalent comments on the PR. Extend post-code-test.sh and post-fix-test.sh.

Signed-off-by: Barak Korren <bkorren@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@ifireball ifireball self-assigned this Jul 7, 2026
@ifireball ifireball requested a review from a team July 7, 2026 07:39
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 7, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:39 AM UTC · Completed 7:54 AM UTC
Commit: c1a58d8 · View workflow run →

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Report categorized post-script failures on issues/PRs (shared failure reporter)

✨ Enhancement 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Add shared post-script failure reporter with categorization, sanitization, and run links.
• Post detailed failure comments to the issue/PR for push, scan, and hook failures.
• Expand harness tests to cover categories, redaction, and push-rejection classification.
Diagram

graph TD
A["scripts/post-code.sh"] --> G{{"Push/scan/hooks"}} --> L[["scripts/lib/post-failure-report.sh"]] --> I(("Issue comment"))
B["scripts/post-fix.sh"] --> G --> L --> P(("PR comment"))
L --> R{{"Workflow run link"}}
subgraph Legend
direction LR
_s[Script] ~~~ _lib[[Library]] ~~~ _ext{{External/Tool}} ~~~ _tgt((Comment target))
end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Keep per-script bespoke error comments
  • ➕ No new shared library surface area
  • ➕ Each script can tailor messaging without shared constraints
  • ➖ Duplication and drift between post-code and post-fix
  • ➖ Harder to keep sanitization/redaction consistent everywhere
  • ➖ More test duplication
2. Implement as a composite GitHub Action / reusable workflow
  • ➕ Centralizes behavior at the workflow layer (less bash plumbing)
  • ➕ Easier to standardize permissions, environment, and outputs
  • ➕ Can be reused across repos with versioned releases
  • ➖ Higher migration cost and requires workflow refactors
  • ➖ Harder to unit test locally compared to pure bash scripts
  • ➖ May reduce flexibility for repo-specific logic
3. Use GitHub API directly (curl) instead of `gh`
  • ➕ Avoids dependency on GitHub CLI availability/behavior
  • ➕ More explicit control over auth and error handling
  • ➖ More code and more auth/header boilerplate
  • ➖ Higher likelihood of subtle API/JSON handling bugs in bash

Recommendation: The shared post-failure-report.sh approach is the best fit here: it removes duplicated comment construction, enforces consistent sanitization, and makes failure categories (notably workflow-permission limitations) visible to users. Consider a composite action only if multiple workflows/repos need the same reporting logic beyond these harness scripts.

Files changed (5) +507 / -173

Enhancement (3) +342 / -118
post-failure-report.shAdd shared categorized failure reporting + sanitization utilities +240/-0

Add shared categorized failure reporting + sanitization utilities

• Introduces a reusable library that categorizes post-script failures, redacts sensitive output (tokens/PEM keys), truncates noisy logs, and builds consistent issue/PR comment bodies including workflow run links. Provides helpers to post best-effort comments to issues or PRs and convenience functions to set category/detail and exit.

scripts/lib/post-failure-report.sh

post-code.shPost categorized failure comments to the issue on post-code errors +52/-79

Post categorized failure comments to the issue on post-code errors

• Replaces bespoke ERR-trap issue commenting with the shared failure reporter and sets a global ERR trap to report failures. Converts several hard exits into categorized 'post_fail_to_issue' calls (branch validation, gitleaks/secret scan, signed-off-by, pre-commit failures, push failures, PR creation failures) and captures tool output for inclusion in sanitized comments.

scripts/post-code.sh

post-fix.shPost categorized failure comments to the PR on post-fix errors +50/-39

Post categorized failure comments to the PR on post-fix errors

• Adds shared failure reporter sourcing and sets an ERR trap to comment on the PR when post-fix fails. Replaces silent/uncategorized exits across secret scan, signed-off-by, pre-commit, push, result scanning, and structured output processing with categorized 'post_fail_to_pr' calls and captured output for sanitized reporting.

scripts/post-fix.sh

Tests (2) +165 / -55
post-code-test.shRefactor failure comment tests to use shared library + add redaction/categorization coverage +106/-55

Refactor failure comment tests to use shared library + add redaction/categorization coverage

• Switches tests from an inline comment builder to 'build_post_failure_comment' from the shared library. Adds new cases for category labeling (push rejected, workflow permission, secret scan, pre-commit) plus sanitization and push-failure categorization tests.

scripts/post-code-test.sh

post-fix-test.shAdd PR failure comment tests for post-fix using shared library +59/-0

Add PR failure comment tests for post-fix using shared library

• Adds test coverage ensuring post-fix failure comments include correct category labeling, workflow-permission environmental notes, generic secret-scan messaging, retry hint ('/fs-fix'), and workflow run link presence, while avoiding leaking secret scan findings.

scripts/post-fix-test.sh

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (2)

Context used
✅ Compliance rules (platform): 55 rules
✅ Skills: 4 invoked
  code-review
  code-implementation
  pr-review
  docs-review

Grey Divider


Action required

1. ISSUE_NUMBER in ::warning unsanitized 📜 Skill insight ⛨ Security
Description
The new failure-reporting library emits GitHub Actions workflow commands (::warning::...) with
interpolated values that are not sanitized. Unsanitized interpolation can allow workflow-command
injection via ::, encoded newlines (%0A/%0D), or control characters.
Code

scripts/lib/post-failure-report.sh[R193-223]

+  echo "::warning::Posting failure comment to issue #${ISSUE_NUMBER}..."
+  gh issue comment "${ISSUE_NUMBER}" \
+    --repo "${REPO_FULL_NAME}" \
+    --body "${body}" 2>/dev/null || \
+    echo "::warning::Failed to post error comment to issue #${ISSUE_NUMBER}"
+}
+
+report_post_failure_to_pr() {
+  local exit_code="${1:-$?}"
+
+  if [ "${POST_FAILURE_REPORTED}" = "true" ]; then
+    return 0
+  fi
+  POST_FAILURE_REPORTED=true
+
+  _post_failure_ensure_token
+
+  local category="${POST_FAILURE_CATEGORY:-post-script-error}"
+  local detail="${POST_FAILURE_DETAIL:-Post-fix script failed before push or PR update completed.}"
+  local body
+  # PR_NUMBER and REPO_FULL_NAME are required by post-fix.sh before sourcing.
+  # shellcheck disable=SC2153
+  body="$(build_post_failure_comment \
+    "fix" "${exit_code}" "${category}" "${detail}" \
+    "${REPO_FULL_NAME}" "/fs-fix")"
+
+  echo "::warning::Posting failure comment to PR #${PR_NUMBER}..."
+  gh pr comment "${PR_NUMBER}" \
+    --repo "${REPO_FULL_NAME}" \
+    --body "${body}" 2>/dev/null || \
+    echo "::warning::Failed to post error comment to PR #${PR_NUMBER}"
Evidence
PR Compliance ID 1538382 requires individually sanitizing every interpolated variable used in GitHub
Actions workflow commands. The new code echoes ::warning::... messages containing interpolated
ISSUE_NUMBER / PR_NUMBER (and other dynamic content) without any GHA-command sanitization.

scripts/lib/post-failure-report.sh[193-223]
Skill: pr-review

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`scripts/lib/post-failure-report.sh` prints GitHub Actions workflow commands (e.g., `::warning::...`) that include interpolated variables (e.g., `ISSUE_NUMBER`, `PR_NUMBER`) without sanitizing each variable for workflow-command injection vectors (`::`, `%0A/%0D`, ANSI/control chars).

## Issue Context
Compliance requires that **every interpolated value** in a GitHub Actions workflow command be individually sanitized, not just some values or only "likely safe" values.

## Fix Focus Areas
- scripts/lib/post-failure-report.sh[193-223]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Protected scripts/ paths changed 📜 Skill insight § Compliance
Description
This PR modifies protected governance/infrastructure paths under scripts/, which must always be
flagged for required human review and must not be auto-approved. These changes affect post-script
behavior and failure reporting in automation.
Code

scripts/post-code.sh[R40-46]

+SCRIPT_DIR_POST="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+# shellcheck source=lib/post-failure-report.sh
+source "${SCRIPT_DIR_POST}/lib/post-failure-report.sh"
+
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
Evidence
PR Compliance ID 1538392 mandates raising a finding whenever protected paths (including scripts/)
are modified. The diff shows new/modified automation scripts under scripts/ (e.g.,
scripts/post-code.sh) including new sourcing/trap behavior.

scripts/post-code.sh[40-60]
Skill: pr-review



Remediation recommended

3. Markdown fence injection 🐞 Bug ≡ Correctness
Description
build_post_failure_comment embeds sanitized failure output inside a triple-backtick fenced block
without escaping embedded ``` sequences, so certain command outputs can prematurely terminate the
fence and corrupt the rendered issue/PR comment. This is reachable because post-code.sh/post-fix.sh
pass raw command output (push/pre-commit/etc.) into the failure detail that becomes the comment
body.
Code

scripts/lib/post-failure-report.sh[R138-145]

+  if [ -n "${sanitized_detail}" ]; then
+    detail_block="$(cat <<EOF
+
+**Details:**
+\`\`\`
+${sanitized_detail}
+\`\`\`
+EOF
Evidence
The comment builder inserts ${sanitized_detail} directly between literal ``` fences, but the
sanitizer only redacts token patterns and PEM blocks and does not escape backticks, so the Markdown
structure can be broken by outputs containing ```.

scripts/lib/post-failure-report.sh[38-56]
scripts/lib/post-failure-report.sh[138-146]
scripts/post-code.sh[382-401]
scripts/post-fix.sh[300-319]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`build_post_failure_comment()` builds a Markdown comment that wraps `${sanitized_detail}` in a triple-backtick fenced code block. If `${sanitized_detail}` contains ``` (or an unmatched fence-like sequence), the fence can be closed early and the rest of the comment renders incorrectly.

### Issue Context
Although the content is token-redacted, it is still untrusted command output (e.g., git push / pre-commit output) and may contain arbitrary text.

### Fix Focus Areas
- scripts/lib/post-failure-report.sh[118-166]

### Suggested fix
Update `build_post_failure_comment()` to avoid fenced blocks entirely by rendering details as an indented code block:
- Build `detail_block` by prefixing every line of `${sanitized_detail}` (including empty lines) with 4 spaces, e.g. via `sed 's/^/    /'`.
- Keep the existing token/PEM redaction and line truncation.

This removes the possibility of fence-termination by embedded backticks and preserves readability in GitHub-flavored Markdown.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +193 to +223
echo "::warning::Posting failure comment to issue #${ISSUE_NUMBER}..."
gh issue comment "${ISSUE_NUMBER}" \
--repo "${REPO_FULL_NAME}" \
--body "${body}" 2>/dev/null || \
echo "::warning::Failed to post error comment to issue #${ISSUE_NUMBER}"
}

report_post_failure_to_pr() {
local exit_code="${1:-$?}"

if [ "${POST_FAILURE_REPORTED}" = "true" ]; then
return 0
fi
POST_FAILURE_REPORTED=true

_post_failure_ensure_token

local category="${POST_FAILURE_CATEGORY:-post-script-error}"
local detail="${POST_FAILURE_DETAIL:-Post-fix script failed before push or PR update completed.}"
local body
# PR_NUMBER and REPO_FULL_NAME are required by post-fix.sh before sourcing.
# shellcheck disable=SC2153
body="$(build_post_failure_comment \
"fix" "${exit_code}" "${category}" "${detail}" \
"${REPO_FULL_NAME}" "/fs-fix")"

echo "::warning::Posting failure comment to PR #${PR_NUMBER}..."
gh pr comment "${PR_NUMBER}" \
--repo "${REPO_FULL_NAME}" \
--body "${body}" 2>/dev/null || \
echo "::warning::Failed to post error comment to PR #${PR_NUMBER}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. issue_number in ::warning unsanitized 📜 Skill insight ⛨ Security

The new failure-reporting library emits GitHub Actions workflow commands (::warning::...) with
interpolated values that are not sanitized. Unsanitized interpolation can allow workflow-command
injection via ::, encoded newlines (%0A/%0D), or control characters.
Agent Prompt
## Issue description
`scripts/lib/post-failure-report.sh` prints GitHub Actions workflow commands (e.g., `::warning::...`) that include interpolated variables (e.g., `ISSUE_NUMBER`, `PR_NUMBER`) without sanitizing each variable for workflow-command injection vectors (`::`, `%0A/%0D`, ANSI/control chars).

## Issue Context
Compliance requires that **every interpolated value** in a GitHub Actions workflow command be individually sanitized, not just some values or only "likely safe" values.

## Fix Focus Areas
- scripts/lib/post-failure-report.sh[193-223]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread scripts/post-code.sh
Comment on lines +40 to 46
SCRIPT_DIR_POST="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib/post-failure-report.sh
source "${SCRIPT_DIR_POST}/lib/post-failure-report.sh"

# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Protected scripts/ paths changed 📜 Skill insight § Compliance

This PR modifies protected governance/infrastructure paths under scripts/, which must always be
flagged for required human review and must not be auto-approved. These changes affect post-script
behavior and failure reporting in automation.

Comment on lines +138 to +145
if [ -n "${sanitized_detail}" ]; then
detail_block="$(cat <<EOF

**Details:**
\`\`\`
${sanitized_detail}
\`\`\`
EOF

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

3. Markdown fence injection 🐞 Bug ≡ Correctness

build_post_failure_comment embeds sanitized failure output inside a triple-backtick fenced block
without escaping embedded ``` sequences, so certain command outputs can prematurely terminate the
fence and corrupt the rendered issue/PR comment. This is reachable because post-code.sh/post-fix.sh
pass raw command output (push/pre-commit/etc.) into the failure detail that becomes the comment
body.
Agent Prompt
### Issue description
`build_post_failure_comment()` builds a Markdown comment that wraps `${sanitized_detail}` in a triple-backtick fenced code block. If `${sanitized_detail}` contains ``` (or an unmatched fence-like sequence), the fence can be closed early and the rest of the comment renders incorrectly.

### Issue Context
Although the content is token-redacted, it is still untrusted command output (e.g., git push / pre-commit output) and may contain arbitrary text.

### Fix Focus Areas
- scripts/lib/post-failure-report.sh[118-166]

### Suggested fix
Update `build_post_failure_comment()` to avoid fenced blocks entirely by rendering details as an indented code block:
- Build `detail_block` by prefixing every line of `${sanitized_detail}` (including empty lines) with 4 spaces, e.g. via `sed 's/^/    /'`.
- Keep the existing token/PEM redaction and line truncation.

This removes the possibility of fence-termination by embedded backticks and preserves readability in GitHub-flavored Markdown.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@fullsend-ai-review

Copy link
Copy Markdown

Review

Verdict: comment — one medium-severity defense-in-depth improvement and several low-severity observations. No blocking issues.

This PR extracts failure reporting into a well-structured shared library (scripts/lib/post-failure-report.sh), adds categorized failure types and output sanitization, and extends both post-code.sh and post-fix.sh to post detailed failure comments. The design is sound — the shared library follows the existing scripts/lib/ pattern, the ERR trap + post_fail_to_{issue,pr} pattern is clean, and the token redaction is a strict improvement over the base (which had zero redaction in error comments).

Findings

1. sanitize_failure_detail() does not strip GHA workflow command sequences — Medium

File: scripts/lib/post-failure-report.sh

The sanitize_failure_detail() function redacts tokens and PEM keys but does not strip :: sequences or %0A/%0D URL-encoded newlines. The PR removes the SAFE_PUSH sanitization block from post-code.sh (lines 419–425 in the base) that previously stripped these sequences from push output before ::error:: interpolation.

In the new code path, push output and pre-commit output are echoed to the GHA runner log (echo "${PUSH_OUTPUT}", echo "${PRECOMMIT_OUTPUT}") before being passed to post_fail_to_issue/post_fail_to_pr. If the output contains GHA workflow command sequences (e.g., ::add-mask::, ::warning::), the runner will interpret them.

Mitigating factors: (1) The base code's echo "${PUSH_OUTPUT}" on line 409 was also unsanitized, so this is partially pre-existing; (2) The PR removes the specific ::error:: interpolation that was the main vector; (3) The comment body posted via gh issue comment is not subject to GHA command injection.

Recommendation: Add :: and %0A/%0D stripping to sanitize_failure_detail(), or create a dedicated sanitize_for_gha_log() function applied to all echo statements that output captured command results.

2. Org-mode URL routing test coverage — Low

File: scripts/post-code-test.sh

The base branch has 3 org-mode URL routing tests (error-comment-org-mode-uses-dispatch-repo, error-comment-org-mode-not-source-repo, error-comment-non-org-mode-fallback) that verify the GITHUB_REPOSITORY vs REPO_FULL_NAME distinction. The PR replaces these with the new run_failure_comment_test helper. The replacement test failure-comment-org-mode-uses-dispatch-repo covers the dispatch-repo case, but the negative test (URL must NOT contain the source repo) and the non-org-mode fallback test appear to be dropped.

Consider adding equivalent coverage for these edge cases.

3. ERR trap handler robustness — Low

File: scripts/lib/post-failure-report.sh

The report_post_failure_to_issue() / _to_pr() functions set POST_FAILURE_REPORTED=true before calling build_post_failure_comment(). Under set -e, if build_post_failure_comment fails (e.g., awk/sed error on malformed input), the handler aborts before reaching gh issue comment, and the re-entry guard prevents retry. The CI run still exits non-zero, but the error comment is silently skipped.

This is a theoretical concern (sed/awk are extremely unlikely to fail), and the base code has the same structural pattern.

4. Markdown code fence escape — Low

File: scripts/lib/post-failure-report.sh

build_post_failure_comment() wraps sanitized_detail in a markdown code fence (triple backticks). sanitize_failure_detail() does not strip triple-backtick sequences from the detail. Pre-commit output containing ``` could close the fence and inject arbitrary markdown into the comment. Impact is cosmetic (GitHub sanitizes HTML/scripts), not a security vulnerability.

5. POST_FAILURE_DETAIL_MAX_LINES undocumented — Low

Files: scripts/post-code.sh, scripts/post-fix.sh

The POST_FAILURE_DETAIL_MAX_LINES environment variable is configurable but not listed in the script headers' "Optional environment variables" sections. Since the library is sourced by both scripts, users setting this variable might not discover it without reading the library source.

@ralphbean ralphbean left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we learned in the fix for the prioritize agent's CSMA lib fix that the post scripts of agents cannot reach a lib script. Right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants