feat(webapp): query boundary pinned end-to-end and a capped query retry - #4549
feat(webapp): query boundary pinned end-to-end and a capped query retry#4549kathiekiwi wants to merge 17 commits into
Conversation
The read-only guard was enforced by the TRQL grammar and a parser test, but nothing proved the route itself refuses a write; a route test now drives api.v1.query with a signed environment JWT and asserts nothing reaches ClickHouse. readonly=1 is no longer overridable by a caller's clickhouseSettings. run_query gives up after three consecutive failures so a broken query can't burn a whole agent turn. TRI-11165
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThe webapp now rejects mutating query statements and enforces 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…65' into feat/query-safety-tri-11165
@trigger.dev/build
trigger.dev
@trigger.dev/core
@trigger.dev/python
@trigger.dev/react-hooks
@trigger.dev/redis-worker
@trigger.dev/rsc
@trigger.dev/schema-to-json
@trigger.dev/sdk
commit: |
…65' into feat/query-safety-tri-11165
…x/watch-mode-keepalive-tri-13065
| ...getDefaultClickhouseSettings(), | ||
| ...queryCacheSettings, | ||
| ...baseOptions.clickhouseSettings, // Allow caller overrides if needed | ||
| readonly: "1", // Not overridable: every query through here is read-only. |
There was a problem hiding this comment.
🔍 Forced readonly does not change the settings key order sent to ClickHouse
readonly: "1" is written last in the object literal, but getDefaultClickhouseSettings() (apps/webapp/app/services/queryService.server.ts:62) already defines readonly, so JavaScript keeps the key at its original insertion position and only overwrites the value. The serialized settings therefore still emit readonly before use_query_cache/query_cache_ttl (spread at apps/webapp/app/services/queryService.server.ts:381-383). ClickHouse applies HTTP settings in the order received and rejects changes once readonly=1 is in effect, so if the query-cache path works today it means ClickHouse tolerates the ordering here — worth confirming against a real query-cache-enabled table (e.g. one with queryCache in querySchemas) so the hardening does not silently rely on ordering luck. Note this ordering pre-dates the PR; the new line only makes caller overrides ineffective (no current caller overrides readonly, verified by searching clickhouseSettings usages in apps/webapp/app).
Was this helpful? React with 👍 or 👎 to provide feedback.
What & why
The agent's query tool is read-only, but that was true by three separate facts and only one of them had a test. This proves the boundary holds by contract rather than by prompt, and stops a broken query from burning a whole agent turn.
Two small guards, the rest is tests. TRI-11165.
Stack
Stacked on #4548 (watch-mode keepalive). Merge that first.
What's inside
apps/webapp/test/queryRouteReadOnly.test.ts) that drivesapi.v1.querywith a real signed environment JWT: a multi-statement write and a mutating statement are both refused before anything reaches ClickHouse, and a plain read passes so the seam stays live.readonly=1made non-overridable inqueryService.server.ts— callerclickhouseSettingswere spread after the defaults and could clear it.run_querytool (internal-packages/dashboard-agent/src/tool-api.ts): three consecutive failures returns a terminal "stop and answer with what you have".Key decisions
readonly=1, and the org/project/env scoping is injected server-side from the credential. The request body can't widen scope or turn a read into a write.Testing
queryRouteReadOnly.test.ts— write statements → 400, ClickHouse never called; a read passes.tool-query-retry-cap.test.ts— terminal at the third consecutive failure, counter resets on a success.