Skip to content

fix: route OpenCode reviews through hush proxy using built-in provider#34

Merged
byapparov merged 4 commits into
masterfrom
fix/simplify-opencode-provider-config
Mar 3, 2026
Merged

fix: route OpenCode reviews through hush proxy using built-in provider#34
byapparov merged 4 commits into
masterfrom
fix/simplify-opencode-provider-config

Conversation

@byapparov
Copy link
Copy Markdown
Contributor

Summary

  • Restores hush gateway proxy routing for OpenCode AI reviews (was removed, leaving reviews unprotected)
  • Uses a simple options.baseURL override on the built-in zai-coding-plan provider instead of defining a custom hush-zhipu provider
  • Adds the hush PII guard plugin for file-read protection
  • Explicit PR number in review prompt for reliable comment posting

What changed

The workflow now:

  1. Builds hush from source and starts it on :4000
  2. Configures OpenCode with {"provider":{"zai-coding-plan":{"options":{"baseURL":"http://127.0.0.1:4000/api/coding/paas/v4"}}}}
  3. Runs the review through zai-coding-plan/glm-5 — traffic goes through hush for PII interception

Verification

Tested locally with both a debug build from OpenCode source and the installed v1.2.15 binary. Confirmed that options.baseURL on a built-in provider is correctly propagated through all merge phases and hush proxy receives traffic at /api/coding/paas/v4/chat/completions.

Test plan

  • CI workflow triggers on this PR and OpenCode review comment appears
  • Hush gateway starts successfully and intercepts the review traffic

The previous workflow sent reviews directly to ZhipuAI without PII
protection. This restores hush proxy routing using a simple baseURL
override on the built-in zai-coding-plan provider — no custom provider
definition needed.

Verified locally that OpenCode v1.2.15 correctly propagates
options.baseURL on built-in providers through all merge phases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 2, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 79.78% 292 / 366
🔵 Statements 78.8% 316 / 401
🔵 Functions 75% 42 / 56
🔵 Branches 68.2% 148 / 217
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/index.ts 76.47% 56.62% 66.66% 80.83% 15, 17, 43-48, 84, 93-95, 144, 161-166, 177, 182, 184, 194, 217-222, 230-237, 244, 263
Generated in workflow #105 for commit 3ea8a7e by the Vitest Coverage Report Action

Redirect hush process output to a log file and add a verification step
that checks for "Starting stream proxy" in the logs — proving traffic
actually went through hush rather than directly to the upstream API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aictrl-dev aictrl-dev deleted a comment from github-actions Bot Mar 2, 2026
Use `always()` condition so the "Verify Hush Proxy Was Used" step
runs regardless of whether the review step succeeded or timed out.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 2, 2026

PR #34 Review: Route OpenCode reviews through hush proxy

Scope: This PR modifies only .github/workflows/opencode-review.yml to route AI reviews through the hush gateway. No changes to core redaction/streaming code.


✅ What works well

Workflow design: Clean setup with health check loop, explicit PR number in prompt, and always() condition for verification step.

Proxy integration: Using options.baseURL override on the built-in zai-coding-plan provider is elegant — avoids custom provider definitions.


🔍 Findings on underlying code (src/)

Redaction Logic (src/middleware/redactor.ts:34-96):

  • Comprehensive cloud key patterns (AWS, GCP, GitHub, Stripe, Anthropic, etc.) — good coverage
  • Cloud keys matched before generic SECRET pattern to prevent partial token corruption ✓
  • Email regex is ReDoS-safe (no nested quantifiers) ✓
  • Sensitive key detection in JSON (line 114) catches common cases

Streaming (src/vault/token-vault.ts:87-225):

  • Stateful rehydrator handles tokens split across SSE chunks ✓
  • Backpressure handled in src/index.ts:143-145
  • Buffer capped at 1MB per field (line 93) — prevents unbounded growth ✓

Security:

  • Localhost bind by default (src/index.ts:28) ✓
  • Optional HUSH_AUTH_TOKEN with flexible header formats ✓
  • Plugin blocks sensitive file reads (src/plugins/sensitive-patterns.ts:15-21) ✓

Reliability:

  • 30s upstream timeout (src/index.ts:108) ✓
  • Graceful 502 on forwarding failure ✓

⚠️ Minor observations (not blockers)

  1. IP regex false positives (src/middleware/redactor.ts:38): The IPv4 pattern matches any X.X.X.X including version strings like 1.2.3.4. Consider context-aware matching for lower false-positive rate. Low priority.

  2. Generic SECRET pattern (line 42): [a-zA-Z0-9\-_!@#$%^&*()=+]{16,} may miss secrets with spaces or Unicode. Adequate for common cases.


Verdict

Approve ✅ — Solid workflow integration. The underlying hush codebase has robust redaction, proper streaming handling, and defense-in-depth security.

Reviewed SHA: f387a14

…-sent

Two bugs found in CI hush proxy logs:

1. ERR_HTTP_HEADERS_SENT: When a streaming response fails mid-stream,
   the catch block tried to send a 502 JSON error after SSE headers
   were already written. Now checks res.headersSent first.

2. TimeoutError after 30s: LLM code review calls can take well over
   30s. Bump AbortSignal.timeout from 30s to 120s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 2, 2026

Code Review: PR #34

Overview

This PR restores hush gateway proxy routing for OpenCode AI reviews and improves error handling for long-running streaming responses.


1. Redaction Logic ✓

Strengths:

  • ReDoS-safe email regex with explicit quantifier limits ({1,64} local, {1,253} domain)
  • Cloud provider key patterns use word boundaries and are processed before generic SECRET patterns to avoid partial matches
  • Deep traversal handles nested JSON in tool outputs correctly
  • Sensitive key detection via SENSITIVE_KEYS array catches credentials in both key names and values

Minor concern: IPv4 pattern \b(?:\d{1,3}\.){3}\d{1,3}\b matches invalid octets (e.g., 999.999.999.999). Consider adding octet validation if strict IP matching is needed, though this doesn't create security issues.


2. Streaming Integrity ✓

Strengths:

  • Stateful rehydrator (createStreamingRehydrator) correctly handles tokens split across SSE chunks
  • Dual-mode handling: raw text mode holds back potential partial tokens; SSE mode parses and accumulates content fields
  • Content buffers capped at 1MB (MAX_BUFFER_SIZE) prevents unbounded memory growth
  • Smart partial token detection distinguishes [HUSH_ tokens from JSON array brackets using uppercase/underscore check (/^\[[A-Z_]/)
  • Proper backpressure handling with res.once('drain') pattern at src/index.ts:144

3. Security ✓

Strengths:

  • Vault stores tokens in-memory only with TTL-based pruning (1 hour default)
  • Uses structuredClone for safe deep copies during rehydration
  • Rehydration via split().join() avoids regex special character issues
  • The headersSent check at src/index.ts:162-166 prevents crashes when streaming is in progress
  • Auth tokens properly passed through without logging

No PII leak vectors identified.


4. Reliability ✓

Changes reviewed:

  • Timeout increase (30s → 120s): Appropriate for long LLM reasoning responses
  • Error handling improvement (src/index.ts:160-168): Gracefully handles mid-stream failures by checking headersSent before attempting to send error response
if (!res.headersSent) {
  res.status(502).json({ error: 'Gateway forwarding failed' });
} else {
  res.end(); // Headers already sent — just end the response
}

This prevents the "Can't set headers after they are sent" error during streaming failures.


Workflow Changes

The CI workflow correctly:

  • Builds hush from source before starting
  • Uses health check polling with reasonable timeout (20 × 0.5s = 10s)
  • Verifies proxy was actually used via log grep
  • Passes explicit $PR_NUMBER to avoid auto-detection issues

Summary

Approval: ✅ The changes are well-designed and address the streaming edge cases properly.

Recommendations (optional, non-blocking):

  1. Consider IPv4 octet validation: /\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b/
  2. The streaming rehydrator's raw text mode could optimize prefix checking with a Trie for large vault sizes

Reviewed SHA: 3ea8a7e

@byapparov byapparov merged commit 922bc88 into master Mar 3, 2026
8 checks passed
@byapparov byapparov deleted the fix/simplify-opencode-provider-config branch March 3, 2026 10:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant