Commit 6dabfcd
fix: authorize every connection-scoped endpoint (116 were unguarded)
12 controllers took a caller-supplied connectionId and never checked it.
SecurityConfig only asserts .anyRequest().authenticated() and no filter,
interceptor or aspect inspects a connection id, so authentication was the
only barrier. Verified against a running install, not inferred: a DEVELOPER
holding no grant on any connection could
- read literal-bearing slow-query SQL with real customer ids and names
(GET /slow-query-analytics/{id}/query/{fp}/samples returned 200 while
GET /slow-log-source/{id} returned 403 in the same session),
- enumerate another tenant's schema and table statistics, via two endpoints
that decrypt the target connection's credentials and open a live JDBC
session (/tenant-column-suggestions and /config),
- and permanently delete that tenant's analysis history
(DELETE /slow-queries/history/connection/{id} -> 200, row gone).
Affected: SlowQueryController (43), SlowQueryAnalyticsController (13),
SchemaChangeController (13), SentinelAnalyticsController (10),
PerformanceActionController (9), QueryPerformanceController (8),
QueryPlanController (8), IndexAdvisorController (7),
PerformanceInsightsController (5), AdvisorController (3),
ResourceLimitsController (3), BusinessRuleController (3).
This is the same class of defect BrainController carried (93 of 116
unguarded). The safety test added then hardcodes one Path.of(...), so it
could not see any of these.
What changed
* 127 guard calls: assertCanReadConnectionContent on reads,
assertCanManageConnectionContent on writes and deletes.
* An id is not a capability. For endpoints keyed on alertId, actionId,
regressionId, recommendationId, fingerprintId, planId, ruleId, snapshotId
or historyId, resolve the owning connection and assert on that. 15 new
findConnectionIdFor* accessors where no lookup existed. These report 404,
not 403, for an unknown id — a 403 confirms the row exists, turning the
endpoint into an id oracle, and regressionId is a sequential Long.
* Ids arriving in the request body are not constrained by a path-variable
check. Four holes survived exactly that kind of fix:
- schema-changes/snapshots/compare took two snapshot ids and no
connectionId at all, so it would diff tenant A's schema against
tenant B's; compareSnapshots now refuses a mismatch outright.
- PUT /performance-actions/batch-status took an arbitrary actionIds list
with no scope; it now authorizes every id before mutating any, so a
mixed batch fails atomically.
- changes/acknowledge and regressions/acknowledge authorized the path
connection and then acted on whatever ids the body named;
allChangesBelongTo / allComparisonsBelongTo verify membership, and an
id that resolves to nothing fails too, so unknown ids cannot be mixed
into an otherwise valid batch.
* Never take the actor from the request. userId was a query parameter and
acknowledgedBy/resolvedBy/updatedBy defaulted to the literal string
"user", so the acknowledgement trail was unauthenticated free text that
could name any colleague. 10 sites now use requireCurrentUsername(). The
parameters are still accepted for wire compatibility and ignored.
* ConnectionScopedAuthorizationSafetyTest replaces the per-file approach: it
scans every *Controller.java, so a new controller is covered the day it is
written. Six cases — connection-scoped endpoints authorized, body-supplied
id collections scoped, 403 not swallowed into 500, controller advices not
swallowing denials, exemptions still true, delegated service checks still
present. The exemption list is itself guarded, so it cannot rot into a way
of hiding a real gap.
Two things found by writing and running the fix, not by reading it
* The generalized test immediately found 9 more unguarded endpoints in
controllers nobody was looking at: StatsController, ProjectController,
DashboardController, and a destructive
DELETE /sentinel/demo/cleanup/{connectionId}. Three had been in my draft
exemption list on the assumption they were connection-free; they were not.
* Testing the fix found a bug reading it never would. 24 endpoints returned
403 and index-advisor returned 500: IndexAdvisorExceptionHandler's
@ExceptionHandler(Exception.class) swallowed the denial and reported
"Index operation failed" with the 403's text in the body. The guard held,
but the response blamed the index store. It now handles
ResponseStatusException first, and the safety test asserts no advice with
a catch-all omits that.
Also drops @crossorigin(origins = "*") from SentinelAnalyticsController.
Tested and inert — an evil-origin preflight gets 403 with no
Access-Control-Allow-Origin because the SecurityConfig allowlist wins, while
an allowed origin gets 200 + ACAO — but it reads like an intentional hole.
Verification
Real Maven compile of main and test sources, zero errors.
SlowQueryControllerS3Test needed the new constructor argument and was
updated rather than left red.
Live, against the rebuilt image with a DEVELOPER holding no grant on the
target connection:
- 40/40 previously-leaking reads -> 403
- 10/10 writes and destructive endpoints -> 403, and psql confirms nothing
was mutated
- 7/7 orphan-id and body-scoped paths -> 404, no existence oracle
- 30/30 same endpoint shapes on a granted connection -> 200, zero false
denials; confirmed again from a real browser session
- index-advisor now returns 403 "Read access denied for this connection"
Not covered: mvn test was not executed (the image build uses -DskipTests and
this host has no JDK/Maven). The six safety-test cases were validated by
re-implementing their scan logic against the tree and the file compiles, but
they have not been run by JUnit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>1 parent f1c03f3 commit 6dabfcd
27 files changed
Lines changed: 1009 additions & 15 deletions
File tree
- backend/src
- main/java/com/dbaagent
- controller
- service
- test/java/com/dbaagent/controller
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
709 | 709 | | |
710 | 710 | | |
711 | 711 | | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
712 | 779 | | |
713 | 780 | | |
714 | 781 | | |
| |||
Lines changed: 14 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
13 | 23 | | |
14 | 24 | | |
15 | 25 | | |
16 | 26 | | |
17 | 27 | | |
18 | 28 | | |
19 | 29 | | |
| 30 | + | |
20 | 31 | | |
21 | 32 | | |
22 | 33 | | |
| |||
25 | 36 | | |
26 | 37 | | |
27 | 38 | | |
| 39 | + | |
28 | 40 | | |
29 | 41 | | |
30 | 42 | | |
| |||
44 | 56 | | |
45 | 57 | | |
46 | 58 | | |
| 59 | + | |
47 | 60 | | |
48 | 61 | | |
49 | 62 | | |
| |||
63 | 76 | | |
64 | 77 | | |
65 | 78 | | |
| 79 | + | |
66 | 80 | | |
67 | 81 | | |
68 | 82 | | |
| |||
Lines changed: 14 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 | | |
| |||
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
14 | 21 | | |
15 | 22 | | |
16 | 23 | | |
17 | 24 | | |
18 | 25 | | |
19 | 26 | | |
20 | 27 | | |
| 28 | + | |
21 | 29 | | |
22 | 30 | | |
23 | 31 | | |
| |||
26 | 34 | | |
27 | 35 | | |
28 | 36 | | |
| 37 | + | |
29 | 38 | | |
30 | 39 | | |
31 | 40 | | |
| |||
49 | 58 | | |
50 | 59 | | |
51 | 60 | | |
| 61 | + | |
52 | 62 | | |
53 | 63 | | |
54 | 64 | | |
| |||
70 | 80 | | |
71 | 81 | | |
72 | 82 | | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
73 | 87 | | |
74 | 88 | | |
75 | 89 | | |
| |||
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
11 | 18 | | |
12 | 19 | | |
13 | 20 | | |
| |||
16 | 23 | | |
17 | 24 | | |
18 | 25 | | |
| 26 | + | |
19 | 27 | | |
20 | 28 | | |
21 | 29 | | |
| |||
26 | 34 | | |
27 | 35 | | |
28 | 36 | | |
| 37 | + | |
29 | 38 | | |
30 | 39 | | |
31 | 40 | | |
| |||
Lines changed: 15 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 | | |
| |||
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
15 | 22 | | |
16 | 23 | | |
17 | 24 | | |
| |||
21 | 28 | | |
22 | 29 | | |
23 | 30 | | |
| 31 | + | |
24 | 32 | | |
25 | 33 | | |
26 | 34 | | |
27 | 35 | | |
28 | 36 | | |
29 | 37 | | |
| 38 | + | |
30 | 39 | | |
31 | 40 | | |
32 | 41 | | |
| |||
35 | 44 | | |
36 | 45 | | |
37 | 46 | | |
| 47 | + | |
38 | 48 | | |
39 | 49 | | |
40 | 50 | | |
| |||
43 | 53 | | |
44 | 54 | | |
45 | 55 | | |
| 56 | + | |
46 | 57 | | |
47 | 58 | | |
48 | 59 | | |
| |||
53 | 64 | | |
54 | 65 | | |
55 | 66 | | |
| 67 | + | |
56 | 68 | | |
57 | 69 | | |
58 | 70 | | |
| |||
75 | 87 | | |
76 | 88 | | |
77 | 89 | | |
| 90 | + | |
78 | 91 | | |
79 | 92 | | |
80 | 93 | | |
| |||
94 | 107 | | |
95 | 108 | | |
96 | 109 | | |
| 110 | + | |
97 | 111 | | |
98 | 112 | | |
99 | 113 | | |
| |||
102 | 116 | | |
103 | 117 | | |
104 | 118 | | |
| 119 | + | |
105 | 120 | | |
106 | 121 | | |
107 | 122 | | |
Lines changed: 21 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
64 | 85 | | |
65 | 86 | | |
66 | 87 | | |
| |||
0 commit comments