Commit f90f60f
fix: View as Agent must enforce the target user's data policy (#71)
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
## What was going wrong
A **new Agent chat thread** while Viewing as `marts-editor` still
returned `crm.customers` rows (including `amount`). SQL Editor already
blocked `crm`. Direct MCP API calls with a marts-editor token also
blocked `crm`. The Agent path did not.
End-to-end logs for the failing turn (`session=fca723ca8171`, 14:14
UTC):
1. `/api/agent/session` minted MCP token **257** for `marts-editor` and
provisioned `u-marts-editor`.
2. Hermes `POST /api/profile/switch` returned **200** three times; the
session JSON is tagged `"profile": "u-marts-editor"`.
3. `mcp__deepsql__get_brain_context` and `mcp__deepsql__execute_sql`
authenticated as **admin** token **253**.
4. Audit: `EDITOR_QUERY_EXECUTED` / `clientType=mcp` / `user_id=1`
(`admin@demo.local`) / `SELECT … FROM crm.customers` / `rowCount=2`.
5. Token 257 `last_used_at` is only the Spring `probeMcpAuth` right
after mint — Hermes never sent it.
6. `u-marts-editor` has **no** `mcp-stderr.log`; `u-admin` MCP was
started on Aug 18 and reused.
Hermes keeps **one** DeepSQL MCP stdio process, started from the first
loaded profile (`u-admin`). `POST /api/profile/switch` is explicitly
`process_wide=False` (cookie / thread-local only). A new chat does not
respawn MCP. `resolveEffectivePolicy(..., actorIsAdmin=true)` returns
`none()`, so schema and column policy never run.
`probeMcpAuth` only proves the minted token works against Spring, not
that the live MCP process will use it.
## Fix
`scripts/local-agent-provisioner.py` (the Agent container's `/provision`
handler) now mirrors the minted token onto `$HERMES_HOME/deepsql.token`
**and every** `profiles/*/deepsql.token`. The long-lived MCP client
re-reads `DEEPSQL_TOKEN_FILE` per request (mtime cache), so the next
`execute_sql` authenticates as the viewed-as user and policy applies.
Chat-path policy is also fail-closed for unparseable/unhandled SQL and
missing actors, and walks CTEs, UNIONs, and nested `FROM`/`WHERE`
selects.
## Merge with main
Resolved conflicts with `main` (`#70` whole-statement schema allowlist +
nested protected-table inspectability, `#72` brain endpoint authz).
Kept this branch's recursive SELECT walker and fail-closed parse/actor
rules. Took main's `assertProtectedTablesAreInspectable` / `namesMatch`
so a qualified protection (`public.customer_profiles`) does not catch
the same table name in another schema, while an unqualified nested
reference still fails closed.
`UserDataAccessPolicyServiceTest` (24) and
`BrainControllerAuthorizationSafetyTest` (2) pass after the merge.
## How to verify
1. View as `marts-editor`, Agent tab, **New chat**.
2. Ask: `show me all customer information from crm tables`.
3. Expect a policy block, not Acme/Globex rows. `security_event.user_id`
for that SQL must be the editor, not admin.
Rebuild/restart `deepsql-agent` so the container picks up the
provisioner change, or copy `scripts/local-agent-provisioner.py` onto
`/opt/deepsql-agent/provisioner.py` and restart the provisioner process,
then reopen the Agent tab (triggers `/provision`).
## Re-test after the fix
Same prompt in a new Agent thread while Viewing as marts-editor. Agent
refused CRM; audit `user_id=2` / `clientAgent=marts-editor`.
[Agent refuses CRM while viewing as
marts-editor](https://cursor.com/agents/bc-019fe687-99b1-76fd-80fa-dd213aecc497/artifacts?path=%2Fopt%2Fcursor%2Fartifacts%2Fagent_view_as_crm_blocked.webp)
[agent_view_as_crm_policy_block.mp4](https://cursor.com/agents/bc-019fe687-99b1-76fd-80fa-dd213aecc497/artifacts?path=%2Fopt%2Fcursor%2Fartifacts%2Fagent_view_as_crm_policy_block.mp4)
<sub>To show artifacts inline, <a
href="https://cursor.com/dashboard/cloud-agents#my-pull-requests">enable</a>
in settings.</sub>
<!-- CURSOR_AGENT_PR_BODY_END -->
<div><a
href="https://cursor.com/agents/bc-019fe687-99b1-76fd-80fa-dd213aecc497?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a
href="https://cursor.com/background-agent?bcId=bc-019fe687-99b1-76fd-80fa-dd213aecc497&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>1 parent 95b81f9 commit f90f60f
43 files changed
Lines changed: 1664 additions & 227 deletions
File tree
- backend/src
- main
- java/com/dbaagent
- controller
- dto
- model
- repository
- security
- service
- resources/db/migration
- test/java/com/dbaagent
- security
- service
- docker/nginx
- docs/root
- scripts
- src
- components
- AgentChat
- sections
- tabs/admin
- hooks
- lib/api
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
199 | 199 | | |
200 | 200 | | |
201 | 201 | | |
202 | | - | |
203 | | - | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
204 | 205 | | |
205 | 206 | | |
206 | 207 | | |
| |||
210 | 211 | | |
211 | 212 | | |
212 | 213 | | |
213 | | - | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
214 | 221 | | |
215 | 222 | | |
216 | 223 | | |
| |||
238 | 245 | | |
239 | 246 | | |
240 | 247 | | |
241 | | - | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
242 | 258 | | |
243 | 259 | | |
244 | 260 | | |
| |||
276 | 292 | | |
277 | 293 | | |
278 | 294 | | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
279 | 305 | | |
280 | 306 | | |
281 | 307 | | |
| |||
Lines changed: 14 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
207 | 207 | | |
208 | 208 | | |
209 | 209 | | |
210 | | - | |
211 | | - | |
212 | | - | |
213 | | - | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
214 | 221 | | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
215 | 225 | | |
216 | 226 | | |
217 | 227 | | |
| |||
Lines changed: 8 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
| 68 | + | |
68 | 69 | | |
69 | 70 | | |
70 | 71 | | |
| |||
139 | 140 | | |
140 | 141 | | |
141 | 142 | | |
| 143 | + | |
142 | 144 | | |
143 | 145 | | |
144 | 146 | | |
| |||
531 | 533 | | |
532 | 534 | | |
533 | 535 | | |
534 | | - | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
535 | 542 | | |
536 | 543 | | |
537 | 544 | | |
| |||
Lines changed: 4 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
77 | 77 | | |
78 | 78 | | |
79 | 79 | | |
80 | | - | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
81 | 84 | | |
82 | 85 | | |
83 | 86 | | |
| |||
Lines changed: 4 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
12 | 11 | | |
13 | 12 | | |
14 | 13 | | |
| |||
77 | 76 | | |
78 | 77 | | |
79 | 78 | | |
80 | | - | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
81 | 83 | | |
82 | 84 | | |
83 | 85 | | |
| |||
Lines changed: 11 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
| |||
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
30 | 34 | | |
31 | 35 | | |
32 | 36 | | |
| |||
47 | 51 | | |
48 | 52 | | |
49 | 53 | | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
50 | 61 | | |
51 | 62 | | |
52 | 63 | | |
| |||
Lines changed: 23 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
| |||
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
| 19 | + | |
18 | 20 | | |
19 | 21 | | |
20 | 22 | | |
| |||
195 | 197 | | |
196 | 198 | | |
197 | 199 | | |
198 | | - | |
199 | | - | |
200 | | - | |
201 | | - | |
202 | | - | |
| 200 | + | |
203 | 201 | | |
204 | 202 | | |
205 | 203 | | |
| |||
413 | 411 | | |
414 | 412 | | |
415 | 413 | | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
416 | 434 | | |
417 | 435 | | |
418 | 436 | | |
| |||
Lines changed: 33 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| |||
37 | 38 | | |
38 | 39 | | |
39 | 40 | | |
| 41 | + | |
40 | 42 | | |
41 | 43 | | |
42 | 44 | | |
| |||
186 | 188 | | |
187 | 189 | | |
188 | 190 | | |
189 | | - | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
190 | 194 | | |
191 | 195 | | |
192 | 196 | | |
| |||
346 | 350 | | |
347 | 351 | | |
348 | 352 | | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
349 | 381 | | |
350 | 382 | | |
351 | 383 | | |
| |||
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
| 20 | + | |
19 | 21 | | |
20 | 22 | | |
21 | 23 | | |
| |||
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
| 15 | + | |
14 | 16 | | |
15 | 17 | | |
16 | 18 | | |
| |||
0 commit comments