Skip to content

#1553 - gate history + currentQuery egress on /ai/ask at send-time - #1585

Merged
juanresendiz813 merged 2 commits into
developmentfrom
fix/1553-history-egress
Jul 28, 2026
Merged

#1553 - gate history + currentQuery egress on /ai/ask at send-time#1585
juanresendiz813 merged 2 commits into
developmentfrom
fix/1553-history-egress

Conversation

@juanresendiz813

Copy link
Copy Markdown
Contributor

Fixes #1553.

Problem

The /ai/ask egress gate (#1554) stripped only the cellsetDigest. history and currentQuery still reached the LLM ungated, so cell-derived data leaked even at schema-only. Killer case: a turn-1 aggregated insight is replayed by the client at turn-2 schema-only — the assistant turn carrying model-emitted figures egresses regardless of the current posture. History is client-supplied and untrusted every turn, so the only enforceable control is at send-time.

Fix (Option B — "drop, not redact", send-time re-gate)

When the active posture forbids aggregated egress (!egressPermitsCellData(), fail-closed on a null/unwired guard), at the request-building seam — before the NlAskRequest is constructed, so both providers are covered in one place:

  • Drop assistant-role history turns, keep the user's own turns. Assistant turns replay emit_insight/emit_email_draft figures.
  • Strip currentQuery filter members — clear filters[].members while keeping the query structure (measures/rows/columns/cube + filter dimension/hierarchy/level refs). The model still sees the query shape, not which members were picked.

Applied in both askInternal (the /ai/ask seam) and the askChained preamble, mirroring the existing cellsetDigest strip and its fail-closed semantics. Both re-gates are copy-not-mutate: history is filtered into a new list; currentQuery is redacted on a mapper round-trip copy, so the caller-owned objects are never touched.

The agentic toolTranscript needed no change — its egress-withheld path (resultDigest == null) already holds, and under schema-only the chained loop stops before it ever executes/digests, so no data-bearing tool turn is produced. Confirmed with a test.

Tests

Added to AiAskServiceTest, mirroring the #1554 egress/digest-strip tests:

  • schema-only: assistant history turns dropped, user turns kept; currentQuery.filters[].members cleared, structure retained.
  • aggregated: history + currentQuery pass through untouched.
  • Fail-closed: null/unwired guard drops + strips.
  • No mutation of the caller's original history/currentQuery.
  • toolTranscript withheld-path stays withheld under schema-only.

One pre-existing test (passesQuestionAndHistoryToProviderUnchanged) asserted full-history passthrough with no guard wired; it now wires an AGGREGATED posture to keep asserting the plumbing forwards history intact (the fail-closed drop is covered by the dedicated tests).

mvn -o -pl saiku-core/saiku-service test -Dtest=AiAskServiceTest,OssieAiAskServiceTest,AiPolicyGuardTest,ToolTurnTest → 103 tests, 0 failures. spotless:apply clean.

@juanresendiz813
juanresendiz813 merged commit 0b5295b into development Jul 28, 2026
5 of 6 checks passed
@juanresendiz813
juanresendiz813 deleted the fix/1553-history-egress branch July 28, 2026 16:36
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.

AI /ask egress gate covers only the cellset digest — history and currentQuery reach the LLM ungated

1 participant