Skip to content

feat(server-core): Redact query values in log events - #11854

Merged
MazterQyou merged 1 commit into
masterfrom
server-core/redact-query-values
Sep 15, 2026
Merged

MazterQyou merged 1 commit into
masterfrom
server-core/redact-query-values

Conversation

@MazterQyou

@MazterQyou MazterQyou commented Sep 11, 2026

Copy link
Copy Markdown
Member

Check List

  • Tests have been run in packages where changes have been made if available
  • Linter has been run for changed code
  • Tests for the changes have been added if not covered yet
  • Docs have been added / updated if required

Description of Changes Made

This PR redacts filter values, SQL parameters and SQL API string literals in log output, controlled by CUBEJS_LOG_REDACTION, while events sent to Cube Cloud keep the original values.

A SQL API statement is redacted in cubesql on the parsed statement (string literals replaced, numbers and placeholders kept) and attached beside the original as redactedQuery; an error message that quotes the statement gets a redactedError twin. The log sink swaps the twins in. A statement that cannot be parsed has no twin and is logged as received.

CUBEJS_LOG_REDACTION defaults to on, except in development mode (NODE_ENV not production, or CUBEJS_DEV_MODE=true), where runnable SQL in the console is wanted. cubesql reads the same variable.

Related tests are included.

Notes

  • Events sent to Cube Cloud carry two extra fields when redaction is on, redactedQuery and redactedError; unknown fields are ignored there. No change is needed in cube-runtime or Cube Cloud.
  • sanitizedQuery in error telemetry now uses the same redaction pass: marker is 'redacted' (was '[REPLACED]'), and short strings and 'true'/'false' are no longer exempt.
  • Executing SQL and Streaming SQL no longer log the rows of lambda inline tables, only their name and columns, regardless of the switch.

@MazterQyou
MazterQyou requested review from a team and keydunov as code owners September 11, 2026 17:05
@mintlify

mintlify Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
cubed3 🟢 Ready View Preview Sep 14, 2026, 6:18 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

@github-actions github-actions Bot added docs Issues that require a documentation improvement javascript Pull requests that update Javascript code labels Sep 11, 2026
@claude

claude Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 7m 11s —— View job


Review complete — solid, well-tested design; 1 high, 3 medium, 5 low. 9 inline comments posted.

Full review
  • Gather context (PR diff, changed files, log call sites in gateway / orchestrator / Rust shim)
  • Review log-redaction.ts implementation
  • Review integration points (server.ts, PreAggregationLoader.ts)
  • Review tests & docs
  • Post inline findings

What's good

The design choice that makes this work is putting the wrapper outermost, so the telemetry, dev-server and agent wrappers all receive the redacted copy — and stamping queryKeyMd5 over the unredacted key before redaction, which preserves the one field consumers actually correlate on. canonicalJson with byte-ordered keys to match serde_json's default BTreeMap is exactly right and easy to get wrong. Coverage is good: I traced the Rust SQL API path (shim.rslog_load_state / NodeBridgeLoggerapiGateway.log → core logger) and its query prop does get redacted. Cycle/shared-reference handling via the memo map, non-mutation of the input, and filter-leaf detection on the leaf (rather than a filters key) are all the right calls. The test file is genuinely thorough — one it per lexical rule, plus fixtures cross-checked against Cube Cloud's normalizer.

Findings

# Sev Where Issue
1 High server.ts:353 Unconditional, no opt-out — dev console loses Executing SQL values (devLogger inlines them via SqlString.format). Suggest env gate or dev-mode exemption + docs.
2 Med log-redaction.ts:165-175 Postgres-only lexer: in MySQL/ClickHouse semantics a \' inside a plain literal leaks the value's tail ('O\'Brien lives at 5 Privet Drive''redacted'Brien lives at 5 Privet Drive'redacted'). MySQL "..." literals also copied verbatim.
3 Med log-redaction.ts:274, index.mdx:146 error / trace strings pass through and routinely embed values; securityContext and dateRange kept by design. The docs' "never carry the values" is stronger than the code.
4 Med log-redaction.ts:404 Redaction runs before the CUBEJS_LOG_LEVEL filter — full deep copy + canonical JSON + md5 per event, including trace events the sink discards.
5 Low log-redaction.ts:385-396 Non-plain-object params → returned unredacted and mutated (queryKeyMd5 written onto the caller's object), contradicting the stated contract.
6 Low PreAggregationLoader.ts:69 'FETCH_TABLES_FOR' sentinel never matches (real key is Fetch tables for …), so the two paths now disagree; and the logged md5 changes for object-bearing keys — worth a changelog line.
7 Low log-redaction.ts:388-393 FETCH_TABLES_FOR matched against the whole key JSON — a user value containing that phrase silently drops queryKeyMd5. Plus duplicated md5 code.
8 Low log-redaction.ts:286-292 1e+21 vs. ryu's 1e21 — likely divergence from serde_json; the test pins the JS output under an "as serde_json does" title.
9 Low log-redaction.ts:1-20 ~1/3 of the file is prose; the header and the 25-line lexer block restate the code and the tests. Suggested cuts inline.

Test gaps worth closing: a backslash-escaped literal (#2), non-plain-object params (#5), and an assertion that queryKeyMd5 is not stamped onto events without a queue key (the field is a new payload shape for every custom logger implementation).

Not verified

I could not run the test suite or the linter — npx / yarn are outside this session's allowed commands, so packages/cubejs-backend-shared/test/log-redaction.test.ts and the new index.test.ts case were reviewed by reading only. Finding #2 was derived by tracing scanQuotedLiteral by hand rather than executing it; worth confirming with a quick run before acting on it. No prior review threads existed on this PR, so nothing to resolve or dedupe against.

• branch [`server-core/redact-query-values`](https://github.com/cube-js/cube/tree/server-core/redact-query-values)

Comment thread packages/cubejs-server-core/src/core/server.ts Outdated
Comment thread packages/cubejs-backend-shared/src/log-redaction.ts Outdated
Comment thread packages/cubejs-backend-shared/src/log-redaction.ts Outdated
Comment thread packages/cubejs-backend-shared/src/log-redaction.ts
Comment thread docs-mintlify/admin/monitoring/monitoring-integrations/index.mdx Outdated
Comment thread packages/cubejs-query-orchestrator/src/orchestrator/PreAggregationLoader.ts Outdated
Comment thread packages/cubejs-backend-shared/src/log-redaction.ts Outdated
Comment thread packages/cubejs-backend-shared/src/log-redaction.ts
Comment thread packages/cubejs-backend-shared/src/log-redaction.ts
Comment thread packages/cubejs-backend-shared/src/log-redaction.ts Outdated
@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.12329% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.40%. Comparing base (03377e2) to head (0bcca51).

Files with missing lines Patch % Lines
rust/cubesql/cubesql/src/config/mod.rs 55.55% 8 Missing ⚠️
rust/cubesql/cubesql/src/sql/postgres/shim.rs 89.18% 8 Missing ⚠️
...ackages/cubejs-backend-shared/src/log-redaction.ts 94.44% 2 Missing and 1 partial ⚠️
...-query-orchestrator/src/orchestrator/QueryCache.ts 57.14% 3 Missing ⚠️
packages/cubejs-server-core/src/core/server.ts 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #11854      +/-   ##
==========================================
+ Coverage   80.37%   80.40%   +0.02%     
==========================================
  Files         500      501       +1     
  Lines      106573   106780     +207     
  Branches     3885     3908      +23     
==========================================
+ Hits        85658    85856     +198     
- Misses      20369    20376       +7     
- Partials      546      548       +2     
Flag Coverage Δ
cube-backend 60.54% <90.78%> (+0.13%) ⬆️
cubesql 84.77% <92.59%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@MazterQyou
MazterQyou force-pushed the server-core/redact-query-values branch from 3ef78db to 9ff783c Compare September 11, 2026 18:25
@MazterQyou
MazterQyou requested a review from a team as a code owner September 11, 2026 18:25
@github-actions github-actions Bot added the rust Pull requests that update Rust code label Sep 11, 2026
@claude

claude Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 6m 14s —— View job


Re-review of the updated design — 1 high, 2 medium, 2 low; 4 new inline comments, 8 prior threads resolved, 2 re-affirmed

Full review
  • Gather context (diff vs origin/master, changed files, prior review threads)
  • Review log-redaction.ts + CUBEJS_LOG_REDACTION gating
  • Review the Rust side (statement.rs, shim.rs, config/mod.rs, node_export.rs)
  • Review tests & docs
  • Resolve stale threads, post new findings

What changed since the last round, and what it fixed

The redesign is the right one. Moving SQL redaction out of the hand-rolled JS lexer and into StatementLiteralRedactor over the parsed AST removes the whole class of dialect-lexing bugs the previous version had, and it puts the work where the statement is already being parsed. CUBEJS_LOG_REDACTION with dev-mode-off defaults, mirrored in ConfigObjImpl so the two processes agree, addresses the "no opt-out" finding; the docs paragraphs now describe what the code actually does (securityContext kept, error messages passed through) instead of promising more. Carrying the redacted key on SpanId::query_key and reusing it at every log site is a nicer shape than re-redacting per event. redactLogParams no longer mutates a non-plain-object params, and the FETCH_TABLES_FOR sentinel is a shared exported constant matched on the key's head. formatNumber now reasons about the u64/i64 round trip explicitly, which is the part of canonicalJson that was easiest to get quietly wrong.

Resolved 8 of my 10 prior threads as addressed. Two remain open and still apply — I have not re-posted them:

  • log-redaction.ts:111 — the 1e+21 / -1e+19 fixture. formatNumber now routes |x| >= 2^64 through toExponential(), which emits e+21; serde_json formats f64 through ryu::Buffer::format_finite, which emits no +. The test comment now says "observed from serde_json 1.0.151", so if that observation is right the thread can be closed — but the two claims are in direct conflict and I could not run cargo here to settle it.
  • log-redaction.ts:215 — redaction still runs outside the CUBEJS_LOG_LEVEL filter, so trace events are deep-copied and hashed before being discarded.

New findings

# Sev Where Issue
1 High statement.rs:864 visit_value is exhaustive but the traversal is not. Default visit_statement only descends into Query/Explain/Declare, so SET x = 'pii', INSERT … VALUES ('pii'), PREPARE/EXECUTE are logged verbatim — and the span is built from the raw message text, before Cube decides the statement is unsupported. Expr::TypedString (line 254) and the _ => {} expr arm (line 261) are narrower holes. Placeholder discovery wants fail-open; redaction wants fail-closed.
2 Med statement.rs:903 A statement that does not parse logs verbatim — the case most likely to carry a hand-typed literal, surfacing under Cube SQL Error. Documented, but it is the promise's main exception. Same comment: to_string() drops comments and reformats, so the logged SQL is no longer what the client sent (BI-tool tracing comments disappear).
3 Med PreAggregationLoader.ts:57 Local jsonStr.includes(FETCH_TABLES_FOR) disagrees with the shared isFetchTablesKey (head vs. anywhere); when queryKey[0] is not a string the function returns the whole key JSON — values included — as the logged queryKeyMd5. Now that the sentinel is fixed this branch is live for the first time.
4 Low node_export.rs:298 "query": null when span_id is None, where the two sibling sites unwrap() — a payload shape a consumer reading query.sql would not expect.
5 Low shim.rs / statement.rs redact_sql_literals parses each statement a second time purely for logging. Cheap next to planning, but it is per-statement on the hot path and worth a quick measurement on a wide BI query.

Test gaps that follow from the above: no Rust case for a non-Query statement (finding 1), none for Expr::TypedString, and none for the PreAggregationLoader introspection branch (finding 3).

Not verified

yarn, npx, node -e and cargo are all outside this session's allowed commands, so nothing here was executed — log-redaction.test.ts, index.test.ts and test_redact_sql_literals were reviewed by reading. Findings 1 and 2 come from tracing the Visitor default methods in statement.rs:52-663 by hand; worth confirming with a redact_sql_literals("SET application_name = 'x'") case before acting. The serde_json exponent question above is likewise unresolved for the same reason.

• branch [`server-core/redact-query-values`](https://github.com/cube-js/cube/tree/server-core/redact-query-values)

Comment thread rust/cubesql/cubesql/src/sql/statement.rs Outdated
Comment thread rust/cubesql/cubesql/src/sql/statement.rs
Comment thread packages/cubejs-query-orchestrator/src/orchestrator/PreAggregationLoader.ts Outdated
Comment thread packages/cubejs-backend-native/src/node_export.rs Outdated
@MazterQyou
MazterQyou force-pushed the server-core/redact-query-values branch from 9ff783c to 27d7c68 Compare September 11, 2026 18:56
@claude

claude Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 7m 19s —— View job


Re-review of the redesign — 1 high, 2 medium, 2 low; 5 new inline comments, 4 prior threads resolved, 2 re-affirmed

Full review

  • Gather context (diff vs origin/master, changed files, prior threads)
  • Review the JS side (log-redaction.ts, env.ts, server.ts, PreAggregationLoader.ts)
  • Review the Rust side (statement.rs, shim.rs, config/mod.rs, node_export.rs)
  • Review tests & docs
  • Resolve stale threads, post findings

What changed since the last round, and what it fixed

The two structural changes are both the right ones. Enabling sqlparser's visitor feature and going through visit_expressions_mut replaces the partial hand-written Visitor traversal with the derived one, so redaction now genuinely reaches SET / INSERT / PREPARE / EXECUTE and Expr::TypedString — the exact statement kinds that leaked last round, each with a test. Returning the original text when nothing was redacted is a nice touch: a statement with no literal keeps its comments and spacing, so the to_string() reprint only costs you readability in the case where you were already losing the values. redact_value returning a bool to drive that is the minimum machinery for it.

On the JS side, dropping the hand-rolled SQL lexer entirely removes the whole class of dialect-lexing bugs (the \' tail leak, MySQL "…") rather than patching them, and formatNumber now reasons about the u64/i64 round trip explicitly. redactLogParams copies a non-plain-object params instead of mutating it, FETCH_TABLES_FOR / isFetchTablesKey are shared and matched on the head, and the md5 helper deduplicates the two hashing sites. node_export.rs collapsing Option<Arc<SpanId>> to Arc<SpanId> removes five unwrap()s and the "query": null shape along with them — a better outcome than the fix I asked for.

Resolved 4 of my prior threads as addressed (statement traversal, the PreAggregationLoader sentinel, node_export's null query, and the redaction-cost thread — thanks for the measurement; 25µs on an 8KB event against 13µs for a bare JSON.stringify answers it, and the point about a custom logger receiving every event regardless of CUBEJS_LOG_LEVEL is correct). Two remain open and still apply; I have not re-posted them:

  • log-redaction.ts:111 — the 1e+21 / -1e+19 fixture. formatNumber routes |x| >= 2^64 through toExponential(), which emits e+21; serde_json formats f64 through ryu, which emits no +. The comment now says "Verified against serde_json 1.0.151", so if that observation is right the thread can close — but the two claims still contradict each other and cargo is not available in this session to settle it.
  • statement.rs:903 — a statement that does not parse is still logged verbatim, which is the case most likely to carry a hand-typed literal. Now documented in both the docstring and the docs, so this is a stated exception rather than a gap; leaving it open only because the decision is the reviewer's.

New findings

# Sev Where Issue
1 High shim.rs:467 Cube SQL Error bypasses redaction entirely. handle_connection_errorSessionLogger::errorNodeBridgeLoggerapiGateway.log carries props["query"], built from the raw statement in query_engine.rs:216/:244 and parser_pg.rs:281. The JS lexer used to cover this incidentally; nothing does now. Same comment: SensitiveDataSanitizer is the weaker predecessor of redact_value and the two now coexist, with the misleadingly-named sanitizedQuery prop next to the unredacted one.
2 Med config/mod.rs:219 env_optparse panics on a value bool::from_str rejects, and this is the first Rust read of CUBEJS_DEV_MODE. CUBEJS_DEV_MODE=TRUE works today (JS asBoolStrict lower-cases) and now crashes cubesql at startup; CUBEJS_LOG_REDACTION=1 likewise.
3 Med statement.rs:887 visit_expressions_mut reaches ast::Value inside expressions only; literals stored as bare String (ShowStatementFilter::Like, Statement::Comment) are not covered, while the docstring says "every string literal, in every statement kind". Also: value-level redaction takes syntax strings too (DATE_TRUNC('redacted', …), INTERVAL 'redacted'), which is safe but undocumented.
4 Low PreAggregationLoader.ts:55 Guard and destructure disagree on shape: isFetchTablesKey accepts a string head, const [[sql]] then yields the string's first character. Unreachable under the local QueryKey type — which also makes the branch dead, as its predecessor was.
5 Low log-redaction.ts:4 The module header states "Cube generates it with placeholders" as a fact; unsafeValue() inlining and date ranges make it a constraint, and finding 1 is the case where the other half of the sentence does not hold either.

Not a finding, just noted: redact_sql_literals parses each statement a second time purely for logging, on the Query/Parse hot path. Cheap next to planning, but it is now per-message and unconditional when redaction is on — worth one measurement on a wide BI query.

Test gaps that follow: no case for the Cube SQL Error props path (finding 1), none for a SHOW … LIKE filter (finding 3), and none for PreAggregationLoader's introspection branch (finding 4, though the honest fix there is to delete it).

Not verified

yarn, node -e and cargo are all outside this session's allowed commands and the workspace has no node_modules, so nothing here was executed — log-redaction.test.ts, index.test.ts and test_redact_sql_literals were reviewed by reading. Finding 1 was derived by tracing the call chain (shim.rs:557telemetry/mod.rs:84logger.rs:33sql-server.ts:50) and the two with_meta sites; worth confirming with a deliberately unsupported statement against a local instance before acting. Finding 2 rests on env-var's asBoolStrict lower-casing its input, which I could not run to confirm. The serde_json exponent question above is unresolved for the same reason.

• branch [`server-core/redact-query-values`](https://github.com/cube-js/cube/tree/server-core/redact-query-values)

Comment thread docs-mintlify/reference/configuration/environment-variables.mdx Outdated
@MazterQyou
MazterQyou force-pushed the server-core/redact-query-values branch from 8d131ea to 46ae5cd Compare September 14, 2026 17:02
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 4m 58s —— View job


Re-review — 1 high; 1 inline comment, both of my prior threads resolved

Full review

  • Gather context (diff vs origin/master, changed files, prior threads)
  • Review the JS side (gateway.ts, log-redaction.ts, env.ts, server.ts, QueryCache.ts)
  • Review the Rust side (statement.rs, shim.rs, config/mod.rs, node_export.rs, transport.rs)
  • Review tests & docs
  • Resolve stale threads, post findings

What changed since the last round

Both findings are fixed, and the first in the stronger form — the producer now attaches the twin rather than the sink guessing:

  • /v1/cubesql gets a real redactedQuery. redactSqlLiterals is exported from the native module (node_export.rs:833, js/index.ts:380) and the gateway builds { sql: redactSqlLiterals(query) } beside the raw one (gateway.ts:576), threading it through HandleErrorOptions into all five handleError branches. So User Error / Orchestrator error / Continue wait / Internal Server Error now log the statement with its literals replaced instead of losing it — the same twin cubesql attaches, produced by the same pass. That is the right answer to the premise I disputed, and it removes the JS/Rust split in what "redacted statement" means.
  • The telemetry sentence is gone from environment-variables.mdx. The clause now names only the agent endpoint, which is what the code does.

Resolved both threads. The one open thread left is @ovr's; its subject is out of the diff, but it is a human thread so I left it.

New finding

# Sev Where Issue
1 High gateway.ts:576 The twin is built from req.body.query destructured at line 548, before cubeSqlRequestSchema.validate — and this runs in the catch that validation failure lands in. {} or {"query": 42} reaches cx.argument::<JsString>(0)?, which throws out of the catch; userAsyncHandler's .catch(next) turns a 400 Invalid query format into a 500 and the User Error event is never logged. On by default in production. Same shape when loadNative() itself throws.

Folded into that comment: log-redaction.test.ts:32 pins the drop using exactly the payload that now has a twin, so the fixture no longer describes a live producer.

What I checked and found sound

  • Every SQL API event still carries its twin, and only where it belongs. Both SpanId::new sites chain with_redacted_query_key (shim.rs:459-466, node_export.rs:316-323); transport.rs:681-688 inserts redactedQuery only when the payload has a query, and every log_load_state site now passes span_id.query_keyshim.rs:296/394/558/2035/2062, query_engine.rs:113/298, node_export.rs:294/342/606/636. grep '"sql":' across cubesql and cubejs-backend-native finds no raw-SQL payload outside the two span constructors and the tests.
  • handle_connection_error states its condition once now (shim.rs:523-533): one query binding gated on log_redaction(), one match producing both twins, log_props carrying them while err_response keeps the originals for the client that sent the statement. redacted_error is None when props carry no query, which is the data-source-error case the docs already scope out.
  • redact_error_message's two needles — raw text first, then each statement re-printed — cover both entry points' shapes, with the lower-cased/comment-carrying fixture and the multi-statement parser error pinning the case that used to miss. An empty query makes contains("") true but the replacement is empty too; a query with no literal returns its original text, so the replace is a no-op.
  • Wrapper placement. withLogRedaction wraps the raw sink at server.ts:201, before initAgent and both telemetry wrappers, so the agent collector forwards the original and Query History keeps the statement as sent; the telemetry sites now read params.redactedError ?? params.error (:303, :332), matching what they already did for sanitizedQuery. Nothing reads this.options.logger behind the wrapper — the only options.logger call sites (QueryCache.ts:883, QueryQueue.ts:137, CompilerApi.ts:148) all trace back to this.logger.
  • redact_literals over visit_expressions_mut reaches SET / INSERT / PREPARE / EXECUTE and Expr::TypedString, each with a case; returning the original text when nothing was redacted keeps comments and spacing (the /* Tableau */ fixture). The docstring scopes itself to "every string literal an expression carries" and names the SHOW … LIKE / COMMENT ON gap.
  • Dev-mode parity between env.ts:216 and config/mod.rs:164 is unchanged (NODE_ENV !== 'production' || CUBEJS_DEV_MODE), env_parse_bool warns and defaults, Config::test() pins log_redaction: false, and the three NODE_ENV cases plus both explicit values are pinned in log-redaction.test.ts:237-273. The two index.test.ts cases cover the wrapper end to end under both modes.
  • QueryCache.payloadForLog is non-mutating, handles the inlineTables === undefined case that is every non-lambda query, and covers all three spreads; the csvRows rule in log-redaction.ts:78 stays as the backstop.
  • holdsValues / PARAMETER_KEYS, isSqlWithParams declining order / dateRange, the cycle and shared-reference memo, the class-instance copy and the non-mutation case all still hold on re-read. config.mdx now tells a custom-logger author what arrives redacted, which was the gap on that side.

Not verified

yarn / jest could not run (no node_modules in the checkout) and cargo is unavailable, so log-redaction.test.ts, index.test.ts and the Rust tests were reviewed by reading. The finding was derived by tracing gateway.ts:548-580 against cubeSqlRequestSchema (query.js:214-219) and userAsyncHandler (:125-129); curl -XPOST localhost:4000/cubejs-api/v1/cubesql -d '{}' with CUBEJS_LOG_REDACTION=true would show it directly.

• branch server-core/redact-query-values

Comment thread packages/cubejs-api-gateway/src/gateway.ts Outdated
@MazterQyou
MazterQyou force-pushed the server-core/redact-query-values branch from 46ae5cd to 67eb193 Compare September 14, 2026 17:13
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 5m 42s —— View job


Re-review — 2 low; 2 inline comments, my last open thread resolved

Full review

  • Gather context (diff vs origin/master, changed files, prior threads)
  • Review the JS side (gateway.ts, log-redaction.ts, env.ts, server.ts, QueryCache.ts)
  • Review the Rust side (statement.rs, shim.rs, config/mod.rs, node_export.rs, transport.rs)
  • Review tests & docs
  • Resolve stale threads, post findings

What changed since the last round

The high finding is fixed, and the fix moved the logic somewhere it can be read:

  • redactedSqlForLog (gateway.ts:2503) guards on typeof query === 'string' before the native call, so {} / {"query": 42} no longer throws a TypeError out of the catch — the 400 stays a 400 and the User Error event is still logged. The try additionally covers loadNative() failing. Two new tests pin both halves: the malformed body keeps its 400 and its event with no twin, and a failing statement gets redactedQuery beside the original.
  • The fail-closed drop broadened to match. log-redaction.ts:117 now tests 'sql' in query rather than typeof query.sql === 'string', so a request body that put an object or an array under query is dropped too rather than written through — which is the case the type guard above deliberately leaves without a twin. Both shapes are pinned in log-redaction.test.ts:32-39.

I resolved that thread; the only remaining open one is @ovr's, and its subject (queryKeyMd5 stamping, the getCacheHash divergence, canonicalJson) is out of the diff entirely — PreAggregationLoader is untouched now and still owns its own hash — but it is a human thread, so I left it to close.

New findings

# Sev Where Issue
1 Low gateway.ts:2509 The bare catch { return undefined } makes a permanently-unloadable native module indistinguishable from a malformed body: every /v1/cubesql error logs query: { sql: 'redacted' } forever with nothing saying why. One this.log line keeps the degradation observable without changing the outcome.
2 Low environment-variables.mdx:1391, index.mdx:153 The docs name one exception (unparsed statement logged as received) but not the second outcome — a statement with no twin is dropped wholesale, not literal-replaced. That is the reachable case from finding 1 and from a malformed body.

What I checked and found sound

  • Every SQL API event still carries its twin. Both SpanId::new sites chain with_redacted_query_key (shim.rs:459-467, node_export.rs:316-324), redacted_query_key(sql, true) always returns Some (an unparsed statement falls back to its own text), and transport.rs:681-689 inserts redactedQuery only when the payload has a query. Walked every log_load_state site — shim.rs:293/345/373/388/551/2028/2055, query_engine.rs:107/191/262/295, node_export.rs:289/337/601/650 — and each one that carries a statement reads span_id.query_key, so each gets its twin. The three Data Query Status events carry no query and correctly get none.
  • The two entry points now agree on errors. shim.rs:523-533 computes both twins from one props["query"] binding gated once on log_redaction(); SQL API Error gets redactedError inline (:545) and redactedQuery from transport.rs; log_props carries both into logger.error while err_response (:562-570) keeps the originals for the client that sent the statement. node_export.rs:634-645 does the same for its Cube SQL Error, with the config resolved once per query.
  • Every CompilationError that quotes a statement has a needle. Checked the parser too: parser_pg.rs:281 attaches query meta for the multi-statement case, and both CompilationError::SqlParser sites (:211, :249) attach original_query — so the Unable to parse snippets fall inside the already-documented unparsed-statement exception rather than being a separate gap. redact_error_message's raw-then-reprinted needle covers both entry points' shapes; the lower-cased/comment-carrying fixture pins the case that used to miss.
  • Borrow shape in handle_connection_error: query is an Option<&String> into props whose borrow ends at the match, so the later props.map(...) move is fine, and both earlier props.clone() calls are gone.
  • holdsValues still has its producer. PreAggregationLoader is out of the diff now, and queryOptions (:533) still sets queryKeyMd5 itself — so the 'queryKeyMd5' in node gate keys off something the producer owns, and the pre-aggregation build payload's values are still reached. getCacheHash and the logged hash cannot drift, since nothing shared touches either.
  • The drop rule has no false-positive target. Grepped packages/*/src for a logged query object carrying sql: only gateway.ts:572. PreAggregationLoader.ts:901's query: { sql, params } goes to client.unload, not a logger. sanitizeQueryForLogging(undefined) yields {}, which has no sql and is untouched.
  • QueryCache.payloadForLog is non-mutating (shallow spread, fresh object per table), handles the inlineTables === undefined case that is every non-lambda query, and covers all three spreads (:673, :741, :812); the queue key is [query, values] with no inlineTables, so the rows never reach QueryQueue's events either. The csvRows rule in log-redaction.ts:78 stays as the backstop.
  • Config parity. env.ts:216 and config/mod.rs:164 both compute NODE_ENV !== 'production' || CUBEJS_DEV_MODE, matching OptsHandler.isDevMode(); env_parse_bool warns and defaults rather than panicking; Config::test() pins log_redaction: false; and the native bridge builds Config::default() (config.rs:130), so both processes read the same env rather than one of them silently landing on a test default.
  • Wrapper placement. withLogRedaction wraps the raw sink at server.ts:201, before initAgent and both telemetry wrappers, so the agent endpoint and Query History keep the statement as sent; the telemetry sites read params.redactedError ?? params.error (:303, :332), matching what they already did for sanitizedQuery.
  • redact_literals over visit_expressions_mut reaches SET / INSERT / PREPARE / EXECUTE and Expr::TypedString, each with a case; returning the original text when nothing was redacted keeps comments and spacing (the /* Tableau */ fixture). The docstring scopes itself to "every string literal an expression carries" and names the SHOW … LIKE / COMMENT ON gap.
  • The test mock for @cubejs-backend/native uses a Proxy rather than a spread — correct, since the module's exports are non-enumerable getters, and the comment says exactly why.

Not verified

yarn / jest could not run (no node_modules in the checkout) and cargo is unavailable, so log-redaction.test.ts, index.test.ts and the Rust tests were reviewed by reading. Finding 1 is a code-path observation needing no runtime confirmation; finding 2 is editorial.

• branch [`server-core/redact-query-values`](https://github.com/cube-js/cube/tree/server-core/redact-query-values)

Comment thread packages/cubejs-api-gateway/src/gateway.ts Outdated
Comment thread docs-mintlify/reference/configuration/environment-variables.mdx
@MazterQyou
MazterQyou force-pushed the server-core/redact-query-values branch from 67eb193 to 8863cad Compare September 14, 2026 17:28
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 3m 49s —— View job


Re-review — 2 medium, 1 low; 3 inline comments, both of my prior threads resolved

Full review

  • Gather context (diff vs origin/master, changed files, prior threads)
  • Review the JS side (env.ts, gateway.ts, log-redaction.ts, server.ts, QueryCache.ts)
  • Review the Rust side (statement.rs, shim.rs, config/mod.rs, node_export.rs, transport.rs)
  • Review tests & docs
  • Resolve stale threads, post findings

What changed since the last round

Both of my findings are fixed, and I resolved both threads:

  • redactedSqlForLog says why it gave up. The bare catch { return undefined } is now a this.log({ type: 'Redaction Unavailable', … }), so a native module that will not load is distinguishable from a malformed body instead of silently dropping every statement forever. See finding 3 — the mechanism is right, the shape has two rough edges.
  • Both docs pages name the second outcome. "A statement Cube could not redact at all, such as a malformed request body or a SQL API error raised before the statement was read, is logged as redacted" now sits beside the unparsed-statement exception, in environment-variables.mdx:1392-1393 and monitoring-integrations/index.mdx:154-155.

There is also a third change I had not asked for: logRedaction now defaults off where isNativeSupported() is not true, documented in both places and pinned by a new test. That is findings 1 and 2 below.

New findings

# Sev Where Issue
1 Med env.ts:224 The native gate turns off the half that never needed native. Filter values, values/params/query_values, [sql, params] tuples and csvRows are all pure JS; only the SQL API twin calls into the module — and on such a platform the SQL API cannot run at all (sql-server.ts:79-81, env.ts:2049). A production musl deployment now logs REST filter values in the clear by default.
2 Med env.ts:226 isNativeSupported()detectLibc()spawnSync('getconf', …) (platform.ts:12), and getEnv does not memoise, so redactedSqlForLog forks a process synchronously on every /v1/cubesql error. defaultOn is also computed before the variable is read, so an explicit CUBEJS_LOG_REDACTION pays for it too.
3 Low gateway.ts:2521 Redaction Unavailable fires per failing request rather than once, and carries no req.context, so it has no requestId tying it to the User Error beside it.

What I checked and found sound

  • The lazy native import is genuinely lazy. redactSqlLiterals calls loadNative() at call time (js/index.ts:383-387), and loadedNative is cached, so importing it at the top of gateway.ts does not force a load on an unsupported platform. That is what makes the try in redactedSqlForLog the right place for the failure.
  • The two new gateway tests cover the pair the last round was missing: a malformed body keeps its 400 and still logs User Error with no twin, and a failing statement logs redactedQuery beside the original. The Proxy-not-spread mock is correct for a module whose exports are non-enumerable getters, and the comment says exactly why.
  • The env matrix is complete: production, three dev-mode shapes, both explicit values, and the new unsupported-platform case with the force-on override, all in log-redaction.test.ts:231-280. jest.spyOn(platform, 'isNativeSupported') does intercept, since env.ts imports the binding rather than destructuring it at load.
  • Rust side unchanged and still sound from the previous rounds: env_parse_bool warns and defaults rather than panicking, dev_mode matches OptsHandler.isDevMode() (config/mod.rs:164-165 against env.ts:223), Config::test() pins log_redaction: false, transport.rs:681-689 inserts redactedQuery only when the payload has a query, and redact_error_message's raw-then-reprinted needle covers both entry points' shapes.
  • The sink's contract holds. withLogRedaction wraps the raw sink at server.ts:201, before initAgent and both telemetry wrappers, so the agent endpoint keeps the statement as sent; the telemetry sites read params.redactedError ?? params.error (:303, :332), matching what they already did for sanitizedQuery. The COMPANIONS swap is top-level, which is where both twins arrive.
  • holdsValues / PARAMETER_KEYS, the csvRows backstop, QueryCache.payloadForLog, the cycle and shared-reference memo, the class-instance copy and the non-mutation case all still hold on re-read.

Threads

Resolved both of my own. The one open thread is @ovr's on log-redaction.ts — its subject (queryKeyMd5, canonicalJson, the getCacheHash divergence) is out of the diff entirely and PreAggregationLoader is untouched, but it is a human thread, so I left it to close.

Not verified

yarn / jest could not run (no node_modules in the checkout) and cargo is unavailable, so log-redaction.test.ts, the new index.test.ts cases and the Rust tests were reviewed by reading. Findings 1 and 2 were derived by tracing isNativeSupported / detectLibc and the getEnv('logRedaction') call sites (server.ts:201, gateway.ts:2513); a CUBEJS_LOG_REDACTION=true run with strace -f -e trace=clone on a few /v1/cubesql errors would show the spawns directly.

• branch server-core/redact-query-values

Comment thread packages/cubejs-backend-shared/src/env.ts Outdated
Comment thread packages/cubejs-backend-shared/src/env.ts
Comment thread packages/cubejs-api-gateway/src/gateway.ts Outdated
@MazterQyou
MazterQyou force-pushed the server-core/redact-query-values branch from 8863cad to ced365f Compare September 14, 2026 17:42
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 4m 15s —— View job


Re-review — no new findings; all three of my prior threads resolved

Full review

  • Gather context (diff vs origin/master, changed files, prior threads)
  • Review the JS side (env.ts, gateway.ts, log-redaction.ts, server.ts, QueryCache.ts)
  • Review the Rust side (statement.rs, shim.rs, config/mod.rs, node_export.rs, transport.rs)
  • Review tests & docs
  • Resolve stale threads, post findings

What changed since the last round

All three findings are addressed, and the first two in the stronger form — by removing the mechanism rather than working around it:

  • The native gate is gone from env.ts. logRedaction is now just NODE_ENV !== 'production' || CUBEJS_DEV_MODE with an explicit value winning, so the pure-JS half of redaction — filter values, values / params / query_values, [sql, params] tuples, csvRows — stays on for a production musl deployment. That also removes the spawnSync('getconf', …) per /v1/cubesql error, since isNativeSupported() is no longer on the path at all; the second finding disappears with the first. The unsupported-platform test case is gone from log-redaction.test.ts along with it, and the outcome is documented rather than silently defaulted: both docs pages now say a statement Cube could not redact is logged as redacted.
  • Redaction Unavailable fires once and carries the request context. redactionUnavailableReported latches it, and this.log({ … }, context) gives it the requestId that ties it to the User Error beside it, so a native module that will not load explains itself once instead of once per retry.

What I checked and found sound

  • redactedSqlForLog is the only native call on the error path, and it is guarded on both axesgetEnv('logRedaction') and typeof query === 'string' before the call, try/catch around loadNative(). redactSqlLiterals resolves the module lazily (js/index.ts:383-386), so importing it at the top of gateway.ts costs nothing on a platform that cannot load it, and @cubejs-backend/native is a real dependency of cubejs-api-gateway (package.json:28), not a peer.
  • handleError's twin threading is uniform. loggedQuery carries sanitizeQueryForLogging(query) plus the twin into the four CubejsHandlerError / Continue wait / Orchestrator error / UserError branches, and Internal Server Error spreads the twin beside its deliberately unsanitized query. Only the /v1/cubesql route supplies a redactedQuery; every REST caller passes a member-shaped query, which has no sql key and so never meets the drop rule.
  • The drop rule has no false-positive target. Grepped packages/*/src for a logged query object carrying sql: still only gateway.ts:572. 'sql' in query catches the malformed-body shapes ({ sql: 42 }, { sql: {…} }) that the type guard deliberately leaves untwinned, and both are pinned in log-redaction.test.ts:32-39.
  • The two new gateway tests cover the pair that mattered: a malformed body keeps its 400 and still logs User Error with no twin, and a failing statement logs redactedQuery beside the original. The Proxy-not-spread mock is right for a module whose exports are non-enumerable getters, and the comment says why.
  • Rust side unchanged from the round I signed off on, and re-read to confirm: both SpanId::new sites chain with_redacted_query_key; transport.rs:681-689 inserts redactedQuery only when the payload already has a query; handle_connection_error states its condition once, computes both twins from one props["query"], and leaves err_response building {message}\nQUERY: {query} from the originals for the client that sent the statement; node_export.rs:634-644 attaches redactedError under config.log_redaction() with the config resolved once per query. redact_literals over visit_expressions_mut still reaches SET / INSERT / PREPARE / EXECUTE and Expr::TypedString, and returning the original text when nothing was redacted keeps comments and spacing.
  • Dev-mode parity holds after the env.ts rewrite: env.ts:216-226 and config/mod.rs:164-165 both compute NODE_ENV !== 'production' || CUBEJS_DEV_MODE, matching OptsHandler.isDevMode(); env_parse_bool warns and defaults rather than panicking on a casing it does not expect; Config::test() pins log_redaction: false. The three NODE_ENV cases and both explicit values are pinned in log-redaction.test.ts:230-266, and the two index.test.ts cases cover the wrapper end to end under both modes.
  • Wrapper placement is still the sink only (server.ts:201), ahead of initAgent and both telemetry wrappers, and those now read params.redactedError ?? params.error (:303, :332) — matching what they already did for sanitizedQuery.
  • QueryCache.payloadForLog, holdsValues / PARAMETER_KEYS, the csvRows backstop, isSqlWithParams declining order / dateRange, the cycle and shared-reference memo, and the class-instance copy all still hold on re-read. config.mdx tells a custom-logger author what arrives redacted.

Nit, not worth a thread: the COMPANIONS swap keys off companion in redacted, so a twin present-but-undefined would blank the field it replaces rather than leave it. Unreachable today — handleError spreads the key only when truthy, and both Rust sites always produce a string — but !== undefined would make the swap total for the same cost.

Threads

Resolved all three of my own. The one open thread is @ovr's on log-redaction.ts — its subject (queryKeyMd5, canonicalJson, the getCacheHash divergence) is out of the diff entirely and PreAggregationLoader is untouched, but it is a human thread, so I left it to close.

Not verified

yarn / jest could not run (the checkout has no node_modules) and cargo is unavailable, so log-redaction.test.ts, the index.test.ts cases and the Rust tests were reviewed by reading. The "no false-positive target" claim for the drop rule comes from grepping packages/*/src for logged query objects carrying sql, not from a running instance.

• branch [`server-core/redact-query-values`](https://github.com/cube-js/cube/tree/server-core/redact-query-values)

@MazterQyou
MazterQyou force-pushed the server-core/redact-query-values branch from ced365f to 71988da Compare September 14, 2026 18:09
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 3m 51s —— View job


Re-review — 1 low; 1 inline comment, no threads of mine left open

Full review

  • Gather context (diff vs origin/master, changed files, prior threads)
  • Review the JS side (gateway.ts, log-redaction.ts, env.ts, server.ts, QueryCache.ts)
  • Review the Rust side (statement.rs, shim.rs, config/mod.rs, node_export.rs, transport.rs)
  • Review tests & docs
  • Resolve stale threads, post findings

What changed since the last round

One change: redactedSqlForLog dropped the try / catch and the Redaction Unavailable event, with a comment stating the reason — this endpoint runs the statement through the same native module, so a module that will not load has already failed the request, and the error may propagate. That is a defensible call and it removes the per-request warning I asked to latch; see the finding below for the part of it that does not quite hold.

New finding

# Sev Where Issue
1 Low gateway.ts:2517 Two of the three ways into that catch never touch native: assertApiScope (:556) and cubeSqlRequestSchema.validate (:558) throw before execSql. On a platform where loadNative() throws, those turn a clean 403 / 400 plus a logged User Error into a generic 500 with no event — the one line that would have named the missing module is the one that disappears.

What I checked and found sound

  • Every SQL API event carries its twin, and only where it belongs. Both SpanId::new sites chain with_redacted_query_key (shim.rs:459-466, node_export.rs:316-322), redacted_query_key(sql, true) always returns Some (an unparsed statement falls back to its own text), and transport.rs:681-689 inserts redactedQuery only when the payload already has a query. Walked every log_load_state site — shim.rs:293/345/373/388/551/2028/2055, query_engine.rs:107/191/262/295, node_export.rs:289/337/601/650 — and each one carrying a statement reads span_id.query_key; the Data Query Status / Plan Rewrite events carry no query and correctly get no twin. grep '"sql":' across cubesql and cubejs-backend-native finds no raw-SQL payload outside the two span constructors and the tests.
  • handle_connection_error states its condition once (shim.rs:523-533): one props["query"] binding gated on log_redaction(), one match producing both twins. SQL API Error gets redactedError inline and redactedQuery from transport.rs; log_props carries both into logger.error, while err_response still builds {message}\nQUERY: {query} from the originals for the client that sent the statement.
  • redact_error_message's two needles — raw text first, then each statement re-printed — cover both entry points' shapes. An empty query makes contains("") true but the replacement is empty too, so replace is a no-op; a query with no literal returns its own text, so the replace is a no-op; an unparsed query returns the message unchanged, the documented exception.
  • redact_literals over visit_expressions_mut reaches SET / INSERT / PREPARE / EXECUTE and Expr::TypedString, each with a case in test_redact_sql_literals; returning the original text when nothing was redacted keeps comments and spacing (the /* Tableau */ fixture). The docstring scopes itself to "every string literal an expression carries" and names the SHOW … LIKE / COMMENT ON gap. SensitiveDataSanitizer is gone in favour of redacted_statement, so sanitizedQuery and the logged text are one pass.
  • Wrapper placement. withLogRedaction wraps the raw sink at server.ts:201, before initAgent and both telemetry wrappers, so the agent endpoint keeps the statement as sent; the telemetry sites read params.redactedError ?? params.error (:303, :332), matching what they already did for sanitizedQuery.
  • The drop rule has no false-positive target. 'sql' in query catches the malformed-body shapes the type guard deliberately leaves untwinned, and the only producer of a logged query object carrying sql is gateway.ts:572PreAggregationLoader.ts:901's query: { sql, params } goes to client.unload, not a logger.
  • Config parity. env.ts:216-226 and config/mod.rs:164-165 both compute NODE_ENV !== 'production' || CUBEJS_DEV_MODE, matching OptsHandler.isDevMode(); the native gate is gone from env.ts, so the pure-JS half of redaction stays on where the module cannot load; env_parse_bool warns and defaults rather than panicking; Config::test() pins log_redaction: false.
  • QueryCache.payloadForLog is non-mutating, handles the inlineTables === undefined case that is every non-lambda query, and covers all three spreads (:673, :741, :812); the csvRows rule in log-redaction.ts:78 stays as the backstop.
  • holdsValues / PARAMETER_KEYS, isSqlWithParams declining order / dateRange, the cycle and shared-reference memo, the class-instance copy, the NODE_ENV matrix and the two gateway tests (malformed body keeps its 400 and its event; a failing statement logs redactedQuery beside the original) all still hold on re-read. Docs read as separate rules and now name both the agent endpoint and the "logged as redacted" outcome.

Threads

All of my own threads are resolved; nothing to resolve this round. The one open thread is @ovr's on log-redaction.ts — its subject (queryKeyMd5, canonicalJson, the getCacheHash divergence) is out of the diff entirely and PreAggregationLoader is untouched, but it is a human thread, so I left it to close.

Not verified

yarn / jest could not run (the checkout has no node_modules) and cargo is unavailable, so log-redaction.test.ts, the index.test.ts cases and the Rust tests were reviewed by reading. The finding was derived by reading the catch at gateway.ts:564-580 against loadNative() (js/index.ts:175-194) and userAsyncHandler; it needs no runtime confirmation.

• branch server-core/redact-query-values

Comment thread packages/cubejs-api-gateway/src/gateway.ts Outdated
Signed-off-by: Alex Qyoun-ae <4062971+MazterQyou@users.noreply.github.com>
@MazterQyou
MazterQyou force-pushed the server-core/redact-query-values branch from 71988da to 0bcca51 Compare September 14, 2026 18:16
@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Claude finished @MazterQyou's task in 1m 31s —— View job


Re-review — no new findings; my last open thread is resolved

Full review

  • Gather context (diff vs origin/master, changed files, prior threads)
  • Review the JS side (gateway.ts, log-redaction.ts, env.ts, server.ts, QueryCache.ts)
  • Review the Rust side (statement.rs, shim.rs, config/mod.rs, node_export.rs, transport.rs)
  • Review tests & docs
  • Resolve stale threads, post findings

What changed since the last round

One change, and it addresses the one finding I had: the comment on redactedSqlForLog (gateway.ts:2505-2521) now states the consequence explicitly rather than only the execSql half — "On such a platform this also turns a scope or validation error, raised before the statement ran, into a 500 with no event logged." That is the part a reader would otherwise have to derive from the catch block's three entry points, and the decision to let the error propagate is yours to make. Thread resolved.

What I checked and found sound

  • The /v1/cubesql twin path. redactedSqlForLog guards on getEnv('logRedaction') and typeof query === 'string' before the native call, so the malformed-body shapes ({}, {"query": 42}) return no twin and the log-redaction.ts:117 drop rule ('sql' in query) covers them — the 400 stays a 400 and the User Error event is still logged, pinned by the two index.test.ts cases. handleError threads redactedQuery into all five branches: the four via loggedQuery, and Internal Server Error by its own spread beside the deliberately unsanitized query.
  • No false-positive target for the drop rule. The only producer of a logged query object carrying sql is gateway.ts:572; every REST caller passes a member-shaped query, which has no sql key.
  • Every SQL API event carries its twin on the Rust side. Both SpanId::new sites chain with_redacted_query_key (shim.rs:460-466, node_export.rs:318-323); transport.rs:680-688 inserts redactedQuery only when the payload already has a query; handle_connection_error states its condition once (shim.rs:527-533) and produces both twins from one props["query"], with log_props carrying them into logger.error while err_response keeps the originals for the client that sent the statement; node_export.rs:642-644 attaches redactedError under config.log_redaction().
  • Wrapper placement is still the sink only (server.ts:201), ahead of initAgent and both telemetry wrappers, and those read params.redactedError ?? params.error — matching what they already did for sanitizedQuery. The COMPANIONS swap is top-level, which is where both twins arrive.
  • Dev-mode parity. env.ts:218-226 and config/mod.rs:164-165 both compute NODE_ENV !== 'production' || CUBEJS_DEV_MODE, matching OptsHandler.isDevMode(); the native gate is gone, so the pure-JS half of redaction stays on where the module cannot load; env_parse_bool warns and defaults rather than panicking; Config::test() pins log_redaction: false.
  • redact_literals over visit_expressions_mut, redact_error_message's raw-then-reprinted needle, QueryCache.payloadForLog, holdsValues / PARAMETER_KEYS, the csvRows backstop, the cycle and shared-reference memo and the class-instance copy all still hold on re-read.
  • Docs now carry both outcomes — the unparsed statement logged as received, and the un-redactable statement logged as redacted — in both pages, with the dev-mode definition in positive form and the agent endpoint named rather than "Cube Cloud".

Nit, not worth a thread: the new redactedSqlForLog comment is five lines, which is on the long side for a two-line body; the load-bearing sentence is the last one. And environment-variables.mdx:1384 wraps mid-sentence ("Member names, numeric") after the earlier edit.

Threads

Resolved my own last open thread. The one remaining is @ovr's on log-redaction.ts — its subject (queryKeyMd5, canonicalJson, the getCacheHash divergence) is out of the diff entirely and PreAggregationLoader is untouched, but it is a human thread, so I left it to close.

Not verified

yarn / jest could not run (the checkout has no node_modules) and cargo is unavailable, so log-redaction.test.ts, the index.test.ts cases and the Rust tests were reviewed by reading.

• branch [`server-core/redact-query-values`](https://github.com/cube-js/cube/tree/server-core/redact-query-values)

@MazterQyou
MazterQyou merged commit 99cceac into master Sep 15, 2026
343 of 350 checks passed
@MazterQyou
MazterQyou deleted the server-core/redact-query-values branch September 15, 2026 12:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Issues that require a documentation improvement javascript Pull requests that update Javascript code rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants