teleo-infrastructure/ops/gcp_leoclean_runtime_role.sql
Fawaz 77e1336c0f
Some checks are pending
CI / lint-and-test (push) Waiting to run
Restrict leoclean runtime reads to queried columns (#179)
* Restrict leoclean runtime reads to queried columns

* Repair leoclean runtime authority review findings

* Harden leoclean query and CI permission contracts
2026-07-17 06:31:37 -04:00

1459 lines
61 KiB
PL/PgSQL

\set ON_ERROR_STOP on
-- One-time GCP staging provisioning for Leo's Cloud SQL identity. Run this
-- only through ops/provision_gcp_leoclean_runtime_role.sh; psql's \password
-- command encrypts the supplied value client-side, so cleartext is never SQL.
--
-- The login can read the canonical allowlist and can stage a pending proposal
-- through one narrowly-scoped SECURITY DEFINER function. It cannot inherit or
-- SET ROLE into another principal, forge the proposer identity, write directly
-- to canonical/staging tables, or inspect the legacy teleo_restore audit schema.
-- Keep the runtime unable to authenticate until both database phases have
-- completed and verified. Any failure before the final transaction therefore
-- leaves a disabled role instead of a partially provisioned login.
select 'create role leoclean_kb_runtime nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit 8'
where not exists (select 1 from pg_catalog.pg_roles where rolname = 'leoclean_kb_runtime')
\gexec
select 'create role leoclean_kb_stage_owner nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit -1'
where not exists (select 1 from pg_catalog.pg_roles where rolname = 'leoclean_kb_stage_owner')
\gexec
alter role leoclean_kb_runtime
with nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit 8;
alter role leoclean_kb_stage_owner
with nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit -1
password null;
-- Snapshot every non-superuser principal connected to either scoped role before
-- removing the direct membership edges. Existing sessions can retain an
-- already-selected SET ROLE identity after NOLOGIN/revocation, so all other
-- connected sessions must be terminated before password or privilege work.
create temporary table leoclean_fenced_roles (
role_oid oid primary key,
role_name name unique not null
) on commit preserve rows;
with recursive connected(role_oid) as (
values
('leoclean_kb_runtime'::pg_catalog.regrole::oid),
('leoclean_kb_stage_owner'::pg_catalog.regrole::oid)
union
select membership.member
from connected
join pg_catalog.pg_auth_members membership
on membership.roleid = connected.role_oid
)
insert into pg_temp.leoclean_fenced_roles (role_oid, role_name)
select role_row.oid, role_row.rolname
from connected
join pg_catalog.pg_roles role_row on role_row.oid = connected.role_oid
where not role_row.rolsuper;
-- NOINHERIT does not prevent SET ROLE. Remove every direct membership edge in
-- either direction before terminating the snapshotted sessions, closing the
-- race for newly connected membership principals.
select format('revoke %I from %I', granted_role.rolname, member_role.rolname)
from pg_catalog.pg_auth_members membership
join pg_catalog.pg_roles granted_role on granted_role.oid = membership.roleid
join pg_catalog.pg_roles member_role on member_role.oid = membership.member
where granted_role.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
or member_role.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
\gexec
do $session_fence$
declare
target record;
remaining text;
begin
for target in
select activity.pid
from pg_catalog.pg_stat_activity activity
join pg_temp.leoclean_fenced_roles fenced on fenced.role_name = activity.usename
where activity.pid <> pg_catalog.pg_backend_pid()
and activity.backend_type = 'client backend'
loop
if not pg_catalog.pg_terminate_backend(target.pid, 5000) then
raise exception 'could not terminate a pre-existing scoped-role session';
end if;
end loop;
select pg_catalog.string_agg(activity.pid::text, ',' order by activity.pid)
into remaining
from pg_catalog.pg_stat_activity activity
join pg_temp.leoclean_fenced_roles fenced on fenced.role_name = activity.usename
where activity.pid <> pg_catalog.pg_backend_pid()
and activity.backend_type = 'client backend';
if remaining is not null then
raise exception 'pre-existing scoped-role sessions remain after termination';
end if;
select pg_catalog.string_agg(prepared.gid, ',' order by prepared.gid)
into remaining
from pg_catalog.pg_prepared_xacts prepared
join pg_temp.leoclean_fenced_roles fenced on fenced.role_name = prepared.owner;
if remaining is not null then
raise exception 'scoped-role prepared transactions must be resolved before provisioning';
end if;
end
$session_fence$;
-- This is deliberately after the autocommitted NOLOGIN/session/membership
-- fence. Interruption can leave a disabled role, never a still-enabled old
-- login or an old connected session. The wrapper supplies the two prompts.
\password leoclean_kb_runtime
alter role leoclean_kb_runtime reset all;
alter role leoclean_kb_stage_owner reset all;
drop table pg_temp.leoclean_fenced_roles;
select pg_catalog.format(
'alter role %I in database %I reset all',
role_row.rolname,
database_row.datname
)
from pg_catalog.pg_db_role_setting setting_row
join pg_catalog.pg_roles role_row on role_row.oid = setting_row.setrole
join pg_catalog.pg_database database_row on database_row.oid = setting_row.setdatabase
where role_row.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
\gexec
-- PostgreSQL normally grants TEMP to PUBLIC. A role-specific REVOKE cannot
-- override that inherited grant, while REVOKE TEMP FROM PUBLIC would be a
-- database-wide change that could break kb_apply/review or operator workflows.
-- Leave PUBLIC TEMP policy unchanged here and surface it in the final receipt.
-- TEMP cannot shadow this SECURITY DEFINER boundary: pg_catalog is first,
-- pg_temp is explicitly last, and every relation reference is schema-qualified.
-- Database CREATE, direct TEMP grants, and all unexpected direct database ACLs
-- are rejected below. A database-wide TEMP removal requires a separate role-use
-- inventory and explicit grants to every workload that still needs it.
alter role leoclean_kb_runtime in database teleo_canonical reset all;
alter role leoclean_kb_stage_owner in database teleo_canonical reset all;
alter role leoclean_kb_runtime in database teleo_kb reset all;
alter role leoclean_kb_stage_owner in database teleo_kb reset all;
alter role leoclean_kb_runtime in database teleo_canonical
set search_path = pg_catalog, public, kb_stage;
alter role leoclean_kb_runtime in database teleo_canonical
set statement_timeout = '15s';
alter role leoclean_kb_runtime in database teleo_canonical
set lock_timeout = '2s';
\connect teleo_canonical
begin;
-- CONNECT granted to PUBLIC cannot be denied to one role with a role-specific
-- REVOKE. Preserve every existing non-scoped principal's effective CONNECT
-- set as explicit ACLs, remove the cluster-wide PUBLIC grant from every
-- database (including templates), and then grant the runtime its sole target.
-- This runs in the same transaction as every remaining privilege/topology
-- assertion, so a later failure restores the original ACLs atomically.
create temporary table leoclean_preserved_database_connect (
role_name name not null,
database_name name not null,
primary key (role_name, database_name)
) on commit drop;
insert into pg_temp.leoclean_preserved_database_connect (role_name, database_name)
select role_row.rolname, database_row.datname
from pg_catalog.pg_roles role_row
cross join pg_catalog.pg_database database_row
where role_row.rolname not in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
and pg_catalog.has_database_privilege(role_row.oid, database_row.oid, 'CONNECT');
select pg_catalog.format('revoke connect on database %I from public', database_row.datname)
from pg_catalog.pg_database database_row
\gexec
select pg_catalog.format(
'revoke all privileges on database %I from leoclean_kb_runtime, leoclean_kb_stage_owner',
database_row.datname
)
from pg_catalog.pg_database database_row
\gexec
select pg_catalog.format(
'grant connect on database %I to %I',
preserved.database_name,
preserved.role_name
)
from pg_temp.leoclean_preserved_database_connect preserved
order by preserved.database_name, preserved.role_name
\gexec
grant connect on database teleo_canonical to leoclean_kb_runtime;
-- The deploy principal needs temporary ownership authority to replace an
-- existing function on idempotent reruns. This membership is transaction-local
-- and is revoked before verification/commit.
select format('grant leoclean_kb_stage_owner to %I', current_user)
where current_user <> 'leoclean_kb_stage_owner'
\gexec
revoke all on schema public, kb_stage
from leoclean_kb_runtime, leoclean_kb_stage_owner;
revoke all privileges on all tables in schema public, kb_stage
from leoclean_kb_runtime, leoclean_kb_stage_owner;
revoke all privileges on all sequences in schema public, kb_stage
from leoclean_kb_runtime, leoclean_kb_stage_owner;
revoke all privileges on all functions in schema public, kb_stage
from leoclean_kb_runtime, leoclean_kb_stage_owner;
revoke all privileges on all procedures in schema public, kb_stage
from leoclean_kb_runtime, leoclean_kb_stage_owner;
-- Built-in defaults also grant PUBLIC EXECUTE on newly created application
-- routines. Preserve every existing non-scoped role's effective access,
-- remove PUBLIC/scoped execution from the complete application routine set,
-- and restore the inventory explicitly. The reviewed staging function is
-- normalized again after CREATE OR REPLACE below.
create temporary table leoclean_preserved_app_routine_execute (
role_name name not null,
function_oid oid not null,
primary key (role_name, function_oid)
) on commit drop;
insert into pg_temp.leoclean_preserved_app_routine_execute (role_name, function_oid)
select role_row.rolname, function_row.oid
from pg_catalog.pg_roles role_row
cross join pg_catalog.pg_proc function_row
join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
where role_row.rolname not in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
and namespace.nspname in ('public', 'kb_stage')
and pg_catalog.has_function_privilege(role_row.oid, function_row.oid, 'EXECUTE');
select pg_catalog.format(
'revoke execute on %s %I.%I(%s) from public, leoclean_kb_runtime, leoclean_kb_stage_owner',
case when function_row.prokind = 'p' then 'procedure' else 'function' end,
namespace.nspname,
function_row.proname,
pg_catalog.pg_get_function_identity_arguments(function_row.oid)
)
from pg_catalog.pg_proc function_row
join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
where namespace.nspname in ('public', 'kb_stage')
\gexec
select pg_catalog.format(
'grant execute on %s %I.%I(%s) to %I',
case when function_row.prokind = 'p' then 'procedure' else 'function' end,
namespace.nspname,
function_row.proname,
pg_catalog.pg_get_function_identity_arguments(function_row.oid),
preserved.role_name
)
from pg_temp.leoclean_preserved_app_routine_execute preserved
join pg_catalog.pg_proc function_row on function_row.oid = preserved.function_oid
join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
order by namespace.nspname, function_row.proname, preserved.role_name
\gexec
select pg_catalog.format(
'revoke all privileges on large object %s from leoclean_kb_runtime, leoclean_kb_stage_owner',
metadata.oid
)
from pg_catalog.pg_largeobject_metadata metadata
\gexec
-- Default PostgreSQL grants EXECUTE on built-in large-object mutators to
-- PUBLIC, which would let the runtime persist data despite all relation ACLs.
-- Snapshot and explicitly preserve every existing non-scoped role's effective
-- access, then remove PUBLIC and both scoped roles from every mutator overload.
create temporary table leoclean_preserved_lo_execute (
role_name name not null,
function_oid oid not null,
primary key (role_name, function_oid)
) on commit drop;
insert into pg_temp.leoclean_preserved_lo_execute (role_name, function_oid)
select role_row.rolname, function_row.oid
from pg_catalog.pg_roles role_row
cross join pg_catalog.pg_proc function_row
join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
where role_row.rolname not in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
and namespace.nspname = 'pg_catalog'
and function_row.proname in (
'lo_creat', 'lo_create', 'lo_export', 'lo_from_bytea', 'lo_import',
'lo_open', 'lo_put', 'lo_truncate', 'lo_truncate64', 'lo_unlink', 'lowrite'
)
and pg_catalog.has_function_privilege(role_row.oid, function_row.oid, 'EXECUTE');
select pg_catalog.format(
'revoke execute on function pg_catalog.%I(%s) from public, leoclean_kb_runtime, leoclean_kb_stage_owner',
function_row.proname,
pg_catalog.pg_get_function_identity_arguments(function_row.oid)
)
from pg_catalog.pg_proc function_row
join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
where namespace.nspname = 'pg_catalog'
and function_row.proname in (
'lo_creat', 'lo_create', 'lo_export', 'lo_from_bytea', 'lo_import',
'lo_open', 'lo_put', 'lo_truncate', 'lo_truncate64', 'lo_unlink', 'lowrite'
)
\gexec
select pg_catalog.format(
'grant execute on function pg_catalog.%I(%s) to %I',
function_row.proname,
pg_catalog.pg_get_function_identity_arguments(function_row.oid),
preserved.role_name
)
from pg_temp.leoclean_preserved_lo_execute preserved
join pg_catalog.pg_proc function_row on function_row.oid = preserved.function_oid
order by function_row.proname, preserved.role_name
\gexec
select pg_catalog.format(
'revoke all privileges on parameter %I from leoclean_kb_runtime, leoclean_kb_stage_owner',
parameter_acl.parname
)
from pg_catalog.pg_parameter_acl parameter_acl
\gexec
-- Table-level REVOKE does not clear column ACLs. Clear every column ACL for both
-- principals before rebuilding the exact allowlist below.
select format(
'revoke all privileges (%s) on table %I.%I from %I',
pg_catalog.string_agg(pg_catalog.quote_ident(attribute.attname), ', ' order by attribute.attnum),
namespace.nspname,
relation.relname,
target.rolname
)
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
cross join (
values ('leoclean_kb_runtime'), ('leoclean_kb_stage_owner')
) as target(rolname)
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind in ('r', 'p', 'v', 'm', 'f')
and attribute.attnum > 0
and not attribute.attisdropped
group by namespace.nspname, relation.relname, target.rolname
\gexec
grant usage on schema public, kb_stage to leoclean_kb_runtime;
-- Bind runtime reads to the exact 92 columns used by the reviewed adapter.
-- Table-level SELECT is deliberately absent so later columns do not become
-- readable before this manifest and the adapter are reviewed together.
create temporary table leoclean_runtime_read_column_allowlist (
schema_name name not null,
relation_name name not null,
column_name name not null,
column_ordinal smallint not null,
primary key (schema_name, relation_name, column_name),
unique (schema_name, relation_name, column_ordinal)
) on commit drop;
insert into pg_temp.leoclean_runtime_read_column_allowlist (
schema_name,
relation_name,
column_name,
column_ordinal
) values
('public', 'agents', 'id', 1),
('public', 'agents', 'handle', 2),
('public', 'claims', 'id', 1),
('public', 'claims', 'type', 2),
('public', 'claims', 'text', 3),
('public', 'claims', 'status', 4),
('public', 'claims', 'confidence', 5),
('public', 'claims', 'tags', 6),
('public', 'claims', 'superseded_by', 7),
('public', 'claims', 'created_at', 8),
('public', 'claims', 'updated_at', 9),
('public', 'claim_evidence', 'claim_id', 1),
('public', 'claim_evidence', 'source_id', 2),
('public', 'claim_evidence', 'role', 3),
('public', 'claim_evidence', 'weight', 4),
('public', 'claim_edges', 'id', 1),
('public', 'claim_edges', 'from_claim', 2),
('public', 'claim_edges', 'to_claim', 3),
('public', 'claim_edges', 'edge_type', 4),
('public', 'sources', 'id', 1),
('public', 'sources', 'source_type', 2),
('public', 'sources', 'url', 3),
('public', 'sources', 'storage_path', 4),
('public', 'sources', 'excerpt', 5),
('public', 'sources', 'hash', 6),
('public', 'personas', 'agent_id', 1),
('public', 'personas', 'name', 2),
('public', 'personas', 'voice', 3),
('public', 'personas', 'role', 4),
('public', 'personas', 'source_ref', 5),
('public', 'personas', 'lens', 6),
('public', 'strategies', 'agent_id', 1),
('public', 'strategies', 'diagnosis', 2),
('public', 'strategies', 'guiding_policy', 3),
('public', 'strategies', 'proximate_objectives', 4),
('public', 'strategies', 'version', 5),
('public', 'strategies', 'active', 6),
('public', 'beliefs', 'agent_id', 1),
('public', 'beliefs', 'level', 2),
('public', 'beliefs', 'statement', 3),
('public', 'beliefs', 'falsifier', 4),
('public', 'beliefs', 'rank', 5),
('public', 'beliefs', 'status', 6),
('public', 'blindspots', 'agent_id', 1),
('public', 'blindspots', 'name', 2),
('public', 'blindspots', 'description', 3),
('public', 'blindspots', 'correction', 4),
('public', 'blindspots', 'kind', 5),
('public', 'blindspots', 'status', 6),
('public', 'agent_roles', 'agent_id', 1),
('public', 'agent_roles', 'title', 2),
('public', 'agent_roles', 'description', 3),
('public', 'peer_models', 'subject_id', 1),
('public', 'peer_models', 'peer_id', 2),
('public', 'peer_models', 'domain', 3),
('public', 'peer_models', 'outranks_on', 4),
('public', 'peer_models', 'deference_rule', 5),
('public', 'behavioral_rules', 'agent_id', 1),
('public', 'behavioral_rules', 'category', 2),
('public', 'behavioral_rules', 'rule', 3),
('public', 'behavioral_rules', 'rationale', 4),
('public', 'contributor_rules', 'agent_id', 1),
('public', 'contributor_rules', 'name', 2),
('public', 'contributor_rules', 'directive', 3),
('public', 'contributor_rules', 'ci_tier', 4),
('public', 'contributor_rules', 'weighting', 5),
('public', 'contributor_rules', 'rationale', 6),
('public', 'reasoning_tools', 'agent_id', 1),
('public', 'reasoning_tools', 'name', 2),
('public', 'reasoning_tools', 'description', 3),
('public', 'reasoning_tools', 'category', 4),
('public', 'governance_gates', 'agent_id', 1),
('public', 'governance_gates', 'name', 2),
('public', 'governance_gates', 'criteria', 3),
('public', 'governance_gates', 'evidence_bar', 4),
('public', 'governance_gates', 'pass_condition', 5),
('kb_stage', 'kb_proposals', 'id', 1),
('kb_stage', 'kb_proposals', 'proposal_type', 2),
('kb_stage', 'kb_proposals', 'status', 3),
('kb_stage', 'kb_proposals', 'proposed_by_handle', 4),
('kb_stage', 'kb_proposals', 'proposed_by_agent_id', 5),
('kb_stage', 'kb_proposals', 'channel', 6),
('kb_stage', 'kb_proposals', 'source_ref', 7),
('kb_stage', 'kb_proposals', 'rationale', 8),
('kb_stage', 'kb_proposals', 'payload', 9),
('kb_stage', 'kb_proposals', 'reviewed_by_handle', 10),
('kb_stage', 'kb_proposals', 'reviewed_at', 11),
('kb_stage', 'kb_proposals', 'review_note', 12),
('kb_stage', 'kb_proposals', 'applied_by_handle', 13),
('kb_stage', 'kb_proposals', 'applied_at', 14),
('kb_stage', 'kb_proposals', 'created_at', 15),
('kb_stage', 'kb_proposals', 'updated_at', 16);
select pg_catalog.format(
'grant select (%s) on table %I.%I to leoclean_kb_runtime',
pg_catalog.string_agg(
pg_catalog.quote_ident(allowlist.column_name),
', '
order by allowlist.column_ordinal
),
allowlist.schema_name,
allowlist.relation_name
)
from pg_temp.leoclean_runtime_read_column_allowlist allowlist
group by allowlist.schema_name, allowlist.relation_name
order by allowlist.schema_name, allowlist.relation_name
\gexec
-- The function owner is a dedicated NOLOGIN role. Grant only the columns the
-- function reads/inserts. CREATE is temporary and is revoked immediately after
-- ownership is assigned.
grant usage, create on schema kb_stage to leoclean_kb_stage_owner;
grant usage on schema public to leoclean_kb_stage_owner;
grant select (id, handle) on public.agents to leoclean_kb_stage_owner;
grant select on kb_stage.kb_proposals to leoclean_kb_stage_owner;
grant insert (
proposal_type,
status,
proposed_by_handle,
proposed_by_agent_id,
channel,
source_ref,
rationale,
payload
) on kb_stage.kb_proposals to leoclean_kb_stage_owner;
-- Remove the prior caller-supplied-identity overload before installing the
-- session-bound form. A runtime caller can only stage as canonical agent Leo.
drop function if exists kb_stage.stage_leoclean_proposal(text, text, text, text, text, jsonb);
create or replace function kb_stage.stage_leoclean_proposal(
p_proposal_type text,
p_channel text,
p_source_ref text,
p_rationale text,
p_payload jsonb
) returns jsonb
language plpgsql
security definer
set search_path = pg_catalog, pg_temp
as $function$
declare
staged kb_stage.kb_proposals%rowtype;
proposer_id uuid;
begin
if session_user <> 'leoclean_kb_runtime' then
raise exception 'stage_leoclean_proposal is restricted to leoclean_kb_runtime'
using errcode = '42501';
end if;
if p_proposal_type not in ('revise_claim', 'revise_strategy', 'add_edge') then
raise exception 'unsupported Leo proposal type: %', p_proposal_type using errcode = '22023';
end if;
if nullif(btrim(p_source_ref), '') is null then
raise exception 'source_ref is required' using errcode = '22023';
end if;
if nullif(btrim(p_rationale), '') is null then
raise exception 'rationale is required' using errcode = '22023';
end if;
if p_payload is null or jsonb_typeof(p_payload) <> 'object' then
raise exception 'proposal payload must be a JSON object' using errcode = '22023';
end if;
select agent.id
into proposer_id
from public.agents as agent
where agent.handle = 'leo';
if proposer_id is null then
raise exception 'canonical Leo agent row is required' using errcode = '23503';
end if;
insert into kb_stage.kb_proposals (
proposal_type,
status,
proposed_by_handle,
proposed_by_agent_id,
channel,
source_ref,
rationale,
payload
) values (
p_proposal_type,
'pending_review',
'leo',
proposer_id,
coalesce(nullif(p_channel, ''), 'telegram'),
p_source_ref,
p_rationale,
p_payload
)
returning * into staged;
return to_jsonb(staged);
end
$function$;
alter function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb)
owner to leoclean_kb_stage_owner;
revoke create on schema kb_stage from leoclean_kb_stage_owner;
-- Normalize explicit ACL entries left by CREATE OR REPLACE or an earlier grant.
-- A newly-created function starts with owner EXECUTE, while CREATE OR REPLACE
-- preserves an existing ACL on reruns. Revoke every scoped/default entry here
-- so the first successful provision already has the same runtime-only ACL as
-- every later provision.
revoke all on function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb)
from public, leoclean_kb_runtime, leoclean_kb_stage_owner;
select format(
'revoke all on function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb) from %I',
grantee.rolname
)
from pg_catalog.pg_proc function_row
cross join lateral pg_catalog.aclexplode(
coalesce(
function_row.proacl,
pg_catalog.acldefault('f', function_row.proowner)
)
) as acl
join pg_catalog.pg_roles grantee on grantee.oid = acl.grantee
where function_row.oid = 'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)'::pg_catalog.regprocedure
\gexec
grant execute on function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb)
to leoclean_kb_runtime;
select format('revoke leoclean_kb_stage_owner from %I', current_user)
where current_user <> 'leoclean_kb_stage_owner'
\gexec
do $verification$
declare
unexpected text;
runtime_oid oid := 'leoclean_kb_runtime'::pg_catalog.regrole::oid;
owner_oid oid := 'leoclean_kb_stage_owner'::pg_catalog.regrole::oid;
stage_function oid := 'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)'::pg_catalog.regprocedure::oid;
begin
if exists (
select 1
from pg_catalog.pg_roles role_row
where role_row.rolname = 'leoclean_kb_runtime'
and (role_row.rolcanlogin
or role_row.rolsuper
or role_row.rolcreatedb
or role_row.rolcreaterole
or role_row.rolinherit
or role_row.rolreplication
or role_row.rolbypassrls
or role_row.rolconnlimit <> 8)
) then
raise exception 'leoclean_kb_runtime has unsafe provisioning role attributes';
end if;
if exists (
select 1
from pg_catalog.pg_roles role_row
where role_row.rolname = 'leoclean_kb_stage_owner'
and (role_row.rolcanlogin
or role_row.rolsuper
or role_row.rolcreatedb
or role_row.rolcreaterole
or role_row.rolinherit
or role_row.rolreplication
or role_row.rolbypassrls
or role_row.rolconnlimit <> -1)
) then
raise exception 'leoclean_kb_stage_owner has unsafe role attributes';
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I->%I', granted_role.rolname, member_role.rolname),
', '
order by granted_role.rolname, member_role.rolname
)
into unexpected
from pg_catalog.pg_auth_members membership
join pg_catalog.pg_roles granted_role on granted_role.oid = membership.roleid
join pg_catalog.pg_roles member_role on member_role.oid = membership.member
where membership.roleid in (runtime_oid, owner_oid)
or membership.member in (runtime_oid, owner_oid);
if unexpected is not null then
raise exception 'scoped Leo roles retain role memberships: %', unexpected;
end if;
if not exists (
select 1
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
where namespace.nspname = 'kb_stage'
and relation.relname = 'kb_proposals'
and relation.relkind = 'r'
and relation.relpersistence = 'p'
and not relation.relrowsecurity
and not relation.relforcerowsecurity
) then
raise exception 'kb_stage.kb_proposals is not the reviewed permanent base table';
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I:%s', namespace.nspname, relation.relname, relation.relkind),
', '
order by namespace.nspname, relation.relname
)
into unexpected
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
where (namespace.nspname, relation.relname) in (
('public', 'agents'), ('public', 'claims'), ('public', 'claim_evidence'),
('public', 'claim_edges'), ('public', 'sources'), ('public', 'personas'),
('public', 'strategies'), ('public', 'beliefs'), ('public', 'blindspots'),
('public', 'agent_roles'), ('public', 'peer_models'), ('public', 'behavioral_rules'),
('public', 'contributor_rules'), ('public', 'reasoning_tools'),
('public', 'governance_gates'), ('kb_stage', 'kb_proposals')
)
and relation.relkind <> 'r';
if unexpected is not null then
raise exception 'Leo read allowlist contains a non-base relation: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%s->%s', inheritance.inhparent::pg_catalog.regclass, inheritance.inhrelid::pg_catalog.regclass),
', '
)
into unexpected
from pg_catalog.pg_inherits inheritance
where inheritance.inhparent in (
'public.agents'::pg_catalog.regclass, 'public.claims'::pg_catalog.regclass,
'public.claim_evidence'::pg_catalog.regclass, 'public.claim_edges'::pg_catalog.regclass,
'public.sources'::pg_catalog.regclass, 'public.personas'::pg_catalog.regclass,
'public.strategies'::pg_catalog.regclass, 'public.beliefs'::pg_catalog.regclass,
'public.blindspots'::pg_catalog.regclass, 'public.agent_roles'::pg_catalog.regclass,
'public.peer_models'::pg_catalog.regclass, 'public.behavioral_rules'::pg_catalog.regclass,
'public.contributor_rules'::pg_catalog.regclass, 'public.reasoning_tools'::pg_catalog.regclass,
'public.governance_gates'::pg_catalog.regclass, 'kb_stage.kb_proposals'::pg_catalog.regclass
)
or inheritance.inhrelid in (
'public.agents'::pg_catalog.regclass, 'public.claims'::pg_catalog.regclass,
'public.claim_evidence'::pg_catalog.regclass, 'public.claim_edges'::pg_catalog.regclass,
'public.sources'::pg_catalog.regclass, 'public.personas'::pg_catalog.regclass,
'public.strategies'::pg_catalog.regclass, 'public.beliefs'::pg_catalog.regclass,
'public.blindspots'::pg_catalog.regclass, 'public.agent_roles'::pg_catalog.regclass,
'public.peer_models'::pg_catalog.regclass, 'public.behavioral_rules'::pg_catalog.regclass,
'public.contributor_rules'::pg_catalog.regclass, 'public.reasoning_tools'::pg_catalog.regclass,
'public.governance_gates'::pg_catalog.regclass, 'kb_stage.kb_proposals'::pg_catalog.regclass
);
if unexpected is not null then
raise exception 'Leo read allowlist has inheritance edges: %', unexpected;
end if;
with expected(ordinal, attname, typname, not_null, has_default, collated) as (
values
(1, 'id', 'uuid', true, true, false),
(2, 'proposal_type', 'text', true, false, true),
(3, 'status', 'text', true, true, true),
(4, 'proposed_by_handle', 'text', false, false, true),
(5, 'proposed_by_agent_id', 'uuid', false, false, false),
(6, 'channel', 'text', true, true, true),
(7, 'source_ref', 'text', false, false, true),
(8, 'rationale', 'text', true, false, true),
(9, 'payload', 'jsonb', true, false, false),
(10, 'reviewed_by_handle', 'text', false, false, true),
(11, 'reviewed_by_agent_id', 'uuid', false, false, false),
(12, 'reviewed_at', 'timestamptz', false, false, false),
(13, 'review_note', 'text', false, false, true),
(14, 'applied_by_handle', 'text', false, false, true),
(15, 'applied_by_agent_id', 'uuid', false, false, false),
(16, 'applied_at', 'timestamptz', false, false, false),
(17, 'created_at', 'timestamptz', true, true, false),
(18, 'updated_at', 'timestamptz', true, true, false)
), actual as (
select attribute.attnum::int as ordinal,
attribute.attname,
type_row.typname,
attribute.attnotnull as not_null,
attribute.atthasdef as has_default,
attribute.attidentity,
attribute.attgenerated,
attribute.atttypmod,
type_namespace.nspname as type_namespace,
attribute.attcollation = 'pg_catalog.default'::pg_catalog.regcollation as collated
from pg_catalog.pg_attribute attribute
join pg_catalog.pg_type type_row on type_row.oid = attribute.atttypid
join pg_catalog.pg_namespace type_namespace on type_namespace.oid = type_row.typnamespace
where attribute.attrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
and attribute.attnum > 0
and not attribute.attisdropped
)
select pg_catalog.string_agg(coalesce(expected.attname, actual.attname), ', ' order by coalesce(expected.ordinal, actual.ordinal))
into unexpected
from expected
full join actual using (ordinal, attname)
where expected.ordinal is null
or actual.ordinal is null
or expected.typname is distinct from actual.typname
or expected.not_null is distinct from actual.not_null
or expected.has_default is distinct from actual.has_default
or expected.collated is distinct from actual.collated
or actual.attidentity <> ''
or actual.attgenerated <> ''
or actual.atttypmod <> -1
or actual.type_namespace <> 'pg_catalog';
if unexpected is not null then
raise exception 'kb_stage.kb_proposals column contract drifted: %', unexpected;
end if;
if exists (
select 1
from pg_catalog.pg_trigger trigger_row
where trigger_row.tgrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
and not trigger_row.tgisinternal
) or exists (
select 1
from pg_catalog.pg_rewrite rewrite_row
where rewrite_row.ev_class = 'kb_stage.kb_proposals'::pg_catalog.regclass
) or exists (
select 1
from pg_catalog.pg_policy policy_row
where policy_row.polrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
) or exists (
select 1
from pg_catalog.pg_attribute attribute
where attribute.attrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
and attribute.attnum > 0
and not attribute.attisdropped
and attribute.attgenerated <> ''
) then
raise exception 'kb_stage.kb_proposals has unsafe trigger/rule/RLS/generated topology';
end if;
with expected(attname, expression) as (
values
('id', 'gen_random_uuid()'),
('status', '''pending_review''::text'),
('channel', '''cli''::text'),
('created_at', 'now()'),
('updated_at', 'now()')
), actual as (
select attribute.attname,
pg_catalog.pg_get_expr(default_row.adbin, default_row.adrelid) as expression
from pg_catalog.pg_attribute attribute
join pg_catalog.pg_attrdef default_row
on default_row.adrelid = attribute.attrelid
and default_row.adnum = attribute.attnum
where attribute.attrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
and attribute.attnum > 0
and not attribute.attisdropped
)
select pg_catalog.string_agg(coalesce(expected.attname, actual.attname), ', ')
into unexpected
from expected
full join actual using (attname)
where expected.expression is distinct from actual.expression;
if unexpected is not null then
raise exception 'kb_stage.kb_proposals default topology drifted: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%s:%s', dependency.refclassid::pg_catalog.regclass, dependency.refobjid),
', '
)
into unexpected
from pg_catalog.pg_attrdef default_row
join pg_catalog.pg_depend dependency
on dependency.classid = 'pg_catalog.pg_attrdef'::pg_catalog.regclass
and dependency.objid = default_row.oid
where default_row.adrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
and (
dependency.refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
and dependency.refobjid not in (
'pg_catalog.gen_random_uuid()'::pg_catalog.regprocedure,
'pg_catalog.now()'::pg_catalog.regprocedure
)
or dependency.refclassid = 'pg_catalog.pg_type'::pg_catalog.regclass
and not exists (
select 1
from pg_catalog.pg_type type_row
join pg_catalog.pg_namespace namespace on namespace.oid = type_row.typnamespace
where type_row.oid = dependency.refobjid
and namespace.nspname = 'pg_catalog'
)
or dependency.refclassid = 'pg_catalog.pg_operator'::pg_catalog.regclass
and not exists (
select 1
from pg_catalog.pg_operator operator_row
join pg_catalog.pg_namespace namespace on namespace.oid = operator_row.oprnamespace
where operator_row.oid = dependency.refobjid
and namespace.nspname = 'pg_catalog'
)
or dependency.refclassid = 'pg_catalog.pg_collation'::pg_catalog.regclass
and not exists (
select 1
from pg_catalog.pg_collation collation_row
join pg_catalog.pg_namespace namespace on namespace.oid = collation_row.collnamespace
where collation_row.oid = dependency.refobjid
and namespace.nspname = 'pg_catalog'
)
);
if unexpected is not null then
raise exception 'kb_stage.kb_proposals defaults depend on unreviewed objects: %', unexpected;
end if;
if (
select pg_catalog.count(*) <> 6
or pg_catalog.count(*) filter (
where (constraint_row.conname, constraint_row.contype) in (
('kb_proposals_pkey', 'p'),
('kb_proposals_proposal_type_check', 'c'),
('kb_proposals_status_check', 'c'),
('kb_proposals_applied_by_agent_id_fkey', 'f'),
('kb_proposals_proposed_by_agent_id_fkey', 'f'),
('kb_proposals_reviewed_by_agent_id_fkey', 'f')
)
) <> 6
from pg_catalog.pg_constraint constraint_row
where constraint_row.conrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
) or exists (
select 1
from pg_catalog.pg_constraint constraint_row
join pg_catalog.pg_depend dependency
on dependency.classid = 'pg_catalog.pg_constraint'::pg_catalog.regclass
and dependency.objid = constraint_row.oid
and dependency.refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
join pg_catalog.pg_proc function_row on function_row.oid = dependency.refobjid
join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
where constraint_row.conrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
and namespace.nspname <> 'pg_catalog'
) or exists (
select 1
from pg_catalog.pg_index index_row
where index_row.indrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
and (
index_row.indexprs is not null
or (
index_row.indpred is not null
and pg_catalog.pg_get_expr(index_row.indpred, index_row.indrelid)
<> '(proposed_by_handle IS NOT NULL)'
)
)
) then
raise exception 'kb_stage.kb_proposals constraint/index topology drifted';
end if;
select pg_catalog.string_agg(
pg_catalog.format(
'%I:%s:%s:grantable=%s',
database_row.datname,
grantee.rolname,
acl.privilege_type,
acl.is_grantable
),
', '
order by database_row.datname, grantee.rolname, acl.privilege_type
)
into unexpected
from pg_catalog.pg_database database_row
cross join lateral pg_catalog.aclexplode(
coalesce(
database_row.datacl,
pg_catalog.acldefault('d', database_row.datdba)
)
) as acl
join pg_catalog.pg_roles grantee on grantee.oid = acl.grantee
where grantee.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
and not (
database_row.datname = 'teleo_canonical'
and grantee.rolname = 'leoclean_kb_runtime'
and acl.privilege_type = 'CONNECT'
and not acl.is_grantable
);
if unexpected is not null then
raise exception 'scoped Leo roles retain unexpected direct database ACLs: %', unexpected;
end if;
select pg_catalog.string_agg(database_row.datname, ', ' order by database_row.datname)
into unexpected
from pg_catalog.pg_database database_row
where database_row.datname <> 'teleo_canonical'
and has_database_privilege('leoclean_kb_runtime', database_row.oid, 'CONNECT');
if unexpected is not null then
raise exception 'runtime CONNECT reaches a noncanonical database: %', unexpected;
end if;
select pg_catalog.string_agg(database_row.datname, ', ' order by database_row.datname)
into unexpected
from pg_catalog.pg_database database_row
where has_database_privilege('leoclean_kb_stage_owner', database_row.oid, 'CONNECT');
if unexpected is not null then
raise exception 'stage owner CONNECT reaches a database: %', unexpected;
end if;
select pg_catalog.string_agg(database_row.datname, ', ' order by database_row.datname)
into unexpected
from pg_catalog.pg_database database_row
cross join lateral pg_catalog.aclexplode(
coalesce(database_row.datacl, pg_catalog.acldefault('d', database_row.datdba))
) acl
where acl.grantee = 0
and acl.privilege_type = 'CONNECT';
if unexpected is not null then
raise exception 'PUBLIC retains database CONNECT: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%s:%s', setting_row.setdatabase, setting_row.setconfig),
', '
)
into unexpected
from pg_catalog.pg_db_role_setting setting_row
where setting_row.setrole in (runtime_oid, owner_oid)
and not (
setting_row.setrole = runtime_oid
and setting_row.setdatabase = (
select database_row.oid
from pg_catalog.pg_database database_row
where database_row.datname = 'teleo_canonical'
)
and pg_catalog.cardinality(setting_row.setconfig) = 3
and setting_row.setconfig @> array[
'search_path=pg_catalog, public, kb_stage',
'statement_timeout=15s',
'lock_timeout=2s'
]::text[]
);
if unexpected is not null or not exists (
select 1
from pg_catalog.pg_db_role_setting setting_row
where setting_row.setrole = runtime_oid
and setting_row.setdatabase = (
select database_row.oid
from pg_catalog.pg_database database_row
where database_row.datname = 'teleo_canonical'
)
and pg_catalog.cardinality(setting_row.setconfig) = 3
and setting_row.setconfig @> array[
'search_path=pg_catalog, public, kb_stage',
'statement_timeout=15s',
'lock_timeout=2s'
]::text[]
) then
raise exception 'scoped Leo role settings differ from the reviewed contract';
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I:%s', database_row.datname, scoped_role.name),
', '
order by database_row.datname, scoped_role.name
)
into unexpected
from pg_catalog.pg_database database_row
cross join (values ('leoclean_kb_runtime'), ('leoclean_kb_stage_owner')) scoped_role(name)
where has_database_privilege(scoped_role.name, database_row.oid, 'CREATE');
if unexpected is not null then
raise exception 'a scoped Leo role unexpectedly has database CREATE: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%s:%s:%s', dependency.classid::pg_catalog.regclass, dependency.objid, dependency.objsubid),
', '
)
into unexpected
from pg_catalog.pg_shdepend dependency
where dependency.refclassid = 'pg_catalog.pg_authid'::pg_catalog.regclass
and dependency.refobjid = runtime_oid
and dependency.deptype = 'o';
if unexpected is not null then
raise exception 'leoclean_kb_runtime unexpectedly owns database objects: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%s:%s:%s', dependency.classid::pg_catalog.regclass, dependency.objid, dependency.objsubid),
', '
)
into unexpected
from pg_catalog.pg_shdepend dependency
where dependency.refclassid = 'pg_catalog.pg_authid'::pg_catalog.regclass
and dependency.refobjid = owner_oid
and dependency.deptype = 'o'
and not (
dependency.dbid = (select database_row.oid from pg_catalog.pg_database database_row where database_row.datname = current_database())
and
dependency.classid = 'pg_catalog.pg_proc'::pg_catalog.regclass
and dependency.objid = stage_function
and dependency.objsubid = 0
);
if unexpected is not null then
raise exception 'leoclean_kb_stage_owner owns unexpected database objects: %', unexpected;
end if;
select pg_catalog.string_agg(metadata.oid::text, ', ' order by metadata.oid)
into unexpected
from pg_catalog.pg_largeobject_metadata metadata
where metadata.lomowner in (runtime_oid, owner_oid)
or exists (
select 1
from pg_catalog.aclexplode(
coalesce(metadata.lomacl, pg_catalog.acldefault('L', metadata.lomowner))
) acl
where acl.grantee in (0, runtime_oid, owner_oid)
and acl.privilege_type in ('SELECT', 'UPDATE')
);
if unexpected is not null then
raise exception 'scoped Leo roles retain large-object ownership or ACL reachability: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I(%s)', function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid)),
', '
)
into unexpected
from pg_catalog.pg_proc function_row
join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
where namespace.nspname = 'pg_catalog'
and function_row.proname in (
'lo_creat', 'lo_create', 'lo_export', 'lo_from_bytea', 'lo_import',
'lo_open', 'lo_put', 'lo_truncate', 'lo_truncate64', 'lo_unlink', 'lowrite'
)
and (
has_function_privilege('leoclean_kb_runtime', function_row.oid, 'EXECUTE')
or has_function_privilege('leoclean_kb_stage_owner', function_row.oid, 'EXECUTE')
);
if unexpected is not null then
raise exception 'PUBLIC or inherited large-object mutators remain executable: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I(%s)', function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid)),
', '
order by function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid)
)
into unexpected
from pg_catalog.pg_proc function_row
join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
cross join lateral pg_catalog.aclexplode(
coalesce(function_row.proacl, pg_catalog.acldefault('f', function_row.proowner))
) acl
where namespace.nspname = 'pg_catalog'
and function_row.proname in (
'lo_creat', 'lo_create', 'lo_export', 'lo_from_bytea', 'lo_import',
'lo_open', 'lo_put', 'lo_truncate', 'lo_truncate64', 'lo_unlink', 'lowrite'
)
and acl.grantee = 0
and acl.privilege_type = 'EXECUTE';
if unexpected is not null then
raise exception 'PUBLIC retains large-object mutator EXECUTE: %', unexpected;
end if;
select pg_catalog.string_agg(parameter_acl.parname, ', ' order by parameter_acl.parname)
into unexpected
from pg_catalog.pg_parameter_acl parameter_acl
where has_parameter_privilege('leoclean_kb_runtime', parameter_acl.parname, 'SET')
or has_parameter_privilege('leoclean_kb_runtime', parameter_acl.parname, 'ALTER SYSTEM')
or has_parameter_privilege('leoclean_kb_stage_owner', parameter_acl.parname, 'SET')
or has_parameter_privilege('leoclean_kb_stage_owner', parameter_acl.parname, 'ALTER SYSTEM');
if unexpected is not null then
raise exception 'scoped Leo roles retain parameter privileges: %', unexpected;
end if;
if has_schema_privilege('leoclean_kb_runtime', 'public', 'CREATE')
or has_schema_privilege('leoclean_kb_runtime', 'kb_stage', 'CREATE')
or has_schema_privilege('leoclean_kb_stage_owner', 'public', 'CREATE')
or has_schema_privilege('leoclean_kb_stage_owner', 'kb_stage', 'CREATE') then
raise exception 'a scoped Leo role unexpectedly has schema CREATE';
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I:%s', namespace.nspname, relation.relname, privilege.name),
', '
order by namespace.nspname, relation.relname, privilege.name
)
into unexpected
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
cross join (values ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')) privilege(name)
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind in ('r', 'p', 'v', 'm', 'f')
and has_table_privilege('leoclean_kb_runtime', relation.oid, privilege.name);
if unexpected is not null then
raise exception 'leoclean_kb_runtime unexpectedly has table-level DML: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I.%I:%s', namespace.nspname, relation.relname, attribute.attname, privilege.name),
', '
order by namespace.nspname, relation.relname, attribute.attnum, privilege.name
)
into unexpected
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
cross join (values ('INSERT'), ('UPDATE'), ('REFERENCES')) privilege(name)
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind in ('r', 'p', 'v', 'm', 'f')
and attribute.attnum > 0
and not attribute.attisdropped
and has_column_privilege('leoclean_kb_runtime', relation.oid, attribute.attnum, privilege.name);
if unexpected is not null then
raise exception 'leoclean_kb_runtime unexpectedly has column-level DML: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I', namespace.nspname, relation.relname),
', '
order by namespace.nspname, relation.relname
)
into unexpected
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind in ('r', 'p', 'v', 'm', 'f')
and has_table_privilege('leoclean_kb_runtime', relation.oid, 'SELECT');
if unexpected is not null then
raise exception 'leoclean_kb_runtime unexpectedly has table-level SELECT: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I.%I', allowlist.schema_name, allowlist.relation_name, allowlist.column_name),
', '
order by allowlist.schema_name, allowlist.relation_name, allowlist.column_ordinal
)
into unexpected
from pg_temp.leoclean_runtime_read_column_allowlist allowlist
left join pg_catalog.pg_namespace namespace
on namespace.nspname = allowlist.schema_name
left join pg_catalog.pg_class relation
on relation.relnamespace = namespace.oid
and relation.relname = allowlist.relation_name
left join pg_catalog.pg_attribute attribute
on attribute.attrelid = relation.oid
and attribute.attname = allowlist.column_name
and attribute.attnum > 0
and not attribute.attisdropped
where attribute.attnum is null
or not coalesce(
has_column_privilege('leoclean_kb_runtime', relation.oid, attribute.attnum, 'SELECT'),
false
);
if unexpected is not null then
raise exception 'leoclean_kb_runtime is missing required column SELECT: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I.%I', namespace.nspname, relation.relname, attribute.attname),
', '
order by namespace.nspname, relation.relname, attribute.attnum
)
into unexpected
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
left join pg_temp.leoclean_runtime_read_column_allowlist allowlist
on allowlist.schema_name = namespace.nspname
and allowlist.relation_name = relation.relname
and allowlist.column_name = attribute.attname
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind in ('r', 'p', 'v', 'm', 'f')
and attribute.attnum > 0
and not attribute.attisdropped
and has_column_privilege('leoclean_kb_runtime', relation.oid, attribute.attnum, 'SELECT')
and allowlist.column_name is null;
if unexpected is not null then
raise exception 'leoclean_kb_runtime has column SELECT outside its allowlist: %', unexpected;
end if;
if has_table_privilege('leoclean_kb_stage_owner', 'public.agents', 'SELECT')
or not has_column_privilege('leoclean_kb_stage_owner', 'public.agents', 'id', 'SELECT')
or not has_column_privilege('leoclean_kb_stage_owner', 'public.agents', 'handle', 'SELECT')
or not has_table_privilege('leoclean_kb_stage_owner', 'kb_stage.kb_proposals', 'SELECT')
or has_table_privilege('leoclean_kb_stage_owner', 'kb_stage.kb_proposals', 'INSERT') then
raise exception 'leoclean_kb_stage_owner does not have the exact read/insert grant shape';
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I.%I', namespace.nspname, relation.relname, attribute.attname),
', '
order by namespace.nspname, relation.relname, attribute.attnum
)
into unexpected
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind in ('r', 'p', 'v', 'm', 'f')
and attribute.attnum > 0
and not attribute.attisdropped
and has_column_privilege('leoclean_kb_stage_owner', relation.oid, attribute.attnum, 'INSERT')
and not (
namespace.nspname = 'kb_stage'
and relation.relname = 'kb_proposals'
and attribute.attname in (
'proposal_type',
'status',
'proposed_by_handle',
'proposed_by_agent_id',
'channel',
'source_ref',
'rationale',
'payload'
)
);
if unexpected is not null then
raise exception 'leoclean_kb_stage_owner can INSERT unexpected columns: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I:%s', namespace.nspname, relation.relname, privilege.name),
', '
order by namespace.nspname, relation.relname, privilege.name
)
into unexpected
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
cross join (values ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')) privilege(name)
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind in ('r', 'p', 'v', 'm', 'f')
and has_table_privilege('leoclean_kb_stage_owner', relation.oid, privilege.name);
if unexpected is not null then
raise exception 'leoclean_kb_stage_owner unexpectedly has table-level DML: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I.%I:%s', namespace.nspname, relation.relname, attribute.attname, privilege.name),
', '
order by namespace.nspname, relation.relname, attribute.attnum, privilege.name
)
into unexpected
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
cross join (values ('UPDATE'), ('REFERENCES')) privilege(name)
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind in ('r', 'p', 'v', 'm', 'f')
and attribute.attnum > 0
and not attribute.attisdropped
and has_column_privilege('leoclean_kb_stage_owner', relation.oid, attribute.attnum, privilege.name);
if unexpected is not null then
raise exception 'leoclean_kb_stage_owner unexpectedly has column-level DML: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I.%I', namespace.nspname, relation.relname, attribute.attname),
', '
order by namespace.nspname, relation.relname, attribute.attnum
)
into unexpected
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind in ('r', 'p', 'v', 'm', 'f')
and attribute.attnum > 0
and not attribute.attisdropped
and has_column_privilege('leoclean_kb_stage_owner', relation.oid, attribute.attnum, 'SELECT')
and not (
(namespace.nspname = 'kb_stage' and relation.relname = 'kb_proposals')
or (
namespace.nspname = 'public'
and relation.relname = 'agents'
and attribute.attname in ('id', 'handle')
)
);
if unexpected is not null then
raise exception 'leoclean_kb_stage_owner can SELECT unexpected columns: %', unexpected;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I:%s:%s', namespace.nspname, relation.relname, scoped_role.name, privilege.name),
', '
order by namespace.nspname, relation.relname, scoped_role.name, privilege.name
)
into unexpected
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
cross join (values ('leoclean_kb_runtime'), ('leoclean_kb_stage_owner')) scoped_role(name)
cross join (values ('USAGE'), ('SELECT'), ('UPDATE')) privilege(name)
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind = 'S'
and has_sequence_privilege(scoped_role.name, relation.oid, privilege.name);
if unexpected is not null then
raise exception 'a scoped Leo role unexpectedly has sequence privileges: %', unexpected;
end if;
if exists (
select 1
from pg_catalog.pg_attribute attribute
where attribute.attrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
and attribute.attname in (
'proposal_type',
'status',
'proposed_by_handle',
'proposed_by_agent_id',
'channel',
'source_ref',
'rationale',
'payload'
)
and not has_column_privilege(
'leoclean_kb_stage_owner',
attribute.attrelid,
attribute.attnum,
'INSERT'
)
) then
raise exception 'leoclean_kb_stage_owner is missing a required INSERT column grant';
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I(%s)', namespace.nspname, function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid)),
', '
)
into unexpected
from pg_catalog.pg_proc function_row
join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
where namespace.nspname in ('public', 'kb_stage')
and function_row.prosecdef
and function_row.oid <> stage_function
and has_function_privilege('leoclean_kb_runtime', function_row.oid, 'EXECUTE');
if unexpected is not null then
raise exception 'leoclean_kb_runtime can execute unexpected SECURITY DEFINER functions: %', unexpected;
end if;
if not exists (
select 1
from pg_catalog.pg_proc function_row
where function_row.oid = stage_function
and function_row.prosecdef
and function_row.proowner = owner_oid
and function_row.proconfig = array['search_path=pg_catalog, pg_temp']::text[]
) then
raise exception 'stage_leoclean_proposal owner/security/search_path contract is invalid';
end if;
select pg_catalog.string_agg(
pg_catalog.format(
'grantee=%s privilege=%s grantable=%s',
acl.grantee,
acl.privilege_type,
acl.is_grantable
),
', '
)
into unexpected
from pg_catalog.pg_proc function_row
cross join lateral pg_catalog.aclexplode(
coalesce(
function_row.proacl,
pg_catalog.acldefault('f', function_row.proowner)
)
) as acl
where function_row.oid = stage_function
and not (
acl.grantee = runtime_oid
and acl.privilege_type = 'EXECUTE'
and not acl.is_grantable
);
if unexpected is not null then
raise exception 'stage_leoclean_proposal has unexpected ACL entries: %', unexpected;
end if;
if not has_function_privilege(
'leoclean_kb_runtime',
'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)',
'EXECUTE'
) then
raise exception 'leoclean_kb_runtime cannot execute stage_leoclean_proposal';
end if;
if has_function_privilege(
'leoclean_kb_stage_owner',
'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)',
'EXECUTE'
) then
raise exception 'leoclean_kb_stage_owner must not execute stage_leoclean_proposal';
end if;
end
$verification$;
-- LOGIN is the last state change in the same transaction as every database ACL,
-- canonical privilege, large-object, and topology assertion. Any failure rolls
-- back the complete privilege migration and leaves the role NOLOGIN.
alter role leoclean_kb_runtime
with login nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit 8;
do $login_verification$
begin
if exists (
select 1
from pg_catalog.pg_roles role_row
where role_row.rolname = 'leoclean_kb_runtime'
and (
not role_row.rolcanlogin
or role_row.rolsuper
or role_row.rolcreatedb
or role_row.rolcreaterole
or role_row.rolinherit
or role_row.rolreplication
or role_row.rolbypassrls
or role_row.rolconnlimit <> 8
)
) then
raise exception 'leoclean_kb_runtime final login attributes are unsafe';
end if;
end
$login_verification$;
commit;