@checkstack/backend-api 0.21.4 → 0.21.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # @checkstack/backend-api
2
2
 
3
+ ## 0.21.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 0626782: Guard the role editor against granting inert (and misleading) permissions to the
8
+ anonymous role.
9
+
10
+ RPC procedures carry two independent axes: `userType` (the hard authentication
11
+ gate) and `access` rules (authorization). An admin can grant the anonymous role
12
+ any access rule, but if the procedures needing that rule are `userType:
13
+ "authenticated"`/`"user"`, the grant does nothing - the auth middleware rejects
14
+ unauthenticated callers BEFORE access rules are checked (so there is no security
15
+ hole; the grant is simply inert). After anonymous users started seeing
16
+ permission-gated UI, such a grant would surface as visible-but-broken controls.
17
+
18
+ - The backend now computes, from contract metadata, the access rules an anonymous
19
+ caller can actually use (a rule is "usable" iff at least one `public` procedure
20
+ requires it) via `pluginManager.getAnonymousUsableAccessRuleIds()`, exposed to
21
+ plugins through the plugin environment.
22
+ - `auth.getAccessRules` annotates each rule with `anonymousUsable`.
23
+ - `auth.updateRole` REFUSES to ADD a non-usable rule to the anonymous role
24
+ (existing grants are untouched, so no configuration can be wedged). This is a
25
+ guardrail, not an enforcement change - RPC authorization is unchanged.
26
+ - The role editor disables non-usable rules (with an explanation) when editing
27
+ the anonymous role.
28
+
29
+ Verified live: `getAccessRules` reports 11 anonymous-usable vs 58 not; granting
30
+ `incident.incident.manage` to the anonymous role returns HTTP 400 with a clear
31
+ message.
32
+
33
+ - Updated dependencies [56e7c75]
34
+ - @checkstack/common@0.15.0
35
+ - @checkstack/healthcheck-common@1.5.4
36
+ - @checkstack/cache-api@0.3.12
37
+ - @checkstack/queue-api@0.3.12
38
+ - @checkstack/signal-common@0.2.9
39
+ - @checkstack/template-engine@0.4.3
40
+
3
41
  ## 0.21.4
4
42
 
5
43
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/backend-api",
3
- "version": "0.21.4",
3
+ "version": "0.21.5",
4
4
  "license": "Elastic-2.0",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -10,12 +10,12 @@
10
10
  "lint:code": "eslint . --max-warnings 0"
11
11
  },
12
12
  "dependencies": {
13
- "@checkstack/cache-api": "0.3.11",
14
- "@checkstack/common": "0.14.1",
15
- "@checkstack/healthcheck-common": "1.5.3",
16
- "@checkstack/queue-api": "0.3.11",
17
- "@checkstack/signal-common": "0.2.8",
18
- "@checkstack/template-engine": "0.4.2",
13
+ "@checkstack/cache-api": "0.3.12",
14
+ "@checkstack/common": "0.15.0",
15
+ "@checkstack/healthcheck-common": "1.5.4",
16
+ "@checkstack/queue-api": "0.3.12",
17
+ "@checkstack/signal-common": "0.2.9",
18
+ "@checkstack/template-engine": "0.4.3",
19
19
  "@orpc/client": "^1.14.4",
20
20
  "@orpc/contract": "^1.14.4",
21
21
  "@orpc/openapi": "^1.14.4",
@@ -27,7 +27,7 @@
27
27
  "zod": "^4.2.1"
28
28
  },
29
29
  "devDependencies": {
30
- "@checkstack/scripts": "0.6.0",
30
+ "@checkstack/scripts": "0.6.1",
31
31
  "@checkstack/tsconfig": "0.0.7",
32
32
  "@types/bun": "latest",
33
33
  "@types/pg": "^8.20.0",
@@ -139,6 +139,12 @@ export type BackendPluginRegistry = {
139
139
  ) => void;
140
140
  pluginManager: {
141
141
  getAllAccessRules: () => { id: string; description?: string }[];
142
+ /**
143
+ * Qualified ids of access rules an anonymous caller can actually use (rules
144
+ * required by at least one `public` procedure). Used to guard the role
145
+ * editor against granting inert permissions to the anonymous role.
146
+ */
147
+ getAnonymousUsableAccessRuleIds: () => string[];
142
148
  };
143
149
  };
144
150
 
package/src/rpc.test.ts CHANGED
@@ -25,7 +25,7 @@ const testContracts = {
25
25
  publicGlobalEndpoint: proc({
26
26
  userType: "public",
27
27
  operationType: "query",
28
- access: [access("resource", "read", "Test access")],
28
+ access: [access("resource", "read", "Test access", { pluginId: "test" })],
29
29
  }).output(z.object({ message: z.string() })),
30
30
 
31
31
  // Public endpoint with list filtering
@@ -39,7 +39,7 @@ const testContracts = {
39
39
  read: { description: "View systems", isPublic: true },
40
40
  manage: { description: "Manage systems" },
41
41
  },
42
- { listKey: "systems" },
42
+ { listKey: "systems", pluginId: "test" },
43
43
  ).read,
44
44
  ],
45
45
  }).output(
@@ -80,7 +80,7 @@ const testContracts = {
80
80
  read: { description: "View systems", isPublic: true },
81
81
  manage: { description: "Manage systems" },
82
82
  },
83
- { idParam: "systemId" },
83
+ { idParam: "systemId", pluginId: "test" },
84
84
  ).read,
85
85
  ],
86
86
  })
@@ -95,6 +95,7 @@ const testContracts = {
95
95
  access("bulk", "read", "Bulk read", {
96
96
  recordKey: "statuses",
97
97
  isPublic: true,
98
+ pluginId: "test",
98
99
  }),
99
100
  ],
100
101
  })
@@ -129,7 +130,7 @@ const testContracts = {
129
130
  read: { description: "View systems", isPublic: true },
130
131
  manage: { description: "Manage systems" },
131
132
  },
132
- { idParam: "systemId" },
133
+ { idParam: "systemId", pluginId: "test" },
133
134
  ).read,
134
135
  ],
135
136
  instanceAccess: { recordKey: "statuses" },