Skip to content

fix(#1795): run scribe content gates before gates that log content - #1796

Open
fullsend-ai-coder[bot] wants to merge 3 commits into
mainfrom
agent/1795-scribe-content-gates-first
Open

fullsend-ai-coder[bot] wants to merge 3 commits into
mainfrom
agent/1795-scribe-content-gates-first

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

scripts/post-scribe.src.sh (and the bundled scripts/post-scribe.sh) now run the sensitive-content and suspicious-Unicode gates right after the public_safe check. They reject through gate_reject_content, so a proposal matching those patterns is never printed to the CI log, whichever gate would otherwise have rejected it.

Root cause

In both the existing-issue loop and the new-issue loop, several gates ran before the content gates and called gate_reject "${TOPIC}" / gate_reject "${TITLE}", which logs the raw model-produced value (_gha_sanitize only escapes workflow-command characters). For comments these were the invalid existing_issue, missing-summary and confidence gates. For new issues they were the confidence gates. The OMITTED: log line did the same. The content gates also printed the value they had rejected because it was sensitive. This happened on dry runs too.

Changes

  • Content gates now run before every other gate and log line that prints the topic or title.
  • They reject through gate_reject_content, which logs only the item index and a category (sensitive content / suspicious Unicode) and counts the item as a content-gate rejection.
  • The topic gates also check omit_reason, and the new-issue gates also check labels. Both are printed later (OMITTED: line, dry-run Labels: line), and the issue asks that labels never be printed when they match.
  • Rebuilt scripts/post-scribe.sh with scripts/bundle-sh.sh.

Behavior change: an omitted topic, or one with no existing_issue, is now counted as a gate rejection when it contains sensitive content or suspicious Unicode. Before, it was logged or skipped.

Testing

  • New run_test_content_suppressed helper in scripts/post-scribe-test.sh. It asserts the expected content-gate line is printed, the forbidden value never appears on stdout, and the item is counted as rejected.
  • New cases, mostly at confidence: 0.2: sensitive topic, sensitive summary only, suspicious Unicode with an invalid existing_issue, an omitted sensitive topic, a sensitive new-issue title, a sensitive new-issue body only, suspicious Unicode with invalid confidence, and a sensitive label.
  • All new cases fail on main and pass with this change. Existing tests, including the clean low-confidence rejection, still pass, in both source and bundled modes. The two sensitive-email tests now expect the content-gate output.
  • The bundle matches bundle-sh.sh output. shellcheck 0.10.0.1 (repo args) and scan-secrets passed.

Caveats

  • pre-commit could not fetch hook repos in the sandbox (TLS failure). The relevant hooks were run directly instead.
  • bc is not installed in the sandbox, so the post-scribe tests ran with a local bc shim.
  • make lint (skillsaw via uvx) could not run because uvx is unavailable. It does not lint shell scripts.

🤖 Generated with Claude Code


Closes #1795

Post-script verification

  • Branch is not main/master (agent/1795-scribe-content-gates-first)
  • Secret scan passed (gitleaks — f589f2e719ba9bdda0a5915b8d4a86c45e7694e7..HEAD)
  • PR body secret scan passed (gitleaks — no-git)
  • Final PR description secret scan passed (gitleaks — no-git)

The scribe post-script printed the raw topic or title when a gate
that runs before the sensitive-content check rejected a proposal
(confidence, invalid existing_issue, missing summary), and when the
OMITTED line was logged. The sensitive-content and suspicious-Unicode
gates themselves also printed the rejected value via gate_reject. A
proposal carrying an email address or hidden Unicode could therefore
reach the CI log, including on dry runs.

Move both content gates to run right after the public_safe check in
the existing-issue and new-issue loops, and reject through
gate_reject_content, which logs only the item index and category.
The gates also cover omit_reason for topics and labels for new
issues, since both are printed later. Rebuilt scripts/post-scribe.sh
with bundle-sh.sh.

Tests: add post-scribe tests for low-confidence, invalid-number,
invalid-confidence, omitted and label cases asserting the sensitive
value never appears on stdout and the item is counted as rejected.
Update the two sensitive-email tests to the content-gate output.

Note: pre-commit could not fetch hook repos (TLS failure in the
sandbox); hooks were run directly instead (shellcheck 0.10.0.1,
whitespace/EOF/merge-conflict checks, lint-release-cases) and passed.
scan-secrets passed. make lint (skillsaw via uvx) could not run, as
uvx is unavailable; it does not cover shell scripts. post-scribe tests
were run with a bc shim because bc is not installed in the sandbox.

Closes #1795

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner October 26, 2026 18:33
@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Triggers review agent dispatch label Oct 26, 2026
@fullsend-ai-review

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Ended 6:34 PM UTC

Commit: d3ba9f5 · View workflow run →

Automated review did not complete for this commit. Check the latest review status before merging. If no review is queued or running, comment /fs-review to retry.

@fullsend-ai-review

fullsend-ai-review Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:36 PM UTC · Completed 6:46 PM UTC

Commit: d3ba9f5 · View workflow run →

Runtime: pi · Model: sonnet → claude-sonnet-5-5 · Effort: high · Cost: $1.79

@fullsend-ai-review fullsend-ai-review Bot added the risk/moderate PR risk: moderate label Oct 26, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Modifies gate execution order across protected script paths without a feature toggle, but overall risk remains moderate given low git churn, tight alignment with issue #1795, and substantial regression test coverage.

Previous run

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Modifies gate execution order across protected script paths without a feature toggle, but overall risk is moderate given tight alignment with issue #1795, extensive regression test additions, and low git churn.

@fullsend-ai-review

fullsend-ai-review Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Review

Findings

Medium

Low

  • [fail-open] scripts/post-scribe.src.sh:167 — contains_sensitive and contains_suspicious_unicode still map detector execution failures (grep status 2 or higher, Perl status 127 or a runtime error) to "clean", because callers use them as predicates and set -e does not apply. This pre-dates the PR: base main has the same cmd && return 0; ... return 1 structure, and this PR only changes how input reaches the detectors. The detector patterns are fixed literals, so a missing binary is the only realistic trigger. The same code is in the generated scripts/post-scribe.sh.
    Remediation: Optional hardening, outside this PR's scope. Treat exit status 1 as no-match and reject the item (or fail closed) on any other status, then regenerate scripts/post-scribe.sh.

Earlier findings

  • scripts/post-scribe.src.sh:404, fail-open — resolved_by_change. The latest commits replace the echo | grep -q / echo | perl pipelines with here-strings, so the writer can no longer die of SIGPIPE under pipefail and make the detector report "clean". Regression tests cover oversized omit_reason and label values.
Previous run

<!-- fullsend:review-findings-v2:eyJ2ZXJzaW9uIjoyLCJhY3Rpb24iOiJyZXF1ZXN0LWNoYW5nZXMiLCJmaW5kaW5ncyI6W3sic2V2ZXJpdHkiOiJtZWRpdW0iLCJjYXRlZ29yeSI6ImZhaWwtb3BlbiIsImZpbGUiOiJzY3JpcHRzL3Bvc3Qtc2NyaWJlLnNyYy5zaCIsImlkIjoiZl82MmI2MzI0ZTNlZWQzOTdiIiwibGluZSI6NDA0LCJhY3Rpb25hYmxlIjp0cnVlfV19 -->

Review

Findings

Medium

  • [fail-open] scripts/post-scribe.src.sh:404 — The newly gated fields labels (and omit_reason, line 282) can still bypass both content gates and reach the CI log. contains_sensitive and contains_suspicious_unicode (unchanged, ~lines 138-190) pipe echo "${text}" into an early-exiting reader (grep -q / Perl exit) under set -euo pipefail. When the input exceeds the pipe buffer (~64KB) and a match appears early, the writer gets SIGPIPE, the pipeline fails, and the helper returns 1 ("clean"). Schema-bounded fields (topic, title, summary, body) stay below that size, but labels and omit_reason have no maxLength in schemas/scribe-result.schema.json, so a multi-MB value with PII or a zero-width character near the start passes the gates and is printed by [DRY RUN] Labels: / OMITTED:. The detector flaw predates this PR, but this PR newly relies on it for these fields. The bundled scripts/post-scribe.sh is affected the same way.
    Remediation: Make the detectors read all of their input (here-strings, e.g. grep -qE ... <<<"${text}", or printf into grep without -q), or bound the length of labels/omit_reason before the content gates and reject without echoing. Add a regression test with an oversized label/omit_reason, then regenerate scripts/post-scribe.sh with scripts/bundle-sh.sh.

  • [protected-path] scripts/post-scribe.src.sh, scripts/post-scribe.sh, scripts/post-scribe-test.sh — This PR modifies files under the protected scripts/ path. The PR links issue scribe: rejected proposals are printed to CI logs before the sensitive-content gate runs #1795 and explains the rationale, but human approval is always required for protected-path changes, regardless of context.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 6:48 PM UTC · Completed 6:54 PM UTC

Commit: d3ba9f5 · View workflow run →

Runtime: claude · Model: sonnet → claude-sonnet-5-5 · Effort: high · Cost: $0.88

fullsend-ai-coder Bot and others added 2 commits October 26, 2026 18:52
contains_sensitive and contains_suspicious_unicode piped `echo` into an
early-exiting reader. Under `set -o pipefail`, large multi-line input
(e.g. the unbounded labels and omit_reason fields) made the writer die
of SIGPIPE, the pipeline fail, and the helper report "clean", so the
value reached the log. Feed the detectors via here-strings instead and
add regression tests with oversized omit_reason and label values.
Regenerate scripts/post-scribe.sh from the source.

Notes: pre-commit could not fetch hook repos (certificate error); ran
syntax checks, secret scan and scripts/post-scribe-test.sh directly.
The two */low-confidence-rejected tests fail in this sandbox because
`bc` is not installed; they fail identically without this change.

Addresses #1796

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (bot-triggered)

Fixed SIGPIPE fail-open in scribe content gates with regression tests, and resolved the shellcheck SC1003 pre-commit failure in the test script. The two */low-confidence-rejected tests fail in this sandbox only because bc is not installed; they fail identically without these changes.

Fixed (2):

  1. Content-gate helpers piped echo into early-exiting readers, so SIGPIPE under pipefail could make oversized unbounded fields (omit_reason, labels) report clean and leak into logs (scripts/post-scribe.src.sh): Feed contains_sensitive and contains_suspicious_unicode via here-strings instead of pipes; regenerated scripts/post-scribe.sh and added oversized-field regression tests
  2. Pre-commit shellcheck SC1003 in the new test's ZWSP escape construction (scripts/post-scribe-test.sh): Replaced the printf-with-backslash construction with ZWSP_ESCAPE="\u200b", which yields the same JSON escape and passes shellcheck

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@fullsend-ai-review

fullsend-ai-review Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:56 PM UTC · Completed 7:03 PM UTC

Commit: cfb886a · View workflow run →

Runtime: pi · Model: sonnet → claude-sonnet-5-5 · Effort: high · Cost: $1.57

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review October 26, 2026 19:03

Superseded by updated review

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See the review comment for full details.

# Tag characters (U+E0000–E007F), zero-width chars, BOM, bidi overrides/isolates
echo "${text}" \
| perl -e 'binmode(STDIN, q(:encoding(UTF-8))); while (<STDIN>) { if (/[\x{E0000}-\x{E007F}\x{200B}\x{200C}\x{200D}\x{FEFF}\x{202A}-\x{202E}\x{2066}-\x{2069}]/) { exit 0 } } exit 1' \
perl -e 'binmode(STDIN, q(:encoding(UTF-8))); while (<STDIN>) { if (/[\x{E0000}-\x{E007F}\x{200B}\x{200C}\x{200D}\x{FEFF}\x{202A}-\x{202E}\x{2066}-\x{2069}]/) { exit 0 } } exit 1' <<<"${text}" \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] fail-open

contains_sensitive/contains_suspicious_unicode map grep (status >=2) and Perl (status 127/runtime error) execution failures to "clean" because callers use them as predicates. Pre-existing on base main; this PR only changes input delivery (here-strings). Only a missing binary on the runner is a realistic trigger. Same code in scripts/post-scribe.sh.

Suggested fix: Optional hardening outside this PR's scope: treat status 1 as no-match and reject the item (or fail closed) on any other status; regenerate scripts/post-scribe.sh.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Oct 26, 2026

This branch was successfully deployed

1 active deployment
dev — cfb886a1 Deployed Oct 26, 2026 by fullsend-ai-coder[bot] via behaviour #1328
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-review Triggers review agent dispatch requires-manual-review Review requires human judgment risk/moderate PR risk: moderate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

scribe: rejected proposals are printed to CI logs before the sensitive-content gate runs

2 participants