Skip to content

refactor(table names): handle unsafe table names - #174

Merged
EricAndrechek merged 9 commits into
mainfrom
safe-table-names
May 24, 2026
Merged

refactor(table names): handle unsafe table names#174
EricAndrechek merged 9 commits into
mainfrom
safe-table-names

Conversation

@EricAndrechek

@EricAndrechek EricAndrechek commented May 21, 2026

Copy link
Copy Markdown
Member

This PR changes parts of our codebase to use the ClickHouse HTTP client (part of #171) in order to allow ANY legal ClickHouse table names (so basically anything) to use query parameters so that even clear SQL injections as a table name are allowed. Additionally, we change how table names are stored to be safe for NATS internally.

Due to the changes above, test coverage dropped below the threshold, so significant portions of this PR are to get it to the threshold again.

Related Issues

Closes #151, #167

…mes are mostly safe now (need to look again with fresh eyes) and test coverage is back up to required min
@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 15b36f65-d47a-4da3-9cb1-656ff29e22af

📥 Commits

Reviewing files that changed from the base of the PR and between 191116a and ff8399b.

📒 Files selected for processing (3)
  • docs/src/content/docs/api.md
  • internal/query/builder.go
  • internal/query/builder_test.go
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use Go interfaces for core behaviors (Cache, Deduplicator, Publisher, Subscriber) to enable interface-first design with multiple implementations for standalone and clustered modes
Apply gofumpt strict formatting enforced by CI
Use structured logging with log/slog (JSON handler) instead of other logging approaches
Return errors and wrap with fmt.Errorf("context: %w", err) instead of panicking
Do not use global state; pass dependencies explicitly through constructor injection
Use lowercase, single-word (or abbreviated) package names in Go
Every new function should have corresponding test cases
Aim for 80%+ coverage on new code; project-wide CI enforces 80% minimum via merged unit + integration + e2e profiles
Go version 1.26 with strict gofumpt formatting enforced by CI; use golangci-lint v2.11.4 pinned in Makefile; most dev tools pinned in go.mod via tool directives

Files:

  • internal/query/builder_test.go
  • internal/query/builder.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Use table-driven tests with t.Run(tt.name, ...) for test cases in Go
Use shared mocks from internal/testutil/ (MockPublisher, MockCache, MockDeduplicator, MockSubscriber) instead of creating ad-hoc mocks
Use testutil.NewTestSchemaRegistry(tables) or discovery.NewSchemaRegistryFromMap(tables) for schema-aware tests

Files:

  • internal/query/builder_test.go
internal/query/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Structured queries (POST /v1/tables/{table}/query) are type-safe query AST validated against schema, with permission enforcement, timestamp bucketing for cache optimization, and 10,000 row DefaultMaxRows limit cap

Files:

  • internal/query/builder_test.go
  • internal/query/builder.go
🔇 Additional comments (3)
internal/query/builder_test.go (1)

349-351: LGTM!

Also applies to: 358-359, 367-368, 375-376, 384-384

internal/query/builder.go (1)

89-90: LGTM!

Also applies to: 177-178

docs/src/content/docs/api.md (1)

246-246: LGTM!


📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Deduplication enabled by default with configurable dedupe key.
    • Identifier encoding/decoding to support special-character table names.
    • SSE client accepts an optional auth token provider and improved connection/error signaling.
  • Bug Fixes

    • Relaxed table-name validation with safer downstream handling and parameterized DB writes.
    • Query builder now validates filters/columns and returns errors for invalid inputs.
    • Improved JSON error assertions in tests.
  • API Changes

    • Ingest, schema, and structured-query endpoints use ?table=... query parameter.
  • Tests

    • Extended unit, integration, and e2e suites (dedupe, ingest/query, streaming).
  • Chores

    • Local dev config bootstrapping, test timeout tuning, and installer messaging improved.

Walkthrough

Moves table selection to ?table= query parameters, adds percent-encoding helpers for table identifiers, encodes NATS/DLQ subjects, parameterizes ClickHouse inserts, removes regex validation, tightens query-builder error handling, and updates clients, tests, docs, and infra.

Changes

Table-Name Safety and API Endpoint Refactor

Layer / File(s) Summary
Identifier encoding/decoding
internal/query/ident.go, internal/query/ident_test.go
Adds EncodeTable/DecodeTable for percent-encoding table identifiers and unit tests verifying encoding, decoding, and round-trip correctness.
Router & handler migration
internal/api/router.go, internal/api/ingest.go, internal/api/schema.go, internal/api/structured_query.go
Endpoints and handlers switched from path-based table params to ?table= query parameters; chi path-param usage removed; schema returns list when table empty.
NATS subject & stream handlers
internal/api/stream_sse.go, internal/api/stream_ws.go, internal/api/ingest.go
SSE/WS/ingest use query.EncodeTable(table) for NATS subjects; regex-based validTableNameRe removed and validation relaxed to only empty-table semantics for subscription control.
Bento ingest: ClickHouse & DLQ
internal/ingest/bento.go, internal/ingest/bento_test.go
Removes local regex validation; DLQ and ingest/JetStream subjects use encoded table names; ClickHouse writes parameterize the target table via param_target_table and INSERT INTO {target_table:Identifier} template; tests updated to assert parameterization and DLQ payloads.
SQL builder & tests
internal/query/builder.go, internal/query/builder_test.go
Table identifiers are backtick-quoted with embedded-backtick escaping; WHERE/filter pipeline now returns errors for invalid operators/values and propagates to Build; tests updated and expanded for quoting and error cases.
Test utilities & test updates
internal/testutil/testutil.go, many internal/api/*_test.go files
Adds AssertJSONErrorResponse, AssertBodyContains, AssertBodyEquals; consolidates JSON error assertions across tests and migrates tests to query-param request forms.
TypeScript client & stream changes
clients/ts/src/*
Client fetch/insert/schema use ?table=; SSE transport accepts optional auth token callback and injects token as token query param; connect/disconnect/error handling refactored.
E2E & integration tests
tests/e2e/*, tests/integration/*
E2E suites expanded for dedupe, special-character table names, injection-like names, reserved-field validation, policy checks, and max_rows; DLQ integration tests updated to use encoded subjects and include a bad-name test.
Infra & docs
Makefile, .gitignore, tests/e2e/compose.yaml, scripts/orchestrator/main.go, deployments/*, README.md, docs/*, go.mod, tests/e2e/sdk/package.json
Makefile bootstraps local config files and adds test timeout and tool install echo; local config files ignored; dedupe enabled in compose/orchestrator; Dockerfile labels added; docs and README switched to ?table= examples; go.mod and e2e package dev deps updated; coverage threshold adjusted.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • taitelee
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch safe-table-names
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch safe-table-names

@github-actions github-actions Bot added go Pull requests that update go code area/api HTTP handlers, routing, middleware area/ingest Ingest pipeline (Bento, batching, DLQ) area/query Structured query AST, SQL builder area/sdk TypeScript SDK (clients/ts/) area/infra CI, build, deploy, Docker, release labels May 21, 2026
@coderabbitai
coderabbitai Bot requested a review from taitelee May 21, 2026 03:12
@coderabbitai coderabbitai Bot added documentation Improvements or additions to documentation area/docs Documentation, site/, README labels May 21, 2026
@github-actions github-actions Bot removed documentation Improvements or additions to documentation area/docs Documentation, site/, README labels May 21, 2026
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors the system to support arbitrary ClickHouse table names by implementing a robust encoding mechanism for NATS subjects and switching to parameterized queries for ClickHouse inserts. These changes effectively mitigate SQL injection risks while allowing for flexible table naming conventions. Additionally, the test suite has been updated to ensure high coverage and verify the new safety measures.

Highlights

  • Table Name Encoding: Introduced internal/query/ident.go to provide safe encoding and decoding of table names for NATS subjects, allowing for arbitrary characters in table names.
  • API Refactoring: Updated API handlers (ingest, schema, stream_sse, stream_ws, structured_query) to use URL unescaping and the new table name encoding utility.
  • SQL Injection Prevention: Refactored ClickHouse ingestion to use parameterized queries, ensuring that table names are treated as literals rather than executable SQL.
  • Validation Logic: Removed restrictive regex-based table name validation in favor of robust encoding, enabling support for complex table naming conventions.
  • Test Suite Expansion: Added comprehensive end-to-end and integration tests to verify handling of special characters, malicious table names, and overall system stability.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (6)
internal/query/builder_test.go (1)

26-40: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Missing test coverage for backtick escaping in table names.

The Build function now escapes backticks via strings.ReplaceAll(table, "", "``")`, but there's no test verifying this escaping works correctly. Add a test case for table names containing backticks to ensure the double-backtick escaping produces valid SQL.

✅ Proposed test case
func TestBuild_TableNameWithBacktick(t *testing.T) {
	t.Parallel()
	sq := &StructuredQuery{Columns: []string{"page"}, Limit: 10}
	result, err := Build("my`table", sq, testSchema(), 0)
	require.NoError(t, err)
	assert.Equal(t, "SELECT page FROM `my``table` LIMIT 10", result.SQL)
}

Note: You'll need to adjust testSchema() or create a schema that matches the table name being tested, or the test will need to use a schema with a matching Name field if that's validated.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/query/builder_test.go` around lines 26 - 40, Add a unit test that
verifies Build correctly escapes backticks in table names: create
TestBuild_TableNameWithBacktick which calls Build("my`table", sq, testSchema(),
0) with a simple StructuredQuery (e.g., Columns: []string{"page"}, Limit: 10),
require no error and assert the SQL equals "SELECT page FROM `my``table` LIMIT
10"; ensure the testSchema used contains a matching table Name or adjust/create
a test schema entry so Build accepts the table name.
internal/ingest/bento_test.go (1)

210-240: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Test name is now misleading: rename to reflect new pass-through behavior.

The function is named TestJsInput_Read_UnsafeTableNameDropped but the test now asserts the opposite—that unsafe table names are not dropped (assert.False(t, badMsg.doubleAcked)). Rename to something like TestJsInput_Read_UnsafeTableNamePassesThrough or TestJsInput_Read_UnsafeTableNameNotDropped to accurately describe the tested behavior.

♻️ Suggested rename
-func TestJsInput_Read_UnsafeTableNameDropped(t *testing.T) {
+func TestJsInput_Read_UnsafeTableNamePassesThrough(t *testing.T) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/ingest/bento_test.go` around lines 210 - 240, Rename the test
function TestJsInput_Read_UnsafeTableNameDropped to reflect the new pass-through
behavior (e.g., TestJsInput_Read_UnsafeTableNamePassesThrough or
TestJsInput_Read_UnsafeTableNameNotDropped) and update any references or
comments; ensure the function signature (func
TestJsInput_Read_UnsafeTableNamePassesThrough(t *testing.T)) and the file's test
mentions match the new name, and keep assertions around jsInput.Read,
badMsg.doubleAcked, goodMsg.doubleAcked, and MetaGet checks unchanged.
internal/ingest/bento.go (1)

86-94: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Issue #151 requirement not fully addressed: empty-table-name rejections still bypass DLQ.

The PR claims to close Issue #151, which explicitly requires routing empty-table-name rejections through the DLQ (subject dlq.__rejected__) with an x-wh-drop-reason header. This code path still logs and DoubleAcks without publishing to the DLQ stream.

Per the issue objectives: "Route both unsafe-table-name and empty-table-name rejection paths through the existing DLQ."

🔧 Suggested approach
+	const dlqRejectedSubject = "dlq.__rejected__"
+
 	// Reject messages with no table name.
 	if raw.TableName == "" {
 		slog.ErrorContext(msgCtx, "rejecting message: empty table_name")
+		// Route to DLQ per Issue `#151` requirements
+		if _, pubErr := js.Publish(msgCtx, dlqRejectedSubject, m.Data(),
+			jetstream.WithMsgHeader("x-wh-drop-reason", "empty-table-name")); pubErr != nil {
+			slog.ErrorContext(msgCtx, "DLQ publish failed for empty table_name", "error", pubErr)
+		}
 		if doubleAckErr := m.DoubleAck(msgCtx); doubleAckErr != nil {
 			slog.WarnContext(msgCtx, "double ack failed for dropped message", "error", doubleAckErr)
 		}
 		continue
 	}

Note: This requires access to the JetStream context within jsInput.Read. You may need to inject js as a field on jsInput or restructure the rejection path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/ingest/bento.go` around lines 86 - 94, The empty-table-name branch
in jsInput.Read currently logs and DoubleAcks (raw.TableName == "" ->
slog.ErrorContext + m.DoubleAck) but does not publish to the DLQ; change this to
route the rejection through the same DLQ used for unsafe-table-name by
publishing the message to subject "dlq.__rejected__" with header
"x-wh-drop-reason" set to an appropriate reason, then DoubleAck; to do this,
ensure the jsInput struct has access to the JetStream context (add a js field or
otherwise pass js into jsInput.Read), use that js context to PublishMsg/Publish
with headers for the DLQ, and replace the current slog.ErrorContext +
m.DoubleAck-only flow in the raw.TableName == "" branch with the DLQ publish +
DoubleAck sequence mirroring the unsafe-table-name handling.
tests/integration/dlq_test.go (1)

68-68: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use query.EncodeTable for DLQ subject construction.

Line 62 correctly uses query.EncodeTable(table) for the ingest subject, but line 68 constructs the DLQ subject using the raw table name. According to the PR objectives and review stack context, NATS subjects (including DLQ subjects) should use encoded table names for consistency.

🔧 Proposed fix
-	dlqSubject := "dlq." + table
+	dlqSubject := "dlq." + query.EncodeTable(table)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/integration/dlq_test.go` at line 68, The DLQ subject is built from the
raw table name (dlqSubject := "dlq." + table) but should use the encoded table
like the ingest subject; update the dlqSubject construction to use
query.EncodeTable(table) (i.e., prefix "dlq." to the encoded table value) so DLQ
subjects consistently match the ingest subject encoding.
internal/api/stream_test.go (2)

207-207: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Use assertJSONErrorResponse for internal API error-path assertions.

These error-path checks currently use testutil.AssertJSONContains; the package-local helper provides stronger header assertions for API errors.

Based on learnings: “In WaveHouse tests under internal/api/**/*_test.go, use the package-local helper assertJSONErrorResponse(t, w) for HTTP error-path assertions… it also asserts Content-Type and X-Content-Type-Options headers.”

Also applies to: 243-243

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/api/stream_test.go` at line 207, Replace the error-path assertions
that call testutil.AssertJSONContains(t, w, http.StatusBadRequest, ...) with the
package-local helper assertJSONErrorResponse(t, w); specifically locate usages
of testutil.AssertJSONContains in internal/api/stream_test.go (e.g., the call
with map[string]any{"error": tc.errBody} and the other occurrence around the
noted range) and change them to assertJSONErrorResponse(t, w) so the test also
asserts Content-Type and X-Content-Type-Options headers for API error responses.

195-195: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

“empty after url decode” case is not testing a decode-to-empty input.

Line 195 duplicates the missing-table scenario (table == "") instead of a percent-encoded value that decodes to empty/invalid. This weakens the regression test intent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/api/stream_test.go` at line 195, Replace the duplicated test case
that currently uses an empty string for the "empty after url decode" case with a
percent-encoded value that decodes to an empty/invalid value (for example "%00"
or another encoding your URL decoder treats as empty) so the test actually
verifies the decode-to-empty scenario; update the test case input (the second
element of the tuple) from "" to the chosen percent-encoded string and keep the
expected error string ("missing required query parameter: table") or adjust it
if your decoder returns a different error for decoded-empty values, ensuring the
tuple with description "empty after url decode" is the one changed rather than
duplicating the plain-empty case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/api/ingest.go`:
- Around line 40-42: url.PathUnescape errors are currently ignored causing
malformed percent-encodings (e.g. "%ZZ") to be accepted; change the logic so
that when url.PathUnescape(table) returns an error you respond with HTTP 400 and
return immediately (following the same error-handling pattern used elsewhere in
this handler), e.g. check the error from url.PathUnescape and call the handler's
bad-request response path using the same mechanism as other checks instead of
falling back to the raw table value.

In `@internal/api/stream_ws.go`:
- Around line 54-58: The handler currently rejects connections when
r.URL.Query().Get("table") == "" which conflates a missing table parameter
(should be allowed) with an explicit empty value (?table=) (should be rejected);
change the check to inspect presence via r.URL.Query() so: detect whether the
"table" key exists and, if it exists, verify its first value is non-empty,
calling writeJSONError(w, http.StatusBadRequest, "invalid table name") only for
the explicit-empty case; allow the flow to continue when the "table" key is
absent so clients can connect with no initial subscriptions.

In `@tests/e2e/sdk/ingest.test.ts`:
- Around line 317-321: The test currently comments out assertions for the policy
rejection (variable badRes in tests/e2e/sdk/ingest.test.ts), leaving the
rejection path unverified; restore the assertion lines
(expect(badRes.error).not.toBeNull(); expect(badRes.error!.status).toBe(403);)
and ensure the test setup enforces the policy requiring country=US so the
request with country=GB returns the expected error, or if the policy behavior is
still broken, mark the test as skipped (test.skip) and open a follow-up issue to
re-enable it later; target the badRes usage and the commented expect lines to
implement this change.

---

Outside diff comments:
In `@internal/api/stream_test.go`:
- Line 207: Replace the error-path assertions that call
testutil.AssertJSONContains(t, w, http.StatusBadRequest, ...) with the
package-local helper assertJSONErrorResponse(t, w); specifically locate usages
of testutil.AssertJSONContains in internal/api/stream_test.go (e.g., the call
with map[string]any{"error": tc.errBody} and the other occurrence around the
noted range) and change them to assertJSONErrorResponse(t, w) so the test also
asserts Content-Type and X-Content-Type-Options headers for API error responses.
- Line 195: Replace the duplicated test case that currently uses an empty string
for the "empty after url decode" case with a percent-encoded value that decodes
to an empty/invalid value (for example "%00" or another encoding your URL
decoder treats as empty) so the test actually verifies the decode-to-empty
scenario; update the test case input (the second element of the tuple) from ""
to the chosen percent-encoded string and keep the expected error string
("missing required query parameter: table") or adjust it if your decoder returns
a different error for decoded-empty values, ensuring the tuple with description
"empty after url decode" is the one changed rather than duplicating the
plain-empty case.

In `@internal/ingest/bento_test.go`:
- Around line 210-240: Rename the test function
TestJsInput_Read_UnsafeTableNameDropped to reflect the new pass-through behavior
(e.g., TestJsInput_Read_UnsafeTableNamePassesThrough or
TestJsInput_Read_UnsafeTableNameNotDropped) and update any references or
comments; ensure the function signature (func
TestJsInput_Read_UnsafeTableNamePassesThrough(t *testing.T)) and the file's test
mentions match the new name, and keep assertions around jsInput.Read,
badMsg.doubleAcked, goodMsg.doubleAcked, and MetaGet checks unchanged.

In `@internal/ingest/bento.go`:
- Around line 86-94: The empty-table-name branch in jsInput.Read currently logs
and DoubleAcks (raw.TableName == "" -> slog.ErrorContext + m.DoubleAck) but does
not publish to the DLQ; change this to route the rejection through the same DLQ
used for unsafe-table-name by publishing the message to subject
"dlq.__rejected__" with header "x-wh-drop-reason" set to an appropriate reason,
then DoubleAck; to do this, ensure the jsInput struct has access to the
JetStream context (add a js field or otherwise pass js into jsInput.Read), use
that js context to PublishMsg/Publish with headers for the DLQ, and replace the
current slog.ErrorContext + m.DoubleAck-only flow in the raw.TableName == ""
branch with the DLQ publish + DoubleAck sequence mirroring the unsafe-table-name
handling.

In `@internal/query/builder_test.go`:
- Around line 26-40: Add a unit test that verifies Build correctly escapes
backticks in table names: create TestBuild_TableNameWithBacktick which calls
Build("my`table", sq, testSchema(), 0) with a simple StructuredQuery (e.g.,
Columns: []string{"page"}, Limit: 10), require no error and assert the SQL
equals "SELECT page FROM `my``table` LIMIT 10"; ensure the testSchema used
contains a matching table Name or adjust/create a test schema entry so Build
accepts the table name.

In `@tests/integration/dlq_test.go`:
- Line 68: The DLQ subject is built from the raw table name (dlqSubject :=
"dlq." + table) but should use the encoded table like the ingest subject; update
the dlqSubject construction to use query.EncodeTable(table) (i.e., prefix "dlq."
to the encoded table value) so DLQ subjects consistently match the ingest
subject encoding.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7798eeb3-9051-4d8e-b247-5cc443b3ff87

📥 Commits

Reviewing files that changed from the base of the PR and between e07d5ed and c82d4da.

📒 Files selected for processing (18)
  • Makefile
  • internal/api/ingest.go
  • internal/api/schema.go
  • internal/api/stream_sse.go
  • internal/api/stream_test.go
  • internal/api/stream_ws.go
  • internal/api/structured_query.go
  • internal/ingest/bento.go
  • internal/ingest/bento_test.go
  • internal/query/builder.go
  • internal/query/builder_test.go
  • internal/query/ident.go
  • internal/testutil/testutil.go
  • scripts/orchestrator/main.go
  • tests/e2e/compose.yaml
  • tests/e2e/sdk/ingest.test.ts
  • tests/e2e/sdk/query.test.ts
  • tests/integration/dlq_test.go

Comment thread internal/api/ingest.go Outdated
Comment thread internal/api/stream_ws.go Outdated
Comment thread tests/e2e/sdk/ingest.test.ts Outdated
@github-project-automation github-project-automation Bot moved this from Backlog to In review in WaveHouse Task Board May 21, 2026

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request implements a more flexible table name handling strategy by replacing strict regex validation with percent-encoding for NATS subjects and utilizing ClickHouse's identifier parameterization for SQL queries. It also enables event deduplication by default and adds extensive E2E tests for policy enforcement and special characters. Review feedback identifies a regression where the optional table parameter in the WebSocket handler was incorrectly made mandatory and emphasizes the need to maintain regex-based validation for table identifiers to satisfy security requirements. Furthermore, the reviewer recommended expanding test coverage to ensure that both empty parameters and invalid identifiers are handled correctly.

Comment thread internal/api/stream_ws.go Outdated
Comment thread internal/api/stream_test.go Outdated
@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label May 22, 2026
@EricAndrechek
EricAndrechek marked this pull request as ready for review May 22, 2026 23:15
@EricAndrechek

Copy link
Copy Markdown
Member Author

/gemini review
/review

@coderabbitai coderabbitai Bot added documentation Improvements or additions to documentation area/docs Documentation, site/, README labels May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api HTTP handlers, routing, middleware area/docs Documentation, site/, README area/infra CI, build, deploy, Docker, release area/ingest Ingest pipeline (Bento, batching, DLQ) area/query Structured query AST, SQL builder area/sdk TypeScript SDK (clients/ts/) dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation go Pull requests that update go code

Projects

Archived in project

2 participants