teleo-infrastructure/tests/test_gcp_leoclean_runtime_role_postgres.py

1036 lines
42 KiB
Python

from __future__ import annotations
import importlib.util
import json
import os
import re
import shutil
import subprocess
import time
import uuid
from pathlib import Path
from types import ModuleType
import pytest
import yaml
from ops import verify_gcp_leoclean_runtime_permissions as verifier
REPO_ROOT = Path(__file__).resolve().parents[1]
ROLE_SQL = REPO_ROOT / "ops" / "gcp_leoclean_runtime_role.sql"
RUNTIME_TOOL = REPO_ROOT / "hermes-agent" / "leoclean-bin" / "cloudsql_memory_tool.py"
RUN_ENV = "TELEO_RUN_LEOCLEAN_RUNTIME_POSTGRES_CANARY"
POSTGRES_IMAGE = "postgres@sha256:eb4759788a2182f08257135e61a34f2cfc3c2914079f3465d64ee62350f4d081"
POSTGRES_VERSION_NUM = "160014"
DATABASE = "teleo_canonical"
RUNTIME_ROLE = "leoclean_kb_runtime"
LOCAL_RUNTIME_PASSWORD = "disposable-r2-runtime-password"
IDENTIFIER_RE = re.compile(r"[a-z_][a-z0-9_]*\Z")
def _load_runtime_tool() -> ModuleType:
spec = importlib.util.spec_from_file_location("leoclean_runtime_canary_contract", RUNTIME_TOOL)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_ci_runs_digest_pinned_permission_canary_and_always_cleans_up() -> None:
workflow_path = REPO_ROOT / ".github" / "workflows" / "ci.yml"
workflow = yaml.safe_load(workflow_path.read_text(encoding="utf-8"))
assert workflow["env"]["LEOCLEAN_RUNTIME_POSTGRES_IMAGE"] == POSTGRES_IMAGE
steps = workflow["jobs"]["test"]["steps"]
pull_step = next(
step for step in steps if step.get("name") == "Pull digest-pinned PostgreSQL permission canary image"
)
pytest_step = next(step for step in steps if step.get("name") == "Pytest")
cleanup_step = next(
step for step in steps if step.get("name") == "Remove interrupted PostgreSQL permission canaries"
)
assert pull_step["run"] == 'docker pull "${LEOCLEAN_RUNTIME_POSTGRES_IMAGE}"'
assert pytest_step["env"] == {RUN_ENV: "1"}
assert "python -m pytest" in pytest_step["run"]
assert cleanup_step["if"] == "always()"
assert "label=livingip.r2.permission-canary" in cleanup_step["run"]
assert "docker rm --force" in cleanup_step["run"]
def _quote_identifier(value: str) -> str:
assert IDENTIFIER_RE.fullmatch(value), value
return f'"{value}"'
def _run(
command: list[str],
*,
input_text: str | None = None,
timeout: int = 120,
) -> subprocess.CompletedProcess[str]:
return subprocess.run(
command,
input=input_text,
text=True,
capture_output=True,
check=False,
timeout=timeout,
)
def _require_success(completed: subprocess.CompletedProcess[str], action: str) -> str:
if completed.returncode != 0:
raise AssertionError(
f"{action} failed with exit {completed.returncode}\n"
f"stdout:\n{completed.stdout}\nstderr:\n{completed.stderr}"
)
return completed.stdout.strip()
def _psql(
container: str,
sql: str,
*,
role: str = "postgres",
database: str = DATABASE,
pgoptions: str | None = None,
) -> subprocess.CompletedProcess[str]:
command = ["docker", "exec", "-i"]
if pgoptions is not None:
command.extend(("--env", f"PGOPTIONS={pgoptions}"))
command.extend(
[
container,
"psql",
"--no-psqlrc",
"--no-password",
"--set=ON_ERROR_STOP=1",
"--set=VERBOSITY=verbose",
"--tuples-only",
"--no-align",
"--username",
role,
"--dbname",
database,
]
)
return _run(command, input_text=sql)
def _assert_denied(container: str, sql: str, sqlstate: str = "42501") -> None:
completed = _psql(container, sql, role=RUNTIME_ROLE)
assert completed.returncode != 0, f"unexpected success for: {sql}"
assert re.search(rf"(?:ERROR|FATAL):\s+{re.escape(sqlstate)}:", completed.stderr), (
f"expected SQLSTATE {sqlstate} for {sql!r}\nstderr:\n{completed.stderr}"
)
def _generated_read_tables_sql() -> str:
statements: list[str] = []
for schema, relation, columns in verifier.READ_COLUMN_ALLOWLIST:
if (schema, relation) in {("public", "agents"), ("kb_stage", "kb_proposals")}:
continue
definitions = [f"{_quote_identifier(column)} text" for column in columns]
definitions.append('"runtime_hidden_marker" text')
statements.append(
f"create table {_quote_identifier(schema)}.{_quote_identifier(relation)} ({', '.join(definitions)});"
)
return "\n".join(statements)
def _bootstrap_sql() -> str:
# This fixture models the reviewed privilege topology, not production-schema parity.
return f"""
create role kb_review nologin noinherit;
create role kb_apply nologin noinherit;
create schema kb_stage;
create table public.agents (
id uuid primary key,
handle text not null unique,
runtime_hidden_marker text
);
{_generated_read_tables_sql()}
create table kb_stage.kb_proposals (
id uuid primary key default gen_random_uuid(),
proposal_type text not null,
status text not null default 'pending_review',
proposed_by_handle text,
proposed_by_agent_id uuid,
channel text not null default 'cli',
source_ref text,
rationale text not null,
payload jsonb not null,
reviewed_by_handle text,
reviewed_by_agent_id uuid,
reviewed_at timestamptz,
review_note text,
applied_by_handle text,
applied_by_agent_id uuid,
applied_at timestamptz,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
constraint kb_proposals_proposal_type_check check (
proposal_type in ('revise_claim', 'revise_strategy', 'add_edge')
),
constraint kb_proposals_status_check check (
status in ('pending_review', 'approved', 'rejected', 'applied', 'canceled')
),
constraint kb_proposals_proposed_by_agent_id_fkey
foreign key (proposed_by_agent_id) references public.agents(id),
constraint kb_proposals_reviewed_by_agent_id_fkey
foreign key (reviewed_by_agent_id) references public.agents(id),
constraint kb_proposals_applied_by_agent_id_fkey
foreign key (applied_by_agent_id) references public.agents(id)
);
create table public.private_notes (id uuid primary key, body text not null);
insert into public.agents (id, handle, runtime_hidden_marker)
values ('11111111-1111-4111-8111-111111111111', 'leo', 'not-runtime-readable');
create function kb_stage.approve_strict_proposal(uuid, text, jsonb, text, text)
returns jsonb language sql security definer set search_path = pg_catalog, pg_temp
as $function$ select '{{}}'::jsonb $function$;
create function kb_stage.assert_approved_proposal(
uuid, text, jsonb, text, uuid, timestamptz, text
) returns void language plpgsql security definer set search_path = pg_catalog, pg_temp
as $function$ begin return; end; $function$;
create function kb_stage.finish_approved_proposal(
uuid, text, jsonb, text, uuid, timestamptz, text, text
) returns jsonb language sql security definer set search_path = pg_catalog, pg_temp
as $function$ select '{{}}'::jsonb $function$;
"""
def _read_fingerprint(container: str) -> dict[str, int]:
pairs: list[str] = []
for schema, relation, columns in verifier.READ_COLUMN_ALLOWLIST:
key = f"{schema}.{relation}"
first_column = _quote_identifier(columns[0])
qualified = f"{_quote_identifier(schema)}.{_quote_identifier(relation)}"
pairs.extend((f"'{key}'", f"(select count({first_column}) from {qualified})"))
sql = f"select jsonb_build_object({', '.join(pairs)})::text;"
output = _require_success(_psql(container, sql, role=RUNTIME_ROLE), "read fingerprint")
return json.loads(output)
def _provision(container: str, remote_sql_path: str) -> subprocess.CompletedProcess[str]:
return _run(
[
"docker",
"exec",
"--interactive",
container,
"psql",
"--no-psqlrc",
"--no-password",
"--set=ON_ERROR_STOP=1",
"--set=VERBOSITY=verbose",
"--username",
"postgres",
"--dbname",
DATABASE,
f"--file={remote_sql_path}",
],
input_text=f"{LOCAL_RUNTIME_PASSWORD}\n{LOCAL_RUNTIME_PASSWORD}\n",
)
def _assert_cloudsql_admin_attribute_compatibility(container: str) -> None:
probe_role = "cloudsql_admin_probe"
target_role = "cloudsql_attribute_target"
_require_success(
_psql(
container,
f"""
create role {probe_role}
login createrole createdb nosuperuser noreplication nobypassrls;
create role {target_role}
nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls;
grant {target_role} to {probe_role} with admin option;
""",
),
"Cloud SQL administrator attribute fixture",
)
_require_success(
_psql(
container,
f"""
alter role {target_role}
with nologin nocreatedb nocreaterole noinherit connection limit 8;
alter role {target_role}
with login nocreatedb nocreaterole noinherit connection limit 8;
""",
role=probe_role,
),
"Cloud SQL-compatible scoped attribute changes",
)
for forbidden_attribute in ("nosuperuser", "noreplication", "nobypassrls"):
denied = _psql(
container,
f"alter role {target_role} with {forbidden_attribute};",
role=probe_role,
)
assert denied.returncode != 0
assert re.search(r"(?:ERROR|FATAL):\s+42501:", denied.stderr), denied.stderr
def _read_table_rows_state(container: str) -> dict[str, dict[str, object]]:
table_output = _require_success(
_psql(
container,
"""
select namespace.nspname || '|' || relation.relname
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')
order by namespace.nspname, relation.relname;
""",
),
"provisioning table inventory",
)
state: dict[str, dict[str, object]] = {}
for line in table_output.splitlines():
schema, relation = line.strip().split("|", 1)
assert IDENTIFIER_RE.fullmatch(schema), schema
assert IDENTIFIER_RE.fullmatch(relation), relation
qualified = f"{_quote_identifier(schema)}.{_quote_identifier(relation)}"
output = _require_success(
_psql(
container,
f"""
select pg_catalog.jsonb_build_object(
'row_count', pg_catalog.count(*),
'rowset_md5', pg_catalog.md5(
coalesce(
pg_catalog.string_agg(
pg_catalog.to_jsonb(row_data)::text,
E'\\n'
order by pg_catalog.to_jsonb(row_data)::text
),
''
)
)
)::text
from {qualified} as row_data;
""",
),
f"row-state snapshot for {schema}.{relation}",
)
state[f"{schema}.{relation}"] = json.loads(output)
return state
def _read_catalog_state(container: str) -> dict[str, object]:
output = _require_success(
_psql(
container,
"""
with scoped_role_names(role_name) as (
values ('leoclean_kb_runtime'), ('leoclean_kb_stage_owner')
), acl_entries(kind, object_name, grantor_name, grantee_name, privilege_type, is_grantable) as (
select 'database',
database_row.datname,
grantor_role.rolname,
case when acl.grantee = 0 then 'PUBLIC' else grantee_role.rolname end,
acl.privilege_type,
acl.is_grantable
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
left join pg_catalog.pg_roles grantor_role on grantor_role.oid = acl.grantor
left join pg_catalog.pg_roles grantee_role on grantee_role.oid = acl.grantee
union all
select 'schema',
namespace.nspname,
grantor_role.rolname,
case when acl.grantee = 0 then 'PUBLIC' else grantee_role.rolname end,
acl.privilege_type,
acl.is_grantable
from pg_catalog.pg_namespace namespace
cross join lateral pg_catalog.aclexplode(
coalesce(namespace.nspacl, pg_catalog.acldefault('n', namespace.nspowner))
) acl
left join pg_catalog.pg_roles grantor_role on grantor_role.oid = acl.grantor
left join pg_catalog.pg_roles grantee_role on grantee_role.oid = acl.grantee
where namespace.nspname in ('public', 'kb_stage')
union all
select case when relation.relkind = 'S' then 'sequence' else 'relation' end,
pg_catalog.format('%I.%I', namespace.nspname, relation.relname),
grantor_role.rolname,
case when acl.grantee = 0 then 'PUBLIC' else grantee_role.rolname end,
acl.privilege_type,
acl.is_grantable
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
cross join lateral pg_catalog.aclexplode(
coalesce(
relation.relacl,
pg_catalog.acldefault(case when relation.relkind = 'S' then 'S'::"char" else 'r'::"char" end, relation.relowner)
)
) acl
left join pg_catalog.pg_roles grantor_role on grantor_role.oid = acl.grantor
left join pg_catalog.pg_roles grantee_role on grantee_role.oid = acl.grantee
where namespace.nspname in ('public', 'kb_stage')
and relation.relkind in ('r', 'p', 'v', 'm', 'f', 'S')
union all
select 'column',
pg_catalog.format('%I.%I.%I', namespace.nspname, relation.relname, attribute.attname),
grantor_role.rolname,
case when acl.grantee = 0 then 'PUBLIC' else grantee_role.rolname end,
acl.privilege_type,
acl.is_grantable
from pg_catalog.pg_attribute attribute
join pg_catalog.pg_class relation on relation.oid = attribute.attrelid
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
cross join lateral pg_catalog.aclexplode(attribute.attacl) acl
left join pg_catalog.pg_roles grantor_role on grantor_role.oid = acl.grantor
left join pg_catalog.pg_roles grantee_role on grantee_role.oid = acl.grantee
where namespace.nspname in ('public', 'kb_stage')
and attribute.attnum > 0
and not attribute.attisdropped
union all
select 'routine',
pg_catalog.format(
'%I.%I(%s)',
namespace.nspname,
function_row.proname,
pg_catalog.pg_get_function_identity_arguments(function_row.oid)
),
grantor_role.rolname,
case when acl.grantee = 0 then 'PUBLIC' else grantee_role.rolname end,
acl.privilege_type,
acl.is_grantable
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
left join pg_catalog.pg_roles grantor_role on grantor_role.oid = acl.grantor
left join pg_catalog.pg_roles grantee_role on grantee_role.oid = acl.grantee
where namespace.nspname in ('public', 'kb_stage')
union all
select 'large_object',
metadata.oid::text,
grantor_role.rolname,
case when acl.grantee = 0 then 'PUBLIC' else grantee_role.rolname end,
acl.privilege_type,
acl.is_grantable
from pg_catalog.pg_largeobject_metadata metadata
cross join lateral pg_catalog.aclexplode(
coalesce(metadata.lomacl, pg_catalog.acldefault('L', metadata.lomowner))
) acl
left join pg_catalog.pg_roles grantor_role on grantor_role.oid = acl.grantor
left join pg_catalog.pg_roles grantee_role on grantee_role.oid = acl.grantee
union all
select 'parameter',
parameter_acl.parname,
grantor_role.rolname,
case when acl.grantee = 0 then 'PUBLIC' else grantee_role.rolname end,
acl.privilege_type,
acl.is_grantable
from pg_catalog.pg_parameter_acl parameter_acl
cross join lateral pg_catalog.aclexplode(parameter_acl.paracl) acl
left join pg_catalog.pg_roles grantor_role on grantor_role.oid = acl.grantor
left join pg_catalog.pg_roles grantee_role on grantee_role.oid = acl.grantee
), role_state as (
select coalesce(
pg_catalog.jsonb_agg(
pg_catalog.jsonb_build_object(
'role', role_row.rolname,
'can_login', role_row.rolcanlogin,
'is_superuser', role_row.rolsuper,
'can_create_db', role_row.rolcreatedb,
'can_create_role', role_row.rolcreaterole,
'inherits_privileges', role_row.rolinherit,
'can_replicate', role_row.rolreplication,
'bypasses_rls', role_row.rolbypassrls,
'connection_limit', role_row.rolconnlimit,
'role_config', role_row.rolconfig,
'password_is_null', auth_row.rolpassword is null,
'password_is_scram', coalesce(auth_row.rolpassword like 'SCRAM-SHA-256$%', false)
)
order by role_row.rolname
),
'[]'::pg_catalog.jsonb
) as value
from pg_catalog.pg_roles role_row
join pg_catalog.pg_authid auth_row on auth_row.oid = role_row.oid
join scoped_role_names scoped on scoped.role_name = role_row.rolname
), database_role_settings as (
select coalesce(
pg_catalog.jsonb_agg(
pg_catalog.jsonb_build_object(
'role', role_row.rolname,
'database', database_row.datname,
'setting', setting_value.setting
)
order by role_row.rolname, database_row.datname, setting_value.setting
),
'[]'::pg_catalog.jsonb
) as value
from pg_catalog.pg_db_role_setting setting_row
join pg_catalog.pg_roles role_row on role_row.oid = setting_row.setrole
join scoped_role_names scoped on scoped.role_name = role_row.rolname
left join pg_catalog.pg_database database_row on database_row.oid = setting_row.setdatabase
cross join lateral pg_catalog.unnest(setting_row.setconfig) setting_value(setting)
), membership_state as (
select coalesce(
pg_catalog.jsonb_agg(
pg_catalog.jsonb_build_object(
'granted_role', granted_role.rolname,
'member_role', member_role.rolname,
'grantor_role', grantor_role.rolname,
'admin_option', membership.admin_option,
'inherit_option', membership.inherit_option,
'set_option', membership.set_option
)
order by granted_role.rolname, member_role.rolname, grantor_role.rolname
),
'[]'::pg_catalog.jsonb
) as value
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
join pg_catalog.pg_roles grantor_role on grantor_role.oid = membership.grantor
where granted_role.rolname in (select role_name from scoped_role_names)
or member_role.rolname in (select role_name from scoped_role_names)
), ownership_state as (
select coalesce(
pg_catalog.jsonb_agg(
pg_catalog.jsonb_build_object(
'kind', owned.kind,
'object', owned.object_name,
'owner', owned.owner_name
)
order by owned.kind, owned.object_name, owned.owner_name
),
'[]'::pg_catalog.jsonb
) as value
from (
select 'relation' as kind,
pg_catalog.format('%I.%I', namespace.nspname, relation.relname) as object_name,
owner_role.rolname as owner_name
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
join pg_catalog.pg_roles owner_role on owner_role.oid = relation.relowner
where namespace.nspname in ('public', 'kb_stage')
union all
select 'routine',
pg_catalog.format(
'%I.%I(%s)',
namespace.nspname,
function_row.proname,
pg_catalog.pg_get_function_identity_arguments(function_row.oid)
),
owner_role.rolname
from pg_catalog.pg_proc function_row
join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
join pg_catalog.pg_roles owner_role on owner_role.oid = function_row.proowner
where namespace.nspname in ('public', 'kb_stage')
) owned
), normalized_acls as (
select coalesce(
pg_catalog.jsonb_agg(
pg_catalog.jsonb_build_object(
'kind', entries.kind,
'object', entries.object_name,
'grantor', entries.grantor_name,
'grantee', entries.grantee_name,
'privilege', entries.privilege_type,
'grantable', entries.is_grantable
)
order by entries.kind,
entries.object_name,
entries.grantor_name,
entries.grantee_name,
entries.privilege_type,
entries.is_grantable
),
'[]'::pg_catalog.jsonb
) as value
from acl_entries entries
)
select pg_catalog.jsonb_build_object(
'roles', (select value from role_state),
'database_role_settings', (select value from database_role_settings),
'memberships', (select value from membership_state),
'ownership', (select value from ownership_state),
'acls', (select value from normalized_acls),
'stage_function', pg_catalog.jsonb_build_object(
'owner', (
select owner_role.rolname
from pg_catalog.pg_proc function_row
join pg_catalog.pg_roles owner_role on owner_role.oid = function_row.proowner
where function_row.oid = 'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)'::pg_catalog.regprocedure
),
'runtime_execute', pg_catalog.has_function_privilege(
'leoclean_kb_runtime',
'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)',
'EXECUTE'
),
'owner_execute', pg_catalog.has_function_privilege(
'leoclean_kb_stage_owner',
'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)',
'EXECUTE'
)
)
)::text;
""",
),
"provisioning catalog-state snapshot",
)
return json.loads(output)
def _read_provisioning_state(container: str) -> dict[str, object]:
return {
"rows": _read_table_rows_state(container),
"catalog": _read_catalog_state(container),
}
def _state_with_runtime_login(state: dict[str, object], *, can_login: bool) -> dict[str, object]:
adjusted = json.loads(json.dumps(state))
roles = adjusted["catalog"]["roles"]
runtime = next(role for role in roles if role["role"] == RUNTIME_ROLE)
runtime["can_login"] = can_login
return adjusted
def _assert_exact_read_contract(container: str) -> None:
for schema, relation, columns in verifier.READ_COLUMN_ALLOWLIST:
qualified = f"{_quote_identifier(schema)}.{_quote_identifier(relation)}"
projection = ", ".join(_quote_identifier(column) for column in columns)
_require_success(
_psql(container, f"select {projection} from {qualified} limit 0;", role=RUNTIME_ROLE),
f"allowed projection {schema}.{relation}",
)
_assert_denied(container, f"select * from {qualified} limit 0;")
denied_column = (
"reviewed_by_agent_id" if (schema, relation) == ("kb_stage", "kb_proposals") else "runtime_hidden_marker"
)
_assert_denied(container, f"select {_quote_identifier(denied_column)} from {qualified} limit 0;")
def _assert_proposal_function_and_rollback(container: str, runtime_tool: ModuleType) -> None:
crafted_rationale = "permission canary\\'; select pg_catalog.lo_from_bytea(0, ''::bytea); --"
crafted_rationale_sql = runtime_tool.sql_literal(crafted_rationale)
calls = []
for proposal_type in ("revise_claim", "revise_strategy", "add_edge"):
rationale = crafted_rationale_sql if proposal_type == "revise_claim" else "'permission canary'"
calls.append(
"select concat_ws('|', staged->>'proposal_type', staged->>'status', "
"staged->>'proposed_by_handle', staged->>'channel') "
"from (select kb_stage.stage_leoclean_proposal("
f"'{proposal_type}', '', 'r2:{proposal_type}', {rationale}, "
"'{\"canary\": true}'::jsonb) as staged) probe;"
)
sql = "\n".join(
[
"begin;",
"select 'strings=' || current_setting('standard_conforming_strings');",
"select 'readonly=' || current_setting('transaction_read_only');",
*calls,
f"select 'crafted=' || count(*) from kb_stage.kb_proposals where rationale = {crafted_rationale_sql};",
"select 'inside=' || count(id) from kb_stage.kb_proposals;",
"rollback;",
"select 'after=' || count(id) from kb_stage.kb_proposals;",
]
)
output = _require_success(
_psql(
container,
sql,
role=RUNTIME_ROLE,
pgoptions=runtime_tool.RUNTIME_STANDARD_STRINGS_PGOPTIONS,
),
"proposal function rollback",
)
lines = [line.strip() for line in output.splitlines() if line.strip()]
assert "strings=on" in lines
assert "readonly=off" in lines
assert "revise_claim|pending_review|leo|telegram" in lines
assert "revise_strategy|pending_review|leo|telegram" in lines
assert "add_edge|pending_review|leo|telegram" in lines
assert "crafted=1" in lines
assert "inside=3" in lines
assert "after=0" in lines
def _assert_write_and_escalation_denials(container: str) -> None:
direct_writes = (
"insert into public.claims (id) values ('direct-canonical-write');",
"update public.claims set runtime_hidden_marker = 'x' where false;",
"delete from public.claims where false;",
"truncate public.claims;",
"insert into kb_stage.kb_proposals (proposal_type, rationale, payload) values ('revise_claim', 'direct-stage-write', '{}'::jsonb);",
"update kb_stage.kb_proposals set rationale = 'x' where false;",
"delete from kb_stage.kb_proposals where false;",
"truncate kb_stage.kb_proposals;",
)
for sql in direct_writes:
_assert_denied(container, sql)
escalation_attempts = (
"set lo_compat_privileges = on;",
"set role leoclean_kb_stage_owner;",
"set role kb_review;",
"set role kb_apply;",
"set role postgres;",
"create role r2_escape;",
"set session authorization postgres;",
"select kb_stage.approve_strict_proposal('00000000-0000-0000-0000-000000000000', 'leo', '{}'::jsonb, 'approve', 'note');",
"select kb_stage.assert_approved_proposal('00000000-0000-0000-0000-000000000000', 'revise_claim', '{}'::jsonb, 'leo', '11111111-1111-4111-8111-111111111111', now(), 'note');",
"select kb_stage.finish_approved_proposal('00000000-0000-0000-0000-000000000000', 'revise_claim', '{}'::jsonb, 'leo', '11111111-1111-4111-8111-111111111111', now(), 'note', 'applied');",
)
for sql in escalation_attempts:
_assert_denied(container, sql)
_assert_denied(
container,
"select kb_stage.stage_leoclean_proposal('leo', 'revise_claim', 'telegram', 'r2', 'forged', '{}'::jsonb);",
"42883",
)
_assert_denied(container, "select body from public.private_notes;", "42501")
def _assert_large_object_provider_residual_rolls_back(container: str) -> None:
inventory_sql = """
select pg_catalog.format(
'%I(%s)|%s|%s',
function_row.proname,
pg_catalog.pg_get_function_identity_arguments(function_row.oid),
exists (
select 1
from pg_catalog.aclexplode(
coalesce(function_row.proacl, pg_catalog.acldefault('f', function_row.proowner))
) acl
where acl.grantee = 0
and acl.privilege_type = 'EXECUTE'
),
pg_catalog.has_function_privilege(current_user, function_row.oid, 'EXECUTE')
)
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'
)
order by function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid);
"""
observed = _require_success(_psql(container, inventory_sql, role=RUNTIME_ROLE), "large-object residual inventory")
expected = "\n".join(
f"{signature}|{'t' if public_execute else 'f'}|{'t' if public_execute else 'f'}"
for signature, public_execute in verifier.LARGE_OBJECT_MUTATION_ROUTINE_RESIDUAL
)
assert observed == expected
probe_sql = """
select 'before=' || count(*)
from pg_catalog.pg_largeobject_metadata metadata
where metadata.lomowner = (select oid from pg_catalog.pg_roles where rolname = current_user);
begin;
select 'created=' || (pg_catalog.lo_from_bytea(0, ''::bytea) <> 0);
select 'inside=' || count(*)
from pg_catalog.pg_largeobject_metadata metadata
where metadata.lomowner = (select oid from pg_catalog.pg_roles where rolname = current_user);
rollback;
select 'after=' || count(*)
from pg_catalog.pg_largeobject_metadata metadata
where metadata.lomowner = (select oid from pg_catalog.pg_roles where rolname = current_user);
"""
output = _require_success(_psql(container, probe_sql, role=RUNTIME_ROLE), "rolled-back large-object residual")
lines = [
line.strip()
for line in output.splitlines()
if line.strip() and line.strip() not in {"BEGIN", "ROLLBACK"}
]
assert lines == ["before=0", "created=true", "inside=1", "after=0"]
def _assert_runtime_read_only_guard(container: str, runtime_tool: ModuleType) -> None:
read_only_options = runtime_tool.RUNTIME_READ_ONLY_PGOPTIONS
owner_count_sql = f"""
select count(*)
from pg_catalog.pg_largeobject_metadata metadata
where metadata.lomowner = (select oid from pg_catalog.pg_roles where rolname = '{RUNTIME_ROLE}');
"""
assert _require_success(_psql(container, owner_count_sql), "large-object count before read guard") == "0"
assert (
_require_success(
_psql(
container,
"show standard_conforming_strings;",
role=RUNTIME_ROLE,
pgoptions=read_only_options,
),
"runtime string parsing guard",
)
== "on"
)
assert (
_require_success(
_psql(
container,
"show transaction_read_only;",
role=RUNTIME_ROLE,
pgoptions=read_only_options,
),
"runtime read-only transaction guard",
)
== "on"
)
_require_success(
_psql(
container,
"select id, text from public.claims limit 0;",
role=RUNTIME_ROLE,
pgoptions=read_only_options,
),
"allowed read through runtime read-only guard",
)
denied = _psql(
container,
"select pg_catalog.lo_from_bytea(0, ''::bytea);",
role=RUNTIME_ROLE,
pgoptions=read_only_options,
)
assert denied.returncode != 0
assert re.search(r"ERROR:\s+25006:", denied.stderr), denied.stderr
assert _require_success(_psql(container, owner_count_sql), "large-object count after read guard") == "0"
@pytest.mark.skipif(os.environ.get(RUN_ENV) != "1", reason=f"set {RUN_ENV}=1 for the local PG16 canary")
def test_digest_pinned_network_disabled_postgres_16_14_permission_contract(tmp_path: Path) -> None:
assert shutil.which("docker") is not None, "Docker is required for the disposable permission canary"
runtime_tool = _load_runtime_tool()
image_output = _require_success(
_run(["docker", "image", "inspect", "--format", "{{.Id}}|{{json .RepoDigests}}", POSTGRES_IMAGE]),
"pinned image inspection",
)
image_id, repo_digests_json = image_output.split("|", 1)
assert re.fullmatch(r"sha256:[0-9a-f]{64}", image_id)
assert POSTGRES_IMAGE in json.loads(repo_digests_json)
run_token = uuid.uuid4().hex[:12]
container = f"leoclean-r2-{run_token}"
label = f"livingip.r2.permission-canary={run_token}"
cleanup_error: str | None = None
try:
started = _run(
[
"docker",
"run",
"--detach",
"--name",
container,
"--label",
label,
"--pull=never",
"--network=none",
"--tmpfs",
"/var/lib/postgresql/data:rw,noexec,nosuid,nodev,size=256m",
"--env",
"POSTGRES_DB=teleo_canonical",
"--env",
"POSTGRES_HOST_AUTH_METHOD=trust",
POSTGRES_IMAGE,
]
)
_require_success(started, "disposable PostgreSQL start")
inspect = json.loads(_require_success(_run(["docker", "inspect", container]), "container inspection"))[0]
assert inspect["Image"] == image_id
assert inspect["HostConfig"]["NetworkMode"] == "none"
assert not inspect["HostConfig"].get("PortBindings")
assert not inspect["HostConfig"].get("Binds")
tmpfs_spec = inspect["HostConfig"]["Tmpfs"]["/var/lib/postgresql/data"]
assert {"rw", "noexec", "nosuid", "nodev"}.issubset(tmpfs_spec.split(","))
assert any(size in tmpfs_spec.split(",") for size in ("size=256m", "size=268435456"))
assert all(mount["Type"] not in {"bind", "volume"} for mount in inspect["Mounts"])
last_startup_probe: subprocess.CompletedProcess[str] | None = None
for _ in range(120):
final_server = _run(
[
"docker",
"exec",
container,
"sh",
"-c",
'test "$(cat /proc/1/comm)" = postgres',
],
timeout=5,
)
if final_server.returncode == 0:
version = _psql(container, "show server_version_num;")
last_startup_probe = version
if version.returncode == 0 and version.stdout.strip() == POSTGRES_VERSION_NUM:
break
else:
last_startup_probe = final_server
time.sleep(0.25)
else:
logs = _run(["docker", "logs", container], timeout=10)
probe_output = ""
if last_startup_probe is not None:
probe_output = f"\nlast probe stdout:\n{last_startup_probe.stdout}\nlast probe stderr:\n{last_startup_probe.stderr}"
raise AssertionError(
f"final PostgreSQL server did not report version {POSTGRES_VERSION_NUM}"
f"{probe_output}\ncontainer logs:\n{logs.stdout}\n{logs.stderr}"
)
_require_success(_psql(container, "create database teleo_kb;"), "legacy database fixture")
_require_success(_psql(container, _bootstrap_sql()), "permission fixture bootstrap")
_assert_cloudsql_admin_attribute_compatibility(container)
_require_success(
_run(["docker", "cp", str(ROLE_SQL), f"{container}:/tmp/gcp_leoclean_runtime_role.sql"]),
"role SQL copy",
)
failure_anchor = "\n-- LOGIN is the last state change"
role_sql = ROLE_SQL.read_text(encoding="utf-8")
assert role_sql.count(failure_anchor) == 1
failure_sql = role_sql.replace(
failure_anchor,
"\ngrant update on public.private_notes to leoclean_kb_runtime;"
"\nselect 1 / 0 as injected_mid_provision_failure;"
f"{failure_anchor}",
)
failure_path = tmp_path / "gcp_leoclean_runtime_role_injected_failure.sql"
failure_path.write_text(failure_sql, encoding="utf-8")
_require_success(
_run(["docker", "cp", str(failure_path), f"{container}:/tmp/gcp_leoclean_runtime_role_failure.sql"]),
"failure-injected role SQL copy",
)
_require_success(
_psql(container, "alter database teleo_canonical set lo_compat_privileges = on;"),
"unsafe large-object compatibility fixture",
)
unsafe_compat = _provision(container, "/tmp/gcp_leoclean_runtime_role.sql")
assert unsafe_compat.returncode != 0
assert "lo_compat_privileges must remain off" in unsafe_compat.stderr
assert _require_success(
_psql(container, f"select rolcanlogin from pg_catalog.pg_roles where rolname = '{RUNTIME_ROLE}';"),
"unsafe compatibility refence",
) == "f"
_require_success(
_psql(container, "alter database teleo_canonical reset lo_compat_privileges;"),
"large-object compatibility recovery",
)
_require_success(
_psql(container, f"grant execute on function pg_catalog.lo_create(oid) to {RUNTIME_ROLE};"),
"direct large-object ACL fixture",
)
direct_acl = _provision(container, "/tmp/gcp_leoclean_runtime_role.sql")
assert direct_acl.returncode != 0
assert "scoped Leo roles have direct large-object routine ACLs" in direct_acl.stderr
assert _require_success(
_psql(container, f"select rolcanlogin from pg_catalog.pg_roles where rolname = '{RUNTIME_ROLE}';"),
"direct ACL refence",
) == "f"
_require_success(
_psql(container, f"revoke execute on function pg_catalog.lo_create(oid) from {RUNTIME_ROLE};"),
"direct large-object ACL recovery",
)
successful_states: list[dict[str, object]] = []
for provision_number in range(1, 4):
_require_success(
_provision(container, "/tmp/gcp_leoclean_runtime_role.sql"),
f"runtime role provisioning run {provision_number}",
)
state = _read_provisioning_state(container)
assert state["catalog"]["stage_function"] == {
"owner": "leoclean_kb_stage_owner",
"owner_execute": False,
"runtime_execute": True,
}
assert state["catalog"]["memberships"] == []
role_state = {role["role"]: role for role in state["catalog"]["roles"]}
assert role_state[RUNTIME_ROLE]["password_is_scram"] is True
assert role_state[RUNTIME_ROLE]["password_is_null"] is False
assert role_state["leoclean_kb_stage_owner"]["password_is_scram"] is False
assert role_state["leoclean_kb_stage_owner"]["password_is_null"] is True
_assert_large_object_provider_residual_rolls_back(container)
successful_states.append(state)
assert successful_states[1:] == successful_states[:1] * 2
stable_state = successful_states[0]
failed = _provision(container, "/tmp/gcp_leoclean_runtime_role_failure.sql")
assert failed.returncode != 0
assert re.search(r"ERROR:\s+22012:", failed.stderr)
assert _read_provisioning_state(container) == _state_with_runtime_login(stable_state, can_login=False)
disabled_login = _psql(container, "select 1;", role=RUNTIME_ROLE)
assert disabled_login.returncode != 0
assert f'role "{RUNTIME_ROLE}" is not permitted to log in' in disabled_login.stderr
_require_success(
_provision(container, "/tmp/gcp_leoclean_runtime_role.sql"),
"runtime role recovery provisioning",
)
assert _read_provisioning_state(container) == stable_state
identity = _require_success(
_psql(container, "select current_user || '|' || current_database();", role=RUNTIME_ROLE),
"runtime identity",
)
assert identity == f"{RUNTIME_ROLE}|{DATABASE}"
stage_definition = json.loads(
_require_success(
_psql(container, verifier._stage_function_definition_sql(), role=RUNTIME_ROLE),
"external verifier stage-function query",
)
)
assert verifier._assert_stage_function_definition(stage_definition)["owner_execute"] is False
before = _read_fingerprint(container)
assert before == {
f"{schema}.{relation}": (1 if (schema, relation) == ("public", "agents") else 0)
for schema, relation, _columns in verifier.READ_COLUMN_ALLOWLIST
}
_assert_exact_read_contract(container)
_assert_runtime_read_only_guard(container, runtime_tool)
_assert_proposal_function_and_rollback(container, runtime_tool)
_assert_write_and_escalation_denials(container)
_require_success(
_psql(container, "alter table public.claims add column future_private_text text;"),
"future-column sentinel",
)
_assert_denied(container, "select future_private_text from public.claims limit 0;")
assert _read_fingerprint(container) == before
finally:
removed = _run(["docker", "rm", "--force", container], timeout=30)
if removed.returncode != 0 and "No such container" not in f"{removed.stdout}\n{removed.stderr}":
cleanup_error = removed.stderr or removed.stdout
leftovers = _run(
["docker", "ps", "--all", "--filter", f"label={label}", "--format", "{{.Names}}"],
timeout=10,
)
if leftovers.returncode != 0 or leftovers.stdout.strip():
cleanup_error = cleanup_error or leftovers.stderr or leftovers.stdout
if cleanup_error is not None:
raise AssertionError(f"disposable PostgreSQL cleanup failed: {cleanup_error}")