Skip to content

fix: comprehensive audit — security, bugs, cleanup#9

Merged
GeiserX merged 2 commits intomainfrom
review/comprehensive-audit
Apr 16, 2026
Merged

fix: comprehensive audit — security, bugs, cleanup#9
GeiserX merged 2 commits intomainfrom
review/comprehensive-audit

Conversation

@GeiserX
Copy link
Copy Markdown
Owner

@GeiserX GeiserX commented Apr 16, 2026

Summary

Comprehensive codebase audit from 13 specialized subagents (security, architecture, code quality, tests, UI/UX, simplification, debugging, documentation, build analysis, verification, and more). This PR implements safe, non-breaking fixes plus the strategic items approved by the maintainer.

Security fixes

  • Redact API keys in request bodies — recursive redaction for nested JSON (api_key, token, secret, password, credentials, access_token)
  • Fix XSS in generateCurl — shellEscape for header values, body, URL, AND HTTP method
  • Forwarder port 443 restriction — reject non-443 upstream connections in native forwarder
  • Malformed CA cert handling — treat unparseable certs as expired to force regeneration

Bug fixes

  • Fix transparent proxy data loss race — buffer data during TLS bridge connect phase
  • Capture start/stop mutex on ALL entry points — tray clicks, RPC handlers, AND debug HTTP endpoints all serialized via enqueueCaptureTransition()
  • Helper orphan detection — auto-cleanup /etc/hosts + pf rules when app crashes (heartbeat probes port 8443 every 60s, cleans after 2 consecutive misses / 120s)
  • UI contrast — bump --text-muted to #8a8a8a, --text-dim to #6b6b6b, --border to #2e2e2e

Code cleanup

  • Extract shared binary-resolver.ts — deduplicate findProjectRoot/getAppContentsDir/shouldSearchProjectRoot from 3 files (native-bridge, helper-client, upstream-forwarder)
  • Remove dead code: getLogsSinceId, verifyCapture, unused running variable
  • Extract getCertExpiresAt helper to eliminate duplicated X509 parsing
  • Replace nested ternaries with switch/if-else for readability
  • Simplify getOptionalStringParam

Tests (5 → 108)

  • http-parser (20 tests): parseRequestHead, parseResponseHead, findHeaderEnd, dechunk
  • sni-parser (7 tests): extractSNI with valid/invalid/truncated ClientHello
  • cost-estimator (13 tests): null inputs, prefix matching, rounding
  • trace-utils (38 tests): header redaction, recursive body redaction, source app detection, URL sanitization
  • sse-reassembler (1 → 21 tests): Anthropic + OpenAI SSE parsing, token counting

Documentation

  • Fix AGENTS.md architecture diagram (ElectroBun, not SwiftUI; Unix socket, not XPC)
  • Fix traffic flow to describe current DNS-based interception
  • Update Tech Stack IPC bullet to reflect Unix socket + FFI dual mechanism
  • Add missing providers (deepseek, xai)

Deferred to future PRs

  • SQLite encryption — requires sqlcipher dependency or app-level AES-256-GCM; too invasive for this PR
  • Socket shared secret — current getpeereid + console UID check is already solid security

Test plan

  • bun test — 108 pass, 0 fail
  • TypeScript check — no new errors (all pre-existing ElectroBun/Bun FFI type issues)
  • C forwarder compiles cleanly (clang -fsyntax-only)
  • Manual: verify capture start/stop still works
  • Manual: verify orphan cleanup fires after simulated crash

Security:
- Redact API keys in request bodies (api_key, token, secret, etc.)
- Fix XSS in generateCurl: use shellEscape for header keys and body

Bug fixes:
- Fix transparent proxy data loss race: buffer data during TLS bridge connect
- Add capture start/stop mutex to prevent concurrent state mutations
- Fix UI contrast: bump --text-muted to #8a8a8a, --text-dim to #6b6b6b, --border to #2e2e2e

Code cleanup:
- Remove dead code: getLogsSinceId, verifyCapture, unused `running` variable
- Extract getCertExpiresAt helper to eliminate duplicated X509 parsing
- Replace nested ternaries with switch/if-else for readability
- Remove unnecessary async/await in canConnectToForwarder
- Simplify getOptionalStringParam

Documentation:
- Fix AGENTS.md architecture diagram (ElectroBun, not SwiftUI; Unix socket, not XPC)
- Fix AGENTS.md traffic flow to describe current DNS-based interception
- Add missing providers (deepseek, xai) to AGENTS.md
- Update component descriptions to match actual implementation
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 16, 2026

Warning

Rate limit exceeded

@GeiserX has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 14 minutes and 54 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 14 minutes and 54 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d2d1ce9e-b6fa-46d5-b542-722c4f2079c2

📥 Commits

Reviewing files that changed from the base of the PR and between 2ddd03b and 5079f18.

📒 Files selected for processing (17)
  • AGENTS.md
  • native/Forwarder/agenttap-fwd.c
  • native/Helper/HelperTool.swift
  • native/Helper/main.swift
  • src/bun/ca/ca-manager.ts
  • src/bun/index.ts
  • src/bun/native-bridge.ts
  • src/bun/proxy/http-parser.test.ts
  • src/bun/proxy/sni-parser.test.ts
  • src/bun/proxy/upstream-forwarder.ts
  • src/bun/sse/sse-reassembler.test.ts
  • src/bun/storage/cost-estimator.test.ts
  • src/bun/storage/trace-utils.test.ts
  • src/bun/storage/trace-utils.ts
  • src/bun/storage/trace-writer.ts
  • src/bun/system/binary-resolver.ts
  • src/bun/system/helper-client.ts
📝 Walkthrough

Walkthrough

Documentation updated to reflect Bun/ElectroBun menu bar app with Unix socket IPC and JSON protocol. DNS interception refactored to use /etc/hosts sentinel IPs and single PF rule on loopback. Proxy forwarding now extracts real domain via TLS SNI, resolves via DoH, and forwards with IP_BOUND_IF. Code changes include certificate expiration refactoring, tray UI and capture state improvements, data buffering in transparent proxy, request body redaction, and removal of unused exported helper functions.

Changes

Cohort / File(s) Summary
Documentation
AGENTS.md
Architecture updated to Bun/ElectroBun with Unix socket IPC, DNS-based interception via /etc/hosts sentinel IPs, DoH-based proxy forwarding with native forwarder. Added deepseek and xai provider domains.
Certificate Management
src/bun/ca/ca-manager.ts
Consolidated certificate regeneration logic by combining expired/expiring checks. Introduced shared getCertExpiresAt() helper to extract certificate validTo timestamp, with parse-failure handling updated across isCertExpired() and isCertExpiringSoon().
Bun App & Tray UI
src/bun/index.ts
Replaced nested ternaries in updateTrayIndicator() with switch statement. Refactored tray menu label creation to if/else ladder. Added captureTransitionLock promise to serialize rapid start/stop clicks. Updated cURL generation to use shellEscape() for headers/body instead of manual quoting. Simplified getOptionalStringParam() logic.
Proxy & Connection Handling
src/bun/proxy/transparent-proxy.ts, src/bun/proxy/proxy-server.ts, src/bun/proxy/upstream-forwarder.ts
Transparent proxy now buffers incoming chunks during net.connect phase and flushes them post-handshake; error path also removes temporary listener. Proxy server replaced ternary with explicit if/else for bodyStr assignment. Upstream forwarder removed unnecessary async keyword from canConnectToForwarder().
Storage, Logging & State Management
src/bun/storage/trace-writer.ts, src/bun/storage/write-batcher.ts, src/bun/system/log-manager.ts, src/bun/system/redirect-manager.ts
Added request body redaction via redactRequestBody() helper targeting BODY_SENSITIVE_KEYS. Removed internal running flag from write-batcher. Removed exported convenience functions getLogsSinceId() and verifyCapture(); callers must use getLogs() and verifyCaptureDetailed() directly.
UI Theming
src/views/mainview/index.css
Updated CSS custom properties: --border (#262626#2e2e2e), --text-muted (#737373#8a8a8a), --text-dim (#525252#6b6b6b).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • PR #3 — Modifies transparent-proxy, proxy-server, and redirect-manager for transparent-capture plumbing changes.
  • PR #1 — Updates src/bun/index.ts tray logic and provider domain lists.
  • PR #7 — Overlaps on multiple modules including index.ts, proxy handlers, write-batcher, trace-writer, log-manager, and redirect-manager API changes.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title "fix: comprehensive audit — security, bugs, cleanup" directly aligns with the changeset's main focus: security fixes (request body redaction, shell injection), bug fixes (data buffering, race condition serialization, CSS contrast), and code cleanup (dead code removal, helper extraction, ternary refactoring).
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch review/comprehensive-audit

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/bun/index.ts (1)

382-395: ⚠️ Potential issue | 🟠 Major

curl -X is still injectable via the request method.

url, headers, and body are escaped now, but method is still interpolated raw into the shell command. A crafted method token like POST$(...) would survive into the copied command. ``

Proposed fix
-  let cmd = `curl -X ${method} ${shellEscape(url)}`;
+  let cmd = `curl -X ${shellEscape(method)} ${shellEscape(url)}`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/bun/index.ts` around lines 382 - 395, The generateCurl function
interpolates trace.request_method into the shell command raw, allowing command
injection via crafted methods; fix it by normalizing and validating method
before use (e.g., read method from trace.request_method, uppercase it, then
check against an explicit whitelist like GET, POST, PUT, PATCH, DELETE, HEAD,
OPTIONS and fallback to GET or omit -X for invalid values) so only safe HTTP
verbs are allowed; reference the method variable in generateCurl and replace the
direct interpolation with the validated/whitelisted method (or remove -X when
using the default GET) rather than inserting the raw value.
🧹 Nitpick comments (1)
src/views/mainview/index.css (1)

6-10: LGTM — Contrast improvements look solid.

The lightened values for --border, --text-muted, and --text-dim should improve readability against the dark background. Hierarchy is maintained.

If you want to verify accessibility compliance, consider checking WCAG contrast ratios:

🔍 Optional verification script
#!/bin/bash
# Verify WCAG contrast ratios for the updated colors

python3 << 'EOF'
def hex_to_rgb(hex_color):
    hex_color = hex_color.lstrip('#')
    return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))

def relative_luminance(rgb):
    r, g, b = [x / 255.0 for x in rgb]
    r = r / 12.92 if r <= 0.03928 else ((r + 0.055) / 1.055) ** 2.4
    g = g / 12.92 if g <= 0.03928 else ((g + 0.055) / 1.055) ** 2.4
    b = b / 12.92 if b <= 0.03928 else ((b + 0.055) / 1.055) ** 2.4
    return 0.2126 * r + 0.7152 * g + 0.0722 * b

def contrast_ratio(hex1, hex2):
    lum1 = relative_luminance(hex_to_rgb(hex1))
    lum2 = relative_luminance(hex_to_rgb(hex2))
    lighter = max(lum1, lum2)
    darker = min(lum1, lum2)
    return (lighter + 0.05) / (darker + 0.05)

bg = "#0a0a0a"
text_muted = "#8a8a8a"
text_dim = "#6b6b6b"
border = "#2e2e2e"

print(f"WCAG Contrast Ratios (AA requires 4.5:1 for normal text, 3:1 for large)")
print(f"--text-muted on --bg: {contrast_ratio(text_muted, bg):.2f}:1")
print(f"--text-dim on --bg: {contrast_ratio(text_dim, bg):.2f}:1")
print(f"--border on --bg: {contrast_ratio(border, bg):.2f}:1")
EOF
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/mainview/index.css` around lines 6 - 10, The CSS variable updates
(--border, --border-light, --text, --text-muted, --text-dim) look good and
require no code change; to verify accessibility, run the provided WCAG contrast
check (or add it to CI) using the updated variables (--text-muted, --text-dim,
--border) against your background color (e.g., --bg) and adjust any of those
variables if any contrast ratios fall below WCAG thresholds.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@AGENTS.md`:
- Around line 12-29: The Tech Stack still claims IPC is via "Swift FFI" which
conflicts with the new Unix-socket privileged helper described earlier; update
the Tech Stack IPC bullet to reflect the actual mechanism: replace the "Swift
FFI" IPC mention with "Privileged Helper via Unix domain socket (JSON line
protocol)" or clarify that Swift FFI is only used for local bindings while IPC
to the privileged helper uses a Unix socket, so the documentation is consistent
with the Privileged Helper/Unix-socket IPC described in the components.

In `@src/bun/ca/ca-manager.ts`:
- Around line 35-36: ensureCA() is currently accepting malformed/failed-to-parse
PEMs because parse failures return null and downstream checks
(isCertExpired/isCertExpiringSoon) treat that as false; update the logic so that
a parse failure is treated as invalid and triggers regeneration. Specifically,
when you call the cert parsing function (the code path that returns null on
failure), if the result is null treat it the same as "expired" (i.e., force
regeneration) and do not assign unusable material to the loaded variable;
alternatively, make isCertExpired and isCertExpiringSoon return true for null
inputs so ensureCA() will regenerate. Ensure loaded is only set after a
successful parse and validation.

In `@src/bun/index.ts`:
- Around line 194-195: The capture mutex (captureTransitionLock) is only used
for tray clicks; fix all entry points that call doStartCapture() or
doStopCapture() (including the viewer RPC handlers and the debug endpoints) to
serialize transitions by chaining calls onto captureTransitionLock instead of
calling doStartCapture()/doStopCapture() directly; replace direct calls with
captureTransitionLock = captureTransitionLock.then(() => doStartCapture() /* or
doStopCapture() */).catch(()=>{}); return/await that chained promise so callers
observe completion and errors are propagated. Ensure every location that
currently invokes doStartCapture()/doStopCapture() (e.g., viewer RPC handlers
and debug endpoints) is updated to use this pattern.

In `@src/bun/storage/trace-writer.ts`:
- Around line 48-61: The redactRequestBody function only removes sensitive keys
at the top level; update it to recursively traverse the parsed JSON (objects and
arrays) and replace any property whose key matches an entry in
BODY_SENSITIVE_KEYS with REDACTED. Implement a helper (e.g., walkAndRedact or
recurse) that walks objects and arrays, checks keys against BODY_SENSITIVE_KEYS,
and mutates values to REDACTED when matched, then use that helper on the parsed
result before JSON.stringify; keep the try/catch and non-JSON fallback behavior
intact.

---

Outside diff comments:
In `@src/bun/index.ts`:
- Around line 382-395: The generateCurl function interpolates
trace.request_method into the shell command raw, allowing command injection via
crafted methods; fix it by normalizing and validating method before use (e.g.,
read method from trace.request_method, uppercase it, then check against an
explicit whitelist like GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS and
fallback to GET or omit -X for invalid values) so only safe HTTP verbs are
allowed; reference the method variable in generateCurl and replace the direct
interpolation with the validated/whitelisted method (or remove -X when using the
default GET) rather than inserting the raw value.

---

Nitpick comments:
In `@src/views/mainview/index.css`:
- Around line 6-10: The CSS variable updates (--border, --border-light, --text,
--text-muted, --text-dim) look good and require no code change; to verify
accessibility, run the provided WCAG contrast check (or add it to CI) using the
updated variables (--text-muted, --text-dim, --border) against your background
color (e.g., --bg) and adjust any of those variables if any contrast ratios fall
below WCAG thresholds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a09ce436-7949-49ec-a6e9-a04f80c12566

📥 Commits

Reviewing files that changed from the base of the PR and between 3bb2a1e and 2ddd03b.

📒 Files selected for processing (11)
  • AGENTS.md
  • src/bun/ca/ca-manager.ts
  • src/bun/index.ts
  • src/bun/proxy/proxy-server.ts
  • src/bun/proxy/transparent-proxy.ts
  • src/bun/proxy/upstream-forwarder.ts
  • src/bun/storage/trace-writer.ts
  • src/bun/storage/write-batcher.ts
  • src/bun/system/log-manager.ts
  • src/bun/system/redirect-manager.ts
  • src/views/mainview/index.css
💤 Files with no reviewable changes (3)
  • src/bun/storage/write-batcher.ts
  • src/bun/system/log-manager.ts
  • src/bun/system/redirect-manager.ts

Comment thread AGENTS.md
Comment thread src/bun/ca/ca-manager.ts
Comment thread src/bun/index.ts
Comment thread src/bun/storage/trace-writer.ts Outdated
Comment on lines +48 to +61
const BODY_SENSITIVE_KEYS = ["api_key", "apiKey", "secret", "token", "password", "credentials", "access_token"];

function redactRequestBody(body: string | null): string | null {
if (!body) return null;
try {
const parsed = JSON.parse(body);
for (const key of BODY_SENSITIVE_KEYS) {
if (parsed[key]) parsed[key] = REDACTED;
}
return JSON.stringify(parsed);
} catch {
return body; // non-JSON body, return as-is
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Redact nested JSON secrets, not just top-level fields.

This helper only checks direct properties on the parsed root object. Bodies like {"auth":{"api_key":"..."}} or arrays of credential objects will still be stored with secrets intact, so the storage redaction is incomplete. ``

Proposed fix
-const BODY_SENSITIVE_KEYS = ["api_key", "apiKey", "secret", "token", "password", "credentials", "access_token"];
+const BODY_SENSITIVE_KEYS = new Set([
+  "api_key",
+  "apikey",
+  "secret",
+  "token",
+  "password",
+  "credentials",
+  "access_token",
+]);

+function redactJsonSecrets(value: unknown): unknown {
+  if (Array.isArray(value)) {
+    return value.map(redactJsonSecrets);
+  }
+
+  if (value && typeof value === "object") {
+    return Object.fromEntries(
+      Object.entries(value as Record<string, unknown>).map(([key, child]) => [
+        key,
+        BODY_SENSITIVE_KEYS.has(key.toLowerCase()) ? REDACTED : redactJsonSecrets(child),
+      ]),
+    );
+  }
+
+  return value;
+}
+
 function redactRequestBody(body: string | null): string | null {
   if (!body) return null;
   try {
     const parsed = JSON.parse(body);
-    for (const key of BODY_SENSITIVE_KEYS) {
-      if (parsed[key]) parsed[key] = REDACTED;
-    }
-    return JSON.stringify(parsed);
+    return JSON.stringify(redactJsonSecrets(parsed));
   } catch {
     return body; // non-JSON body, return as-is
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/bun/storage/trace-writer.ts` around lines 48 - 61, The redactRequestBody
function only removes sensitive keys at the top level; update it to recursively
traverse the parsed JSON (objects and arrays) and replace any property whose key
matches an entry in BODY_SENSITIVE_KEYS with REDACTED. Implement a helper (e.g.,
walkAndRedact or recurse) that walks objects and arrays, checks keys against
BODY_SENSITIVE_KEYS, and mutates values to REDACTED when matched, then use that
helper on the parsed result before JSON.stringify; keep the try/catch and
non-JSON fallback behavior intact.

…ests

CodeRabbit findings (all 5):
- Fix curl method injection: shellEscape the HTTP method in generateCurl
- Fix malformed CA cert handling: treat unparseable certs as expired
- Apply captureTransitionLock to ALL entry points (RPC + debug server)
- Recursive JSON secret redaction for nested request body fields
- Update AGENTS.md Tech Stack IPC bullet to reflect Unix socket + FFI

Strategic items:
- Helper orphan detection: auto-cleanup /etc/hosts + pf rules when app
  crashes (heartbeat timer probes port 8443 every 60s, cleans after 2 misses)
- Forwarder port 443 restriction: reject non-443 upstream connections
- Extract duplicated findProjectRoot/getAppContentsDir to shared
  binary-resolver.ts module (was copy-pasted in 3 files)

Tests (5 → 108):
- New: http-parser (20), sni-parser (7), cost-estimator (13), trace-utils (38)
- Expanded: sse-reassembler (1 → 21)
@GeiserX
Copy link
Copy Markdown
Owner Author

GeiserX commented Apr 16, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@GeiserX GeiserX merged commit a817f94 into main Apr 16, 2026
6 checks passed
@GeiserX GeiserX deleted the review/comprehensive-audit branch April 16, 2026 20:46
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