@team-semicolon/semicolony-cli 4.18.56 → 4.18.57

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.
@@ -0,0 +1,678 @@
1
+ -- 164_fabric_bridge_principal_boundary.sql
2
+ -- Least-privilege credential resolver for the isolated SEMO Ops Knowledge Fabric bridge.
3
+ --
4
+ -- The bridge role receives no core-schema/table privileges. It can use one dedicated
5
+ -- schema and execute one SECURITY DEFINER function that returns four sanitized fields.
6
+ -- The migration runner wraps this file in a transaction.
7
+ -- @schema-retarget: off
8
+
9
+ DO $$
10
+ BEGIN
11
+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'sc_fabric_bridge_owner') THEN
12
+ CREATE ROLE sc_fabric_bridge_owner NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOBYPASSRLS;
13
+ ELSE
14
+ ALTER ROLE sc_fabric_bridge_owner
15
+ WITH NOLOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOBYPASSRLS;
16
+ END IF;
17
+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'sc_fabric_bridge') THEN
18
+ CREATE ROLE sc_fabric_bridge LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOBYPASSRLS;
19
+ ELSE
20
+ ALTER ROLE sc_fabric_bridge
21
+ WITH LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOBYPASSRLS;
22
+ END IF;
23
+ EXECUTE format(
24
+ 'GRANT CONNECT ON DATABASE %I TO sc_fabric_bridge',
25
+ current_database()
26
+ );
27
+ END $$;
28
+
29
+ -- NOINHERIT does not prevent SET ROLE. Reject every inbound or outbound membership
30
+ -- before temporarily granting the migration runner the owner role.
31
+ DO $$
32
+ DECLARE
33
+ v_owner_oid oid;
34
+ v_bridge_oid oid;
35
+ BEGIN
36
+ SELECT oid INTO STRICT v_owner_oid
37
+ FROM pg_catalog.pg_roles
38
+ WHERE rolname = 'sc_fabric_bridge_owner';
39
+ SELECT oid INTO STRICT v_bridge_oid
40
+ FROM pg_catalog.pg_roles
41
+ WHERE rolname = 'sc_fabric_bridge';
42
+
43
+ IF EXISTS (
44
+ SELECT 1
45
+ FROM pg_catalog.pg_auth_members m
46
+ WHERE m.roleid IN (v_owner_oid, v_bridge_oid)
47
+ OR m.member IN (v_owner_oid, v_bridge_oid)
48
+ ) THEN
49
+ RAISE EXCEPTION 'fabric bridge role membership rejected';
50
+ END IF;
51
+
52
+ EXECUTE format('GRANT sc_fabric_bridge_owner TO %I', current_user);
53
+ END $$;
54
+
55
+ -- Never adopt an untrusted schema or silently preserve pre-existing objects or ACLs.
56
+ -- Only an empty schema owned by the migration runner, or this migration's exact
57
+ -- resolver and exact owner/bridge ACL, is safe to converge.
58
+ DO $$
59
+ DECLARE
60
+ v_bridge_oid oid;
61
+ v_owner_oid oid;
62
+ v_schema_oid oid;
63
+ v_schema_owner text;
64
+ v_unexpected_objects boolean;
65
+ v_unexpected_functions integer;
66
+ v_wrong_resolver_owner integer;
67
+ v_unsafe_default_acl boolean;
68
+ v_unsafe_schema_acl boolean;
69
+ v_unsafe_function_acl boolean;
70
+ BEGIN
71
+ SELECT oid INTO STRICT v_owner_oid
72
+ FROM pg_catalog.pg_roles
73
+ WHERE rolname = 'sc_fabric_bridge_owner';
74
+ SELECT oid INTO STRICT v_bridge_oid
75
+ FROM pg_catalog.pg_roles
76
+ WHERE rolname = 'sc_fabric_bridge';
77
+
78
+ SELECT n.oid, r.rolname
79
+ INTO v_schema_oid, v_schema_owner
80
+ FROM pg_catalog.pg_namespace n
81
+ JOIN pg_catalog.pg_roles r ON r.oid = n.nspowner
82
+ WHERE n.nspname = 'fabric_bridge';
83
+
84
+ IF v_schema_owner IS NOT NULL
85
+ AND v_schema_owner NOT IN (current_user, 'sc_fabric_bridge_owner') THEN
86
+ RAISE EXCEPTION 'fabric bridge schema ownership rejected';
87
+ END IF;
88
+
89
+ SELECT EXISTS (
90
+ SELECT 1
91
+ FROM pg_catalog.pg_class c
92
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
93
+ WHERE n.nspname = 'fabric_bridge'
94
+ UNION ALL
95
+ SELECT 1
96
+ FROM pg_catalog.pg_type t
97
+ JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
98
+ WHERE n.nspname = 'fabric_bridge'
99
+ UNION ALL
100
+ SELECT 1
101
+ FROM pg_catalog.pg_collation c
102
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.collnamespace
103
+ WHERE n.nspname = 'fabric_bridge'
104
+ UNION ALL
105
+ SELECT 1
106
+ FROM pg_catalog.pg_conversion c
107
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.connamespace
108
+ WHERE n.nspname = 'fabric_bridge'
109
+ UNION ALL
110
+ SELECT 1
111
+ FROM pg_catalog.pg_operator o
112
+ JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace
113
+ WHERE n.nspname = 'fabric_bridge'
114
+ UNION ALL
115
+ SELECT 1
116
+ FROM pg_catalog.pg_opclass o
117
+ JOIN pg_catalog.pg_namespace n ON n.oid = o.opcnamespace
118
+ WHERE n.nspname = 'fabric_bridge'
119
+ UNION ALL
120
+ SELECT 1
121
+ FROM pg_catalog.pg_opfamily o
122
+ JOIN pg_catalog.pg_namespace n ON n.oid = o.opfnamespace
123
+ WHERE n.nspname = 'fabric_bridge'
124
+ UNION ALL
125
+ SELECT 1
126
+ FROM pg_catalog.pg_statistic_ext s
127
+ JOIN pg_catalog.pg_namespace n ON n.oid = s.stxnamespace
128
+ WHERE n.nspname = 'fabric_bridge'
129
+ UNION ALL
130
+ SELECT 1
131
+ FROM pg_catalog.pg_ts_config t
132
+ JOIN pg_catalog.pg_namespace n ON n.oid = t.cfgnamespace
133
+ WHERE n.nspname = 'fabric_bridge'
134
+ UNION ALL
135
+ SELECT 1
136
+ FROM pg_catalog.pg_ts_dict t
137
+ JOIN pg_catalog.pg_namespace n ON n.oid = t.dictnamespace
138
+ WHERE n.nspname = 'fabric_bridge'
139
+ UNION ALL
140
+ SELECT 1
141
+ FROM pg_catalog.pg_ts_parser t
142
+ JOIN pg_catalog.pg_namespace n ON n.oid = t.prsnamespace
143
+ WHERE n.nspname = 'fabric_bridge'
144
+ UNION ALL
145
+ SELECT 1
146
+ FROM pg_catalog.pg_ts_template t
147
+ JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace
148
+ WHERE n.nspname = 'fabric_bridge'
149
+ UNION ALL
150
+ SELECT 1
151
+ FROM pg_catalog.pg_constraint c
152
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.connamespace
153
+ WHERE n.nspname = 'fabric_bridge'
154
+ UNION ALL
155
+ SELECT 1
156
+ FROM pg_catalog.pg_extension e
157
+ JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace
158
+ WHERE n.nspname = 'fabric_bridge'
159
+ ) INTO v_unexpected_objects;
160
+
161
+ IF v_unexpected_objects THEN
162
+ RAISE EXCEPTION 'fabric bridge schema inventory rejected';
163
+ END IF;
164
+
165
+ SELECT count(*)
166
+ INTO v_unexpected_functions
167
+ FROM pg_catalog.pg_proc p
168
+ JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
169
+ WHERE n.nspname = 'fabric_bridge'
170
+ AND NOT (
171
+ p.proname = 'resolve_principal'
172
+ AND p.prokind = 'f'
173
+ AND p.pronargs = 1
174
+ AND p.proargtypes = '25'::pg_catalog.oidvector
175
+ AND p.proretset
176
+ AND p.prorettype = 2249
177
+ AND p.proallargtypes = ARRAY[25, 2950, 25, 25, 25]::oid[]
178
+ AND p.proargmodes = ARRAY['i', 't', 't', 't', 't']::"char"[]
179
+ AND p.proargnames = ARRAY[
180
+ 'p_token_hash',
181
+ 'credential_id',
182
+ 'credential_kind',
183
+ 'actor_id',
184
+ 'principal_id'
185
+ ]::text[]
186
+ );
187
+
188
+ IF v_unexpected_functions <> 0 THEN
189
+ RAISE EXCEPTION 'fabric bridge schema inventory rejected';
190
+ END IF;
191
+
192
+ SELECT count(*)
193
+ INTO v_wrong_resolver_owner
194
+ FROM pg_catalog.pg_proc p
195
+ JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
196
+ JOIN pg_catalog.pg_roles r ON r.oid = p.proowner
197
+ WHERE n.nspname = 'fabric_bridge'
198
+ AND p.proname = 'resolve_principal'
199
+ AND p.prokind = 'f'
200
+ AND p.pronargs = 1
201
+ AND p.proargtypes = '25'::pg_catalog.oidvector
202
+ AND r.rolname <> 'sc_fabric_bridge_owner';
203
+
204
+ IF v_wrong_resolver_owner <> 0 THEN
205
+ RAISE EXCEPTION 'fabric bridge schema ownership rejected';
206
+ END IF;
207
+
208
+ SELECT EXISTS (
209
+ SELECT 1
210
+ FROM pg_catalog.pg_default_acl d
211
+ CROSS JOIN LATERAL pg_catalog.aclexplode(d.defaclacl) acl
212
+ WHERE d.defaclrole = v_owner_oid
213
+ AND d.defaclobjtype = 'f'
214
+ AND (
215
+ d.defaclnamespace = 0
216
+ OR d.defaclnamespace = v_schema_oid
217
+ )
218
+ AND NOT (
219
+ acl.grantee = v_owner_oid
220
+ AND acl.grantor = v_owner_oid
221
+ AND acl.privilege_type = 'EXECUTE'
222
+ AND NOT acl.is_grantable
223
+ )
224
+ ) INTO v_unsafe_default_acl;
225
+
226
+ IF v_unsafe_default_acl THEN
227
+ RAISE EXCEPTION 'fabric bridge default acl rejected';
228
+ END IF;
229
+
230
+ SELECT EXISTS (
231
+ SELECT 1
232
+ FROM pg_catalog.pg_namespace n
233
+ CROSS JOIN LATERAL pg_catalog.aclexplode(
234
+ COALESCE(n.nspacl, pg_catalog.acldefault('n', n.nspowner))
235
+ ) acl
236
+ WHERE n.nspname = 'fabric_bridge'
237
+ AND NOT (
238
+ (
239
+ acl.grantee = n.nspowner
240
+ AND acl.grantor = n.nspowner
241
+ AND acl.privilege_type IN ('CREATE', 'USAGE')
242
+ AND NOT acl.is_grantable
243
+ )
244
+ OR (
245
+ acl.grantee = v_bridge_oid
246
+ AND acl.grantor = n.nspowner
247
+ AND acl.privilege_type = 'USAGE'
248
+ AND NOT acl.is_grantable
249
+ )
250
+ )
251
+ ) INTO v_unsafe_schema_acl;
252
+
253
+ SELECT EXISTS (
254
+ SELECT 1
255
+ FROM pg_catalog.pg_proc p
256
+ JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
257
+ CROSS JOIN LATERAL pg_catalog.aclexplode(
258
+ COALESCE(p.proacl, pg_catalog.acldefault('f', p.proowner))
259
+ ) acl
260
+ WHERE n.nspname = 'fabric_bridge'
261
+ AND NOT (
262
+ acl.grantee IN (v_owner_oid, v_bridge_oid)
263
+ AND acl.grantor = p.proowner
264
+ AND acl.privilege_type = 'EXECUTE'
265
+ AND NOT acl.is_grantable
266
+ )
267
+ ) INTO v_unsafe_function_acl;
268
+
269
+ IF v_unsafe_schema_acl OR v_unsafe_function_acl THEN
270
+ RAISE EXCEPTION 'fabric bridge schema acl rejected';
271
+ END IF;
272
+ END $$;
273
+
274
+ CREATE SCHEMA IF NOT EXISTS fabric_bridge AUTHORIZATION sc_fabric_bridge_owner;
275
+ ALTER SCHEMA fabric_bridge OWNER TO sc_fabric_bridge_owner;
276
+ REVOKE ALL ON SCHEMA fabric_bridge FROM PUBLIC;
277
+ ALTER DEFAULT PRIVILEGES FOR ROLE sc_fabric_bridge_owner
278
+ REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;
279
+ ALTER DEFAULT PRIVILEGES FOR ROLE sc_fabric_bridge_owner IN SCHEMA fabric_bridge
280
+ REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;
281
+
282
+ ALTER TABLE semicolony.worker_devices
283
+ ADD COLUMN IF NOT EXISTS fabric_gateway_credential_id uuid
284
+ REFERENCES semicolony.gateway_credentials(id);
285
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_worker_devices_fabric_gateway_credential_id
286
+ ON semicolony.worker_devices (fabric_gateway_credential_id)
287
+ WHERE fabric_gateway_credential_id IS NOT NULL;
288
+
289
+ GRANT USAGE ON SCHEMA semicolony, public TO sc_fabric_bridge_owner;
290
+ GRANT SELECT (id, tenant_id, tenant_slug, token_hash, scopes, status, expires_at, metadata)
291
+ ON semicolony.gateway_credentials TO sc_fabric_bridge_owner;
292
+ GRANT SELECT (fabric_gateway_credential_id, worker_id, device_id, status, kb_access_mode, last_doctor_status, last_seen_at)
293
+ ON semicolony.worker_devices TO sc_fabric_bridge_owner;
294
+ GRANT SELECT (bot_id, tenant_id, status)
295
+ ON semicolony.bot_status TO sc_fabric_bridge_owner;
296
+ GRANT SELECT (slug, tenant_id, status)
297
+ ON semicolony.agent_personas TO sc_fabric_bridge_owner;
298
+ GRANT SELECT (id, slug, tenant_type, metadata)
299
+ ON public.tenants TO sc_fabric_bridge_owner;
300
+ REVOKE EXECUTE ON FUNCTION public.current_tenant_id(), public.is_provider()
301
+ FROM PUBLIC, sc_fabric_bridge;
302
+ GRANT EXECUTE ON FUNCTION public.current_tenant_id(), public.is_provider()
303
+ TO sc_app, sc_provider, sc_fabric_bridge_owner;
304
+
305
+ SET ROLE sc_fabric_bridge_owner;
306
+
307
+ CREATE OR REPLACE FUNCTION fabric_bridge.resolve_principal(p_token_hash text)
308
+ RETURNS TABLE (
309
+ credential_id uuid,
310
+ credential_kind text,
311
+ actor_id text,
312
+ principal_id text
313
+ )
314
+ LANGUAGE plpgsql
315
+ SECURITY DEFINER
316
+ SET search_path = pg_catalog
317
+ AS $function$
318
+ DECLARE
319
+ v_credential_id uuid;
320
+ v_tenant_id uuid;
321
+ v_metadata jsonb;
322
+ BEGIN
323
+ IF p_token_hash !~ '^[0-9a-f]{64}$' THEN
324
+ RETURN;
325
+ END IF;
326
+
327
+ SELECT gc.id, gc.tenant_id, gc.metadata
328
+ INTO v_credential_id, v_tenant_id, v_metadata
329
+ FROM semicolony.gateway_credentials gc
330
+ WHERE gc.token_hash = p_token_hash
331
+ AND gc.tenant_slug = 'team-semicolon'
332
+ AND gc.status = 'active'
333
+ AND (gc.expires_at IS NULL OR gc.expires_at > clock_timestamp())
334
+ AND gc.scopes = ARRAY['fabric:query']::text[]
335
+ AND gc.metadata->>'credential_audience' = 'semo-ops:fabric-bridge'
336
+ AND gc.metadata->>'credential_scope' = 'fabric:query'
337
+ AND gc.metadata->>'tenant_anchor' = 'team-semicolon'
338
+ AND COALESCE(gc.metadata->>'lineage_status', 'active') = 'active'
339
+ LIMIT 1;
340
+ IF NOT FOUND THEN
341
+ RETURN;
342
+ END IF;
343
+
344
+ -- Bind RLS to the credential's immutable tenant before consulting tenant-owned rows.
345
+ PERFORM set_config('app.current_tenant_id', v_tenant_id::text, true);
346
+ PERFORM set_config('app.is_provider', 'off', true);
347
+ IF NOT EXISTS (
348
+ SELECT 1
349
+ FROM public.tenants t
350
+ WHERE t.id = v_tenant_id
351
+ AND t.slug = 'team-semicolon'
352
+ AND t.tenant_type = 'team'
353
+ AND COALESCE(t.metadata->>'status', 'active') = 'active'
354
+ ) THEN
355
+ RETURN;
356
+ END IF;
357
+
358
+ IF v_metadata->>'credential_kind' = 'agent'
359
+ AND v_metadata->>'agent_id' = 'colony'
360
+ AND v_metadata->>'actor' = 'agent:team-semicolon:colony'
361
+ AND v_metadata->>'principal_kind' = 'group'
362
+ AND v_metadata->>'principal' = 'team-semicolon:member'
363
+ AND EXISTS (
364
+ SELECT 1
365
+ FROM semicolony.bot_status bs
366
+ WHERE bs.bot_id = 'colony'
367
+ AND bs.tenant_id = v_tenant_id
368
+ AND bs.status IN ('online', 'active')
369
+ )
370
+ AND EXISTS (
371
+ SELECT 1
372
+ FROM semicolony.agent_personas ap
373
+ WHERE ap.slug = 'colony'
374
+ AND ap.tenant_id = v_tenant_id
375
+ AND ap.status = 'active'
376
+ ) THEN
377
+ RETURN QUERY
378
+ SELECT
379
+ v_credential_id,
380
+ 'agent'::text,
381
+ 'agent:team-semicolon:colony'::text,
382
+ 'team-semicolon:member'::text;
383
+ RETURN;
384
+ END IF;
385
+
386
+ IF v_metadata->>'credential_kind' = 'worker'
387
+ AND v_metadata->>'worker_id' ~ '^[a-z][a-z0-9-]{0,63}$'
388
+ AND v_metadata->>'device_id' ~ '^[a-z][a-z0-9-]{0,99}$'
389
+ AND EXISTS (
390
+ SELECT 1
391
+ FROM semicolony.worker_devices wd
392
+ WHERE wd.fabric_gateway_credential_id = v_credential_id
393
+ AND wd.worker_id = v_metadata->>'worker_id'
394
+ AND wd.device_id = v_metadata->>'device_id'
395
+ AND wd.status = 'active'
396
+ AND wd.kb_access_mode = 'worker-gateway'
397
+ AND wd.last_doctor_status = 'pass'
398
+ AND wd.last_seen_at >= clock_timestamp() - interval '24 hours'
399
+ ) THEN
400
+ RETURN QUERY
401
+ SELECT
402
+ v_credential_id,
403
+ 'worker'::text,
404
+ ('worker:team-semicolon:' || (v_metadata->>'worker_id'))::text,
405
+ 'team-semicolon:member'::text;
406
+ END IF;
407
+ END;
408
+ $function$;
409
+
410
+ RESET ROLE;
411
+
412
+ REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA semicolony, public FROM sc_fabric_bridge;
413
+ REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA semicolony, public FROM sc_fabric_bridge;
414
+ REVOKE ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA semicolony, public FROM sc_fabric_bridge;
415
+ REVOKE ALL ON SCHEMA semicolony, public FROM sc_fabric_bridge;
416
+ REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA fabric_bridge
417
+ FROM PUBLIC, sc_app, sc_provider, sc_fabric_bridge;
418
+ REVOKE ALL ON SCHEMA fabric_bridge FROM sc_fabric_bridge;
419
+
420
+ SET ROLE sc_fabric_bridge_owner;
421
+
422
+ GRANT USAGE ON SCHEMA fabric_bridge TO sc_fabric_bridge;
423
+ GRANT EXECUTE ON FUNCTION fabric_bridge.resolve_principal(text) TO sc_fabric_bridge;
424
+
425
+ RESET ROLE;
426
+
427
+ DO $$
428
+ BEGIN
429
+ EXECUTE format('REVOKE sc_fabric_bridge_owner FROM %I', current_user);
430
+ END $$;
431
+
432
+ -- Treat the migration result as a catalog-level security postcondition, not merely
433
+ -- a sequence of successful GRANT/REVOKE statements.
434
+ DO $$
435
+ DECLARE
436
+ v_bridge_oid oid;
437
+ v_owner_oid oid;
438
+ v_schema_oid oid;
439
+ v_resolver_oid oid;
440
+ v_unexpected_objects boolean;
441
+ v_function_count integer;
442
+ v_schema_acl_count integer;
443
+ v_schema_owner_create integer;
444
+ v_schema_owner_usage integer;
445
+ v_schema_bridge_usage integer;
446
+ v_function_acl_count integer;
447
+ v_function_owner_execute integer;
448
+ v_function_bridge_execute integer;
449
+ v_unsafe_default_acl boolean;
450
+ v_global_default_acl_count integer;
451
+ v_global_owner_execute integer;
452
+ BEGIN
453
+ SELECT oid INTO STRICT v_owner_oid
454
+ FROM pg_catalog.pg_roles
455
+ WHERE rolname = 'sc_fabric_bridge_owner';
456
+ SELECT oid INTO STRICT v_bridge_oid
457
+ FROM pg_catalog.pg_roles
458
+ WHERE rolname = 'sc_fabric_bridge';
459
+ SELECT n.oid
460
+ INTO v_schema_oid
461
+ FROM pg_catalog.pg_namespace n
462
+ WHERE n.nspname = 'fabric_bridge'
463
+ AND n.nspowner = v_owner_oid;
464
+
465
+ SELECT EXISTS (
466
+ SELECT 1
467
+ FROM pg_catalog.pg_class c
468
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
469
+ WHERE n.nspname = 'fabric_bridge'
470
+ UNION ALL
471
+ SELECT 1
472
+ FROM pg_catalog.pg_type t
473
+ JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
474
+ WHERE n.nspname = 'fabric_bridge'
475
+ UNION ALL
476
+ SELECT 1
477
+ FROM pg_catalog.pg_collation c
478
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.collnamespace
479
+ WHERE n.nspname = 'fabric_bridge'
480
+ UNION ALL
481
+ SELECT 1
482
+ FROM pg_catalog.pg_conversion c
483
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.connamespace
484
+ WHERE n.nspname = 'fabric_bridge'
485
+ UNION ALL
486
+ SELECT 1
487
+ FROM pg_catalog.pg_operator o
488
+ JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace
489
+ WHERE n.nspname = 'fabric_bridge'
490
+ UNION ALL
491
+ SELECT 1
492
+ FROM pg_catalog.pg_opclass o
493
+ JOIN pg_catalog.pg_namespace n ON n.oid = o.opcnamespace
494
+ WHERE n.nspname = 'fabric_bridge'
495
+ UNION ALL
496
+ SELECT 1
497
+ FROM pg_catalog.pg_opfamily o
498
+ JOIN pg_catalog.pg_namespace n ON n.oid = o.opfnamespace
499
+ WHERE n.nspname = 'fabric_bridge'
500
+ UNION ALL
501
+ SELECT 1
502
+ FROM pg_catalog.pg_statistic_ext s
503
+ JOIN pg_catalog.pg_namespace n ON n.oid = s.stxnamespace
504
+ WHERE n.nspname = 'fabric_bridge'
505
+ UNION ALL
506
+ SELECT 1
507
+ FROM pg_catalog.pg_ts_config t
508
+ JOIN pg_catalog.pg_namespace n ON n.oid = t.cfgnamespace
509
+ WHERE n.nspname = 'fabric_bridge'
510
+ UNION ALL
511
+ SELECT 1
512
+ FROM pg_catalog.pg_ts_dict t
513
+ JOIN pg_catalog.pg_namespace n ON n.oid = t.dictnamespace
514
+ WHERE n.nspname = 'fabric_bridge'
515
+ UNION ALL
516
+ SELECT 1
517
+ FROM pg_catalog.pg_ts_parser t
518
+ JOIN pg_catalog.pg_namespace n ON n.oid = t.prsnamespace
519
+ WHERE n.nspname = 'fabric_bridge'
520
+ UNION ALL
521
+ SELECT 1
522
+ FROM pg_catalog.pg_ts_template t
523
+ JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace
524
+ WHERE n.nspname = 'fabric_bridge'
525
+ UNION ALL
526
+ SELECT 1
527
+ FROM pg_catalog.pg_constraint c
528
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.connamespace
529
+ WHERE n.nspname = 'fabric_bridge'
530
+ UNION ALL
531
+ SELECT 1
532
+ FROM pg_catalog.pg_extension e
533
+ JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace
534
+ WHERE n.nspname = 'fabric_bridge'
535
+ ) INTO v_unexpected_objects;
536
+
537
+ SELECT count(*)
538
+ INTO v_function_count
539
+ FROM pg_catalog.pg_proc p
540
+ JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
541
+ WHERE n.nspname = 'fabric_bridge';
542
+
543
+ SELECT p.oid
544
+ INTO v_resolver_oid
545
+ FROM pg_catalog.pg_proc p
546
+ WHERE p.pronamespace = v_schema_oid
547
+ AND p.proname = 'resolve_principal'
548
+ AND p.prokind = 'f'
549
+ AND p.pronargs = 1
550
+ AND p.proargtypes = '25'::pg_catalog.oidvector
551
+ AND p.proretset
552
+ AND p.prorettype = 2249
553
+ AND p.proallargtypes = ARRAY[25, 2950, 25, 25, 25]::oid[]
554
+ AND p.proargmodes = ARRAY['i', 't', 't', 't', 't']::"char"[]
555
+ AND p.proargnames = ARRAY[
556
+ 'p_token_hash',
557
+ 'credential_id',
558
+ 'credential_kind',
559
+ 'actor_id',
560
+ 'principal_id'
561
+ ]::text[]
562
+ AND p.proowner = v_owner_oid
563
+ AND p.prosecdef
564
+ AND p.proconfig = ARRAY['search_path=pg_catalog']::text[];
565
+
566
+ SELECT
567
+ count(*),
568
+ count(*) FILTER (
569
+ WHERE acl.grantee = v_owner_oid
570
+ AND acl.grantor = v_owner_oid
571
+ AND acl.privilege_type = 'CREATE'
572
+ AND NOT acl.is_grantable
573
+ ),
574
+ count(*) FILTER (
575
+ WHERE acl.grantee = v_owner_oid
576
+ AND acl.grantor = v_owner_oid
577
+ AND acl.privilege_type = 'USAGE'
578
+ AND NOT acl.is_grantable
579
+ ),
580
+ count(*) FILTER (
581
+ WHERE acl.grantee = v_bridge_oid
582
+ AND acl.grantor = v_owner_oid
583
+ AND acl.privilege_type = 'USAGE'
584
+ AND NOT acl.is_grantable
585
+ )
586
+ INTO
587
+ v_schema_acl_count,
588
+ v_schema_owner_create,
589
+ v_schema_owner_usage,
590
+ v_schema_bridge_usage
591
+ FROM pg_catalog.pg_namespace n
592
+ CROSS JOIN LATERAL pg_catalog.aclexplode(
593
+ COALESCE(n.nspacl, pg_catalog.acldefault('n', n.nspowner))
594
+ ) acl
595
+ WHERE n.oid = v_schema_oid;
596
+
597
+ SELECT
598
+ count(*),
599
+ count(*) FILTER (
600
+ WHERE acl.grantee = v_owner_oid
601
+ AND acl.grantor = v_owner_oid
602
+ AND acl.privilege_type = 'EXECUTE'
603
+ AND NOT acl.is_grantable
604
+ ),
605
+ count(*) FILTER (
606
+ WHERE acl.grantee = v_bridge_oid
607
+ AND acl.grantor = v_owner_oid
608
+ AND acl.privilege_type = 'EXECUTE'
609
+ AND NOT acl.is_grantable
610
+ )
611
+ INTO
612
+ v_function_acl_count,
613
+ v_function_owner_execute,
614
+ v_function_bridge_execute
615
+ FROM pg_catalog.pg_proc p
616
+ CROSS JOIN LATERAL pg_catalog.aclexplode(
617
+ COALESCE(p.proacl, pg_catalog.acldefault('f', p.proowner))
618
+ ) acl
619
+ WHERE p.oid = v_resolver_oid;
620
+
621
+ SELECT EXISTS (
622
+ SELECT 1
623
+ FROM pg_catalog.pg_default_acl d
624
+ CROSS JOIN LATERAL pg_catalog.aclexplode(d.defaclacl) acl
625
+ WHERE d.defaclrole = v_owner_oid
626
+ AND d.defaclobjtype = 'f'
627
+ AND (
628
+ d.defaclnamespace = 0
629
+ OR d.defaclnamespace = v_schema_oid
630
+ )
631
+ AND NOT (
632
+ acl.grantee = v_owner_oid
633
+ AND acl.grantor = v_owner_oid
634
+ AND acl.privilege_type = 'EXECUTE'
635
+ AND NOT acl.is_grantable
636
+ )
637
+ ) INTO v_unsafe_default_acl;
638
+
639
+ SELECT
640
+ count(*),
641
+ count(*) FILTER (
642
+ WHERE acl.grantee = v_owner_oid
643
+ AND acl.grantor = v_owner_oid
644
+ AND acl.privilege_type = 'EXECUTE'
645
+ AND NOT acl.is_grantable
646
+ )
647
+ INTO
648
+ v_global_default_acl_count,
649
+ v_global_owner_execute
650
+ FROM pg_catalog.pg_default_acl d
651
+ CROSS JOIN LATERAL pg_catalog.aclexplode(d.defaclacl) acl
652
+ WHERE d.defaclrole = v_owner_oid
653
+ AND d.defaclobjtype = 'f'
654
+ AND d.defaclnamespace = 0;
655
+
656
+ IF v_schema_oid IS NULL
657
+ OR v_resolver_oid IS NULL
658
+ OR v_unexpected_objects
659
+ OR v_function_count <> 1
660
+ OR v_schema_acl_count <> 3
661
+ OR v_schema_owner_create <> 1
662
+ OR v_schema_owner_usage <> 1
663
+ OR v_schema_bridge_usage <> 1
664
+ OR v_function_acl_count <> 2
665
+ OR v_function_owner_execute <> 1
666
+ OR v_function_bridge_execute <> 1
667
+ OR v_unsafe_default_acl
668
+ OR v_global_default_acl_count <> 1
669
+ OR v_global_owner_execute <> 1
670
+ OR EXISTS (
671
+ SELECT 1
672
+ FROM pg_catalog.pg_auth_members m
673
+ WHERE m.roleid IN (v_owner_oid, v_bridge_oid)
674
+ OR m.member IN (v_owner_oid, v_bridge_oid)
675
+ ) THEN
676
+ RAISE EXCEPTION 'fabric bridge postcondition rejected';
677
+ END IF;
678
+ END $$;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-semicolon/semicolony-cli",
3
- "version": "4.18.56",
3
+ "version": "4.18.57",
4
4
  "description": "SemiColony CLI - AI operations and agent orchestration installer",
5
5
  "main": "dist/bundle.js",
6
6
  "bin": {