1838 lines
65 KiB
Python
1838 lines
65 KiB
Python
#!/usr/bin/env python3
|
|
"""Retain a sanitized least-privilege receipt for the GCP Leo runtime role.
|
|
|
|
This verifier is intentionally service-independent. It must run on the private
|
|
GCP VM as ``teleo`` and connects directly to the private Cloud SQL address. The
|
|
only database credential is read from the scoped Secret Manager secret into
|
|
process memory, passed to each ``psql`` child through ``PGPASSWORD``, and never
|
|
included in an argument, message, receipt, or output file.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import pwd
|
|
import re
|
|
import stat
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from collections.abc import Callable, Mapping
|
|
from dataclasses import dataclass
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
PROJECT_ID = "teleo-501523"
|
|
PRIVATE_CLOUDSQL_HOST = "10.61.0.3"
|
|
PRIVATE_CLOUDSQL_PORT = 5432
|
|
CANONICAL_DATABASE = "teleo_canonical"
|
|
LEGACY_DATABASE = "teleo_kb"
|
|
RUNTIME_DATABASE_ROLE = "leoclean_kb_runtime"
|
|
STAGE_OWNER_DATABASE_ROLE = "leoclean_kb_stage_owner"
|
|
RUNTIME_UNIX_USER = "teleo"
|
|
SCOPED_PASSWORD_SECRET = "gcp-teleo-pgvector-standby-leoclean-kb-runtime-password"
|
|
ADMINISTRATOR_PASSWORD_SECRET = "gcp-teleo-pgvector-standby-postgres-password"
|
|
GCLOUD_BIN = "/usr/bin/gcloud"
|
|
PSQL_BIN = "/usr/bin/psql"
|
|
COMMAND_TIMEOUT_SECONDS = 30
|
|
CHILD_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
RUN_ID_RE = re.compile(r"[a-z0-9][a-z0-9-]{6,62}[a-z0-9]\Z")
|
|
SQLSTATE_RE = re.compile(r"(?m)^(?:ERROR|FATAL):\s+([0-9A-Z]{5}):")
|
|
IAM_PERMISSION = "secretmanager.versions.access"
|
|
REQUIRED_TIER = "T3_live_readonly"
|
|
LOWER_TIER = "T2_runtime"
|
|
ARTIFACT_NAME = "gcp_leoclean_runtime_permissions"
|
|
STAGE_FUNCTION_SIGNATURE = "kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)"
|
|
|
|
# PostgreSQL stores the exact dollar-quoted function body in pg_proc.prosrc.
|
|
# A source-parity regression below binds this constant to the provisioning SQL,
|
|
# while the live verifier compares the catalog body without retaining it.
|
|
EXPECTED_STAGE_FUNCTION_SOURCE = """
|
|
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
|
|
"""
|
|
EXPECTED_STAGE_FUNCTION_SOURCE_SHA256 = "89f78d5ad7bc7227a4588baf42f7f77e2162b3c4647a250f7c88716b0debfdea"
|
|
MAX_STAGE_FUNCTION_SOURCE_BYTES = 65_536
|
|
|
|
STAGE_FUNCTION_METADATA_EXPECTATIONS: tuple[tuple[str, Any], ...] = (
|
|
("argument_defaults", 0),
|
|
(
|
|
"argument_names",
|
|
["p_proposal_type", "p_channel", "p_source_ref", "p_rationale", "p_payload"],
|
|
),
|
|
("argument_types", ["text", "text", "text", "text", "jsonb"]),
|
|
("kind", "f"),
|
|
("language", "plpgsql"),
|
|
("leakproof", False),
|
|
("owner", STAGE_OWNER_DATABASE_ROLE),
|
|
("parallel", "u"),
|
|
("returns_set", False),
|
|
("result", "jsonb"),
|
|
("security_definer", True),
|
|
("strict", False),
|
|
("volatility", "v"),
|
|
)
|
|
|
|
STAGE_OWNER_INSERT_COLUMNS: tuple[str, ...] = (
|
|
"proposal_type",
|
|
"status",
|
|
"proposed_by_handle",
|
|
"proposed_by_agent_id",
|
|
"channel",
|
|
"source_ref",
|
|
"rationale",
|
|
"payload",
|
|
)
|
|
|
|
FUNCTION_PRIVILEGE_EXPECTATIONS: tuple[tuple[str, str, bool, bool], ...] = (
|
|
(
|
|
"stage_leoclean_proposal_5_arg",
|
|
"kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)",
|
|
True,
|
|
True,
|
|
),
|
|
(
|
|
"stage_leoclean_proposal_6_arg",
|
|
"kb_stage.stage_leoclean_proposal(text,text,text,text,text,jsonb)",
|
|
False,
|
|
False,
|
|
),
|
|
(
|
|
"approve_strict_proposal",
|
|
"kb_stage.approve_strict_proposal(uuid,text,jsonb,text,text)",
|
|
True,
|
|
False,
|
|
),
|
|
(
|
|
"assert_approved_proposal",
|
|
"kb_stage.assert_approved_proposal(uuid,text,jsonb,text,uuid,timestamptz,text)",
|
|
True,
|
|
False,
|
|
),
|
|
(
|
|
"finish_approved_proposal",
|
|
"kb_stage.finish_approved_proposal(uuid,text,jsonb,text,uuid,timestamptz,text,text)",
|
|
True,
|
|
False,
|
|
),
|
|
)
|
|
|
|
READ_TABLE_ALLOWLIST: tuple[tuple[str, str], ...] = (
|
|
("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"),
|
|
)
|
|
|
|
CATALOG_ZERO_COUNT_FIELDS: tuple[str, ...] = (
|
|
"column_dml_grants",
|
|
"database_create_grants",
|
|
"missing_allowed_table_selects",
|
|
"owned_database_objects",
|
|
"owner_database_create_grants",
|
|
"owner_direct_database_acl_entries",
|
|
"owner_missing_agent_select_columns",
|
|
"owner_missing_proposal_insert_columns",
|
|
"owner_schema_create_grants",
|
|
"owner_sequence_grants",
|
|
"owner_table_dml_grants",
|
|
"owner_unexpected_column_dml_grants",
|
|
"owner_unexpected_column_selects",
|
|
"owner_unexpected_owned_database_objects",
|
|
"owner_unexpected_routine_execute",
|
|
"owner_unexpected_schema_usage",
|
|
"owner_unexpected_table_selects",
|
|
"schema_create_grants",
|
|
"sequence_grants",
|
|
"table_dml_grants",
|
|
"unexpected_column_selects",
|
|
"unexpected_direct_database_acl_entries",
|
|
"unexpected_routine_execute",
|
|
"unexpected_schema_usage",
|
|
"unexpected_security_definer_execute",
|
|
"unexpected_table_selects",
|
|
)
|
|
|
|
CATALOG_TRUE_FIELDS: tuple[str, ...] = (
|
|
"canonical_connect",
|
|
"owner_grant_contract",
|
|
"owner_schema_usage",
|
|
"runtime_connect_acl_exact",
|
|
"runtime_schema_usage",
|
|
)
|
|
|
|
LEGACY_CATALOG_ZERO_COUNT_FIELDS: tuple[str, ...] = (
|
|
"owner_column_privileges",
|
|
"owner_routine_privileges",
|
|
"owner_schema_privileges",
|
|
"owner_sequence_privileges",
|
|
"owner_table_privileges",
|
|
"runtime_column_privileges",
|
|
"runtime_routine_privileges",
|
|
"runtime_schema_privileges",
|
|
"runtime_sequence_privileges",
|
|
"runtime_table_privileges",
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class CommandResult:
|
|
returncode: int
|
|
stdout: str
|
|
stderr: str
|
|
|
|
|
|
Runner = Callable[[list[str], Mapping[str, str], int, bool], CommandResult]
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class NegativeCheck:
|
|
name: str
|
|
database: str
|
|
sql: str
|
|
expected_sqlstate: str
|
|
|
|
|
|
class VerificationError(RuntimeError):
|
|
"""A deliberately sanitized, machine-readable verification failure."""
|
|
|
|
def __init__(
|
|
self,
|
|
code: str,
|
|
check: str,
|
|
*,
|
|
expected_sqlstate: str | None = None,
|
|
observed_sqlstate: str | None = None,
|
|
) -> None:
|
|
self.code = code
|
|
self.check = check
|
|
self.expected_sqlstate = expected_sqlstate
|
|
self.observed_sqlstate = observed_sqlstate
|
|
super().__init__(f"{code}:{check}")
|
|
|
|
def as_dict(self) -> dict[str, str]:
|
|
payload = {"check": self.check, "code": self.code}
|
|
if self.expected_sqlstate is not None:
|
|
payload["expected_sqlstate"] = self.expected_sqlstate
|
|
if self.observed_sqlstate is not None:
|
|
payload["observed_sqlstate"] = self.observed_sqlstate
|
|
return payload
|
|
|
|
|
|
def subprocess_runner(
|
|
command: list[str],
|
|
env: Mapping[str, str],
|
|
timeout: int,
|
|
discard_stdout: bool,
|
|
) -> CommandResult:
|
|
completed = subprocess.run(
|
|
command,
|
|
check=False,
|
|
text=True,
|
|
env=dict(env),
|
|
stdin=subprocess.DEVNULL,
|
|
stdout=subprocess.DEVNULL if discard_stdout else subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
timeout=timeout,
|
|
)
|
|
return CommandResult(
|
|
returncode=completed.returncode,
|
|
stdout="" if discard_stdout else (completed.stdout or ""),
|
|
stderr=completed.stderr or "",
|
|
)
|
|
|
|
|
|
def validate_run_id(value: str) -> str:
|
|
if not RUN_ID_RE.fullmatch(value):
|
|
raise ValueError("run id must be 8-64 lowercase letters, digits, or hyphens")
|
|
return value
|
|
|
|
|
|
def _run_id_arg(value: str) -> str:
|
|
try:
|
|
return validate_run_id(value)
|
|
except ValueError as exc:
|
|
raise argparse.ArgumentTypeError(str(exc)) from None
|
|
|
|
|
|
def _base_child_environment(_source: Mapping[str, str]) -> dict[str, str]:
|
|
return {
|
|
"HOME": "/home/teleo",
|
|
"LANG": "C",
|
|
"LC_ALL": "C",
|
|
"PATH": CHILD_PATH,
|
|
}
|
|
|
|
|
|
def _gcloud_environment(source: Mapping[str, str], config_dir: Path) -> dict[str, str]:
|
|
env = _base_child_environment(source)
|
|
env.update(
|
|
{
|
|
"CLOUDSDK_CONFIG": str(config_dir),
|
|
"CLOUDSDK_CORE_DISABLE_PROMPTS": "1",
|
|
"CLOUDSDK_CORE_PROJECT": PROJECT_ID,
|
|
}
|
|
)
|
|
return env
|
|
|
|
|
|
def _psql_environment(source: Mapping[str, str], password: str) -> dict[str, str]:
|
|
env = _base_child_environment(source)
|
|
env["PGPASSWORD"] = password
|
|
return env
|
|
|
|
|
|
def _call(
|
|
command: list[str],
|
|
*,
|
|
env: Mapping[str, str],
|
|
runner: Runner,
|
|
check: str,
|
|
discard_stdout: bool = False,
|
|
) -> CommandResult:
|
|
try:
|
|
return runner(command, env, COMMAND_TIMEOUT_SECONDS, discard_stdout)
|
|
except FileNotFoundError:
|
|
raise VerificationError("command_not_found", check) from None
|
|
except subprocess.TimeoutExpired:
|
|
raise VerificationError("command_timed_out", check) from None
|
|
except OSError:
|
|
raise VerificationError("command_os_error", check) from None
|
|
|
|
|
|
def _secret_command(secret_name: str) -> list[str]:
|
|
return [
|
|
GCLOUD_BIN,
|
|
"secrets",
|
|
"versions",
|
|
"access",
|
|
"latest",
|
|
f"--secret={secret_name}",
|
|
f"--project={PROJECT_ID}",
|
|
"--quiet",
|
|
]
|
|
|
|
|
|
def _read_scoped_password(*, env: Mapping[str, str], runner: Runner) -> str:
|
|
result = _call(
|
|
_secret_command(SCOPED_PASSWORD_SECRET),
|
|
env=env,
|
|
runner=runner,
|
|
check="scoped_secret_access",
|
|
)
|
|
if result.returncode != 0:
|
|
raise VerificationError("scoped_secret_access_failed", "scoped_secret_access")
|
|
|
|
password = result.stdout[:-1] if result.stdout.endswith("\n") else result.stdout
|
|
if password.endswith("\r"):
|
|
password = password[:-1]
|
|
if not password or len(password) > 4096 or any(character in password for character in ("\x00", "\r", "\n")):
|
|
raise VerificationError("scoped_secret_value_invalid", "scoped_secret_access")
|
|
return password
|
|
|
|
|
|
def _assert_administrator_secret_denied(*, env: Mapping[str, str], runner: Runner) -> dict[str, Any]:
|
|
result = _call(
|
|
_secret_command(ADMINISTRATOR_PASSWORD_SECRET),
|
|
env=env,
|
|
runner=runner,
|
|
check="administrator_secret_access",
|
|
discard_stdout=True,
|
|
)
|
|
if result.returncode == 0:
|
|
raise VerificationError("administrator_secret_unexpectedly_readable", "administrator_secret_access")
|
|
|
|
permission_denied = re.search(r"\bPERMISSION_DENIED\b", result.stderr, re.IGNORECASE) is not None
|
|
exact_permission = IAM_PERMISSION in result.stderr.lower()
|
|
if not permission_denied or not exact_permission:
|
|
raise VerificationError("administrator_secret_denial_not_iam_permission", "administrator_secret_access")
|
|
return {
|
|
"classification": "iam_permission_denied",
|
|
"permission": IAM_PERMISSION,
|
|
"result": "denied",
|
|
"stdout_discarded": True,
|
|
}
|
|
|
|
|
|
def _conninfo(database: str) -> str:
|
|
return (
|
|
f"host={PRIVATE_CLOUDSQL_HOST} port={PRIVATE_CLOUDSQL_PORT} dbname={database} "
|
|
f"user={RUNTIME_DATABASE_ROLE} sslmode=require connect_timeout=10 "
|
|
"application_name=leoclean_runtime_permission_verifier"
|
|
)
|
|
|
|
|
|
def _psql_command(database: str, sql: str) -> list[str]:
|
|
return [
|
|
PSQL_BIN,
|
|
"--no-psqlrc",
|
|
"--no-password",
|
|
"--quiet",
|
|
"--tuples-only",
|
|
"--no-align",
|
|
"--set=ON_ERROR_STOP=1",
|
|
"--set=VERBOSITY=verbose",
|
|
f"--dbname={_conninfo(database)}",
|
|
f"--command={sql}",
|
|
]
|
|
|
|
|
|
def _single_output_line(result: CommandResult, check: str) -> str:
|
|
lines = [line.strip() for line in result.stdout.splitlines() if line.strip()]
|
|
if len(lines) != 1:
|
|
raise VerificationError("unexpected_psql_output_shape", check)
|
|
return lines[0]
|
|
|
|
|
|
def _run_psql_success(
|
|
*,
|
|
check: str,
|
|
database: str,
|
|
sql: str,
|
|
env: Mapping[str, str],
|
|
runner: Runner,
|
|
) -> str:
|
|
result = _call(
|
|
_psql_command(database, sql),
|
|
env=env,
|
|
runner=runner,
|
|
check=check,
|
|
)
|
|
if result.returncode != 0:
|
|
observed = sorted(set(SQLSTATE_RE.findall(result.stderr)))
|
|
raise VerificationError(
|
|
"psql_check_failed",
|
|
check,
|
|
observed_sqlstate=",".join(observed) if observed else "missing",
|
|
)
|
|
return _single_output_line(result, check)
|
|
|
|
|
|
def _parse_json_object(raw: str, check: str) -> dict[str, Any]:
|
|
try:
|
|
parsed = json.loads(raw)
|
|
except json.JSONDecodeError:
|
|
raise VerificationError("psql_json_invalid", check) from None
|
|
if not isinstance(parsed, dict):
|
|
raise VerificationError("psql_json_not_object", check)
|
|
return parsed
|
|
|
|
|
|
def _parse_zero_count(raw: str, check: str) -> int:
|
|
if raw != "0":
|
|
raise VerificationError("canary_row_count_nonzero", check)
|
|
return 0
|
|
|
|
|
|
def _sql_literal(value: str) -> str:
|
|
return "'" + value.replace("'", "''") + "'"
|
|
|
|
|
|
def normalize_stage_function_source(source: str) -> str:
|
|
normalized = source.replace("\r\n", "\n").replace("\r", "\n")
|
|
return normalized.strip("\n") + "\n"
|
|
|
|
|
|
def _identity_sql() -> str:
|
|
return """
|
|
select pg_catalog.jsonb_build_object(
|
|
'database', pg_catalog.current_database(),
|
|
'current_user', current_user,
|
|
'session_user', session_user,
|
|
'server_addr', pg_catalog.inet_server_addr()::text,
|
|
'server_port', pg_catalog.inet_server_port(),
|
|
'ssl', coalesce(
|
|
(select ssl from pg_catalog.pg_stat_ssl where pid = pg_catalog.pg_backend_pid()),
|
|
false
|
|
),
|
|
'ssl_version', coalesce(
|
|
(select version from pg_catalog.pg_stat_ssl where pid = pg_catalog.pg_backend_pid()),
|
|
''
|
|
)
|
|
)::text;
|
|
""".strip()
|
|
|
|
|
|
def _database_role_posture_sql(role_name: str) -> str:
|
|
return f"""
|
|
select 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,
|
|
'direct_membership_edges', (
|
|
select pg_catalog.count(*)
|
|
from pg_catalog.pg_auth_members membership
|
|
where membership.roleid = role_row.oid
|
|
or membership.member = role_row.oid
|
|
)
|
|
)::text
|
|
from pg_catalog.pg_roles role_row
|
|
where role_row.rolname = {_sql_literal(role_name)};
|
|
""".strip()
|
|
|
|
|
|
def _role_posture_sql() -> str:
|
|
return _database_role_posture_sql(RUNTIME_DATABASE_ROLE)
|
|
|
|
|
|
def _stage_owner_role_posture_sql() -> str:
|
|
return _database_role_posture_sql(STAGE_OWNER_DATABASE_ROLE)
|
|
|
|
|
|
def _function_privilege_posture_sql() -> str:
|
|
values_sql = ",\n ".join(
|
|
f"({_sql_literal(name)}, {_sql_literal(signature)})"
|
|
for name, signature, _expected_exists, _expected_execute in FUNCTION_PRIVILEGE_EXPECTATIONS
|
|
)
|
|
return f"""
|
|
with expected(check_name, signature) as (
|
|
values
|
|
{values_sql}
|
|
), resolved as (
|
|
select check_name,
|
|
signature,
|
|
pg_catalog.to_regprocedure(signature) as function_oid
|
|
from expected
|
|
)
|
|
select pg_catalog.jsonb_object_agg(
|
|
check_name,
|
|
pg_catalog.jsonb_build_object(
|
|
'exists', function_oid is not null,
|
|
'execute', coalesce(
|
|
pg_catalog.has_function_privilege(current_user, function_oid::pg_catalog.oid, 'EXECUTE'),
|
|
false
|
|
)
|
|
)
|
|
)::text
|
|
from resolved;
|
|
""".strip()
|
|
|
|
|
|
def _stage_function_definition_sql() -> str:
|
|
return f"""
|
|
with target as (
|
|
select pg_catalog.to_regprocedure({_sql_literal(STAGE_FUNCTION_SIGNATURE)})::pg_catalog.oid as oid
|
|
), function_row as (
|
|
select target.oid,
|
|
function_catalog.proowner,
|
|
function_catalog.prolang,
|
|
function_catalog.prokind,
|
|
function_catalog.prosecdef,
|
|
function_catalog.proleakproof,
|
|
function_catalog.proisstrict,
|
|
function_catalog.proretset,
|
|
function_catalog.provolatile,
|
|
function_catalog.proparallel,
|
|
function_catalog.pronargdefaults,
|
|
function_catalog.proargnames,
|
|
function_catalog.proargmodes,
|
|
function_catalog.proargtypes,
|
|
function_catalog.proconfig,
|
|
function_catalog.proacl,
|
|
function_catalog.prosrc,
|
|
owner_role.rolname as owner_name,
|
|
language_row.lanname as language_name
|
|
from target
|
|
left join pg_catalog.pg_proc function_catalog on function_catalog.oid = target.oid
|
|
left join pg_catalog.pg_roles owner_role on owner_role.oid = function_catalog.proowner
|
|
left join pg_catalog.pg_language language_row on language_row.oid = function_catalog.prolang
|
|
), runtime_role as (
|
|
select oid from pg_catalog.pg_roles where rolname = {_sql_literal(RUNTIME_DATABASE_ROLE)}
|
|
), owner_role as (
|
|
select oid from pg_catalog.pg_roles where rolname = {_sql_literal(STAGE_OWNER_DATABASE_ROLE)}
|
|
)
|
|
select pg_catalog.jsonb_build_object(
|
|
'exists', function_row.oid is not null,
|
|
'owner', function_row.owner_name,
|
|
'language', function_row.language_name,
|
|
'kind', function_row.prokind,
|
|
'security_definer', function_row.prosecdef,
|
|
'leakproof', function_row.proleakproof,
|
|
'strict', function_row.proisstrict,
|
|
'returns_set', function_row.proretset,
|
|
'volatility', function_row.provolatile,
|
|
'parallel', function_row.proparallel,
|
|
'argument_defaults', function_row.pronargdefaults,
|
|
'argument_names', function_row.proargnames,
|
|
'argument_modes', function_row.proargmodes,
|
|
'argument_types', (
|
|
select pg_catalog.jsonb_agg(pg_catalog.format_type(argument_type, null) order by ordinal)
|
|
from pg_catalog.unnest(function_row.proargtypes::pg_catalog.oid[])
|
|
with ordinality argument(argument_type, ordinal)
|
|
),
|
|
'result', pg_catalog.pg_get_function_result(function_row.oid),
|
|
'configuration', function_row.proconfig,
|
|
'runtime_execute', coalesce(
|
|
pg_catalog.has_function_privilege(current_user, function_row.oid, 'EXECUTE'),
|
|
false
|
|
),
|
|
'acl_exact', coalesce((
|
|
select pg_catalog.count(*) filter (
|
|
where acl.grantee = runtime_role.oid
|
|
and acl.privilege_type = 'EXECUTE'
|
|
and not acl.is_grantable
|
|
) = 1
|
|
and pg_catalog.count(*) filter (
|
|
where not (
|
|
acl.grantee in (runtime_role.oid, owner_role.oid)
|
|
and acl.privilege_type = 'EXECUTE'
|
|
and (acl.grantee = owner_role.oid or not acl.is_grantable)
|
|
)
|
|
) = 0
|
|
from runtime_role
|
|
cross join owner_role
|
|
cross join lateral pg_catalog.aclexplode(
|
|
coalesce(function_row.proacl, pg_catalog.acldefault('f', function_row.proowner))
|
|
) acl
|
|
), false),
|
|
'source', function_row.prosrc
|
|
)::text
|
|
from function_row;
|
|
""".strip()
|
|
|
|
|
|
def _catalog_privilege_posture_sql() -> str:
|
|
allowlist_values = ",\n ".join(
|
|
f"({_sql_literal(schema)}, {_sql_literal(relation)})" for schema, relation in READ_TABLE_ALLOWLIST
|
|
)
|
|
owner_insert_values = ",\n ".join(
|
|
f"({_sql_literal(column_name)})" for column_name in STAGE_OWNER_INSERT_COLUMNS
|
|
)
|
|
return f"""
|
|
with runtime_role as (
|
|
select oid from pg_catalog.pg_roles where rolname = {_sql_literal(RUNTIME_DATABASE_ROLE)}
|
|
), owner_role as (
|
|
select oid from pg_catalog.pg_roles where rolname = {_sql_literal(STAGE_OWNER_DATABASE_ROLE)}
|
|
), stage_function as (
|
|
select pg_catalog.to_regprocedure({_sql_literal(STAGE_FUNCTION_SIGNATURE)})::pg_catalog.oid as oid
|
|
), app_schemas as (
|
|
select oid, nspname
|
|
from pg_catalog.pg_namespace
|
|
where nspname <> 'information_schema'
|
|
and nspname !~ '^pg_'
|
|
), allowed_select(nspname, relname) as (
|
|
values
|
|
{allowlist_values}
|
|
), allowed_relation as (
|
|
select allowed_select.nspname,
|
|
allowed_select.relname,
|
|
relation.oid
|
|
from allowed_select
|
|
left join pg_catalog.pg_namespace namespace
|
|
on namespace.nspname = allowed_select.nspname
|
|
left join pg_catalog.pg_class relation
|
|
on relation.relnamespace = namespace.oid
|
|
and relation.relname = allowed_select.relname
|
|
and relation.relkind in ('r', 'p', 'v', 'm', 'f')
|
|
), owner_insert_column(attname) as (
|
|
values
|
|
{owner_insert_values}
|
|
)
|
|
select pg_catalog.jsonb_build_object(
|
|
'canonical_connect', coalesce(
|
|
pg_catalog.has_database_privilege(current_user, pg_catalog.current_database(), 'CONNECT'),
|
|
false
|
|
),
|
|
'runtime_connect_acl_exact', coalesce((
|
|
select pg_catalog.count(*) filter (
|
|
where database_row.datname = {_sql_literal(CANONICAL_DATABASE)}
|
|
and acl.privilege_type = 'CONNECT'
|
|
and not acl.is_grantable
|
|
) = 1
|
|
from pg_catalog.pg_database database_row
|
|
cross join runtime_role
|
|
cross join lateral pg_catalog.aclexplode(
|
|
coalesce(database_row.datacl, pg_catalog.acldefault('d', database_row.datdba))
|
|
) acl
|
|
where database_row.datname in ({_sql_literal(CANONICAL_DATABASE)}, {_sql_literal(LEGACY_DATABASE)})
|
|
and acl.grantee = runtime_role.oid
|
|
), false),
|
|
'unexpected_direct_database_acl_entries', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_database database_row
|
|
cross join runtime_role
|
|
cross join lateral pg_catalog.aclexplode(
|
|
coalesce(database_row.datacl, pg_catalog.acldefault('d', database_row.datdba))
|
|
) acl
|
|
where database_row.datname in ({_sql_literal(CANONICAL_DATABASE)}, {_sql_literal(LEGACY_DATABASE)})
|
|
and acl.grantee = runtime_role.oid
|
|
and not (
|
|
database_row.datname = {_sql_literal(CANONICAL_DATABASE)}
|
|
and acl.privilege_type = 'CONNECT'
|
|
and not acl.is_grantable
|
|
)
|
|
),
|
|
'owner_direct_database_acl_entries', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_database database_row
|
|
cross join owner_role
|
|
cross join lateral pg_catalog.aclexplode(
|
|
coalesce(database_row.datacl, pg_catalog.acldefault('d', database_row.datdba))
|
|
) acl
|
|
where database_row.datname in ({_sql_literal(CANONICAL_DATABASE)}, {_sql_literal(LEGACY_DATABASE)})
|
|
and acl.grantee = owner_role.oid
|
|
),
|
|
'database_create_grants', (
|
|
select pg_catalog.count(*)::int
|
|
from (values ({_sql_literal(CANONICAL_DATABASE)}), ({_sql_literal(LEGACY_DATABASE)})) database_name(name)
|
|
where pg_catalog.has_database_privilege(current_user, database_name.name, 'CREATE')
|
|
),
|
|
'owner_database_create_grants', (
|
|
select pg_catalog.count(*)::int
|
|
from (values ({_sql_literal(CANONICAL_DATABASE)}), ({_sql_literal(LEGACY_DATABASE)})) database_name(name)
|
|
where pg_catalog.has_database_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
database_name.name,
|
|
'CREATE'
|
|
)
|
|
),
|
|
'owned_database_objects', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_shdepend dependency
|
|
join runtime_role on runtime_role.oid = dependency.refobjid
|
|
where dependency.refclassid = 'pg_catalog.pg_authid'::pg_catalog.regclass
|
|
and dependency.deptype = 'o'
|
|
),
|
|
'owner_unexpected_owned_database_objects', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_shdepend dependency
|
|
join owner_role on owner_role.oid = dependency.refobjid
|
|
cross join stage_function
|
|
where dependency.refclassid = 'pg_catalog.pg_authid'::pg_catalog.regclass
|
|
and dependency.deptype = 'o'
|
|
and not (
|
|
dependency.dbid = (
|
|
select database_row.oid
|
|
from pg_catalog.pg_database database_row
|
|
where database_row.datname = pg_catalog.current_database()
|
|
)
|
|
and dependency.classid = 'pg_catalog.pg_proc'::pg_catalog.regclass
|
|
and dependency.objid = stage_function.oid
|
|
and dependency.objsubid = 0
|
|
)
|
|
),
|
|
'runtime_schema_usage', coalesce(
|
|
pg_catalog.has_schema_privilege(current_user, 'public', 'USAGE')
|
|
and pg_catalog.has_schema_privilege(current_user, 'kb_stage', 'USAGE'),
|
|
false
|
|
),
|
|
'owner_schema_usage', coalesce(
|
|
pg_catalog.has_schema_privilege({_sql_literal(STAGE_OWNER_DATABASE_ROLE)}, 'public', 'USAGE')
|
|
and pg_catalog.has_schema_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
'kb_stage',
|
|
'USAGE'
|
|
),
|
|
false
|
|
),
|
|
'unexpected_schema_usage', (
|
|
select pg_catalog.count(*)::int
|
|
from app_schemas
|
|
where app_schemas.nspname not in ('public', 'kb_stage')
|
|
and pg_catalog.has_schema_privilege(current_user, app_schemas.oid, 'USAGE')
|
|
),
|
|
'owner_unexpected_schema_usage', (
|
|
select pg_catalog.count(*)::int
|
|
from app_schemas
|
|
where app_schemas.nspname not in ('public', 'kb_stage')
|
|
and pg_catalog.has_schema_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
app_schemas.oid,
|
|
'USAGE'
|
|
)
|
|
),
|
|
'schema_create_grants', (
|
|
select pg_catalog.count(*)::int
|
|
from app_schemas
|
|
where pg_catalog.has_schema_privilege(current_user, app_schemas.oid, 'CREATE')
|
|
),
|
|
'owner_schema_create_grants', (
|
|
select pg_catalog.count(*)::int
|
|
from app_schemas
|
|
where pg_catalog.has_schema_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
app_schemas.oid,
|
|
'CREATE'
|
|
)
|
|
),
|
|
'missing_allowed_table_selects', (
|
|
select pg_catalog.count(*)::int
|
|
from allowed_relation
|
|
where allowed_relation.oid is null
|
|
or not coalesce(
|
|
pg_catalog.has_table_privilege(current_user, allowed_relation.oid, 'SELECT'),
|
|
false
|
|
)
|
|
),
|
|
'table_dml_grants', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_class relation
|
|
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
|
|
join app_schemas on app_schemas.oid = namespace.oid
|
|
cross join (values ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')) privilege(name)
|
|
where relation.relkind in ('r', 'p', 'v', 'm', 'f')
|
|
and pg_catalog.has_table_privilege(current_user, relation.oid, privilege.name)
|
|
),
|
|
'column_dml_grants', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_class relation
|
|
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
|
|
join app_schemas on app_schemas.oid = namespace.oid
|
|
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
|
|
cross join (values ('INSERT'), ('UPDATE'), ('REFERENCES')) privilege(name)
|
|
where relation.relkind in ('r', 'p', 'v', 'm', 'f')
|
|
and attribute.attnum > 0
|
|
and not attribute.attisdropped
|
|
and pg_catalog.has_column_privilege(current_user, relation.oid, attribute.attnum, privilege.name)
|
|
),
|
|
'unexpected_table_selects', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_class relation
|
|
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
|
|
join app_schemas on app_schemas.oid = namespace.oid
|
|
left join allowed_select
|
|
on allowed_select.nspname = namespace.nspname
|
|
and allowed_select.relname = relation.relname
|
|
where relation.relkind in ('r', 'p', 'v', 'm', 'f')
|
|
and pg_catalog.has_table_privilege(current_user, relation.oid, 'SELECT')
|
|
and allowed_select.relname is null
|
|
),
|
|
'unexpected_column_selects', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_class relation
|
|
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
|
|
join app_schemas on app_schemas.oid = namespace.oid
|
|
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
|
|
left join allowed_select
|
|
on allowed_select.nspname = namespace.nspname
|
|
and allowed_select.relname = relation.relname
|
|
where relation.relkind in ('r', 'p', 'v', 'm', 'f')
|
|
and attribute.attnum > 0
|
|
and not attribute.attisdropped
|
|
and pg_catalog.has_column_privilege(current_user, relation.oid, attribute.attnum, 'SELECT')
|
|
and allowed_select.relname is null
|
|
),
|
|
'sequence_grants', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_class relation
|
|
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
|
|
join app_schemas on app_schemas.oid = namespace.oid
|
|
cross join (values ('USAGE'), ('SELECT'), ('UPDATE')) privilege(name)
|
|
where relation.relkind = 'S'
|
|
and pg_catalog.has_sequence_privilege(current_user, relation.oid, privilege.name)
|
|
),
|
|
'unexpected_routine_execute', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_proc function_row
|
|
join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
|
|
join app_schemas on app_schemas.oid = namespace.oid
|
|
cross join stage_function
|
|
where function_row.oid is distinct from stage_function.oid
|
|
and pg_catalog.has_function_privilege(current_user, function_row.oid, 'EXECUTE')
|
|
),
|
|
'unexpected_security_definer_execute', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_proc function_row
|
|
join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
|
|
join app_schemas on app_schemas.oid = namespace.oid
|
|
cross join stage_function
|
|
where function_row.prosecdef
|
|
and function_row.oid is distinct from stage_function.oid
|
|
and pg_catalog.has_function_privilege(current_user, function_row.oid, 'EXECUTE')
|
|
),
|
|
'owner_grant_contract', coalesce(
|
|
not pg_catalog.has_table_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
pg_catalog.to_regclass('public.agents'),
|
|
'SELECT'
|
|
)
|
|
and pg_catalog.has_table_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
pg_catalog.to_regclass('kb_stage.kb_proposals'),
|
|
'SELECT'
|
|
)
|
|
and not pg_catalog.has_table_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
pg_catalog.to_regclass('kb_stage.kb_proposals'),
|
|
'INSERT'
|
|
),
|
|
false
|
|
),
|
|
'owner_missing_agent_select_columns', (
|
|
select pg_catalog.count(*)::int
|
|
from (values ('id'), ('handle')) required_column(attname)
|
|
left join pg_catalog.pg_attribute attribute
|
|
on attribute.attrelid = pg_catalog.to_regclass('public.agents')
|
|
and attribute.attname = required_column.attname
|
|
and attribute.attnum > 0
|
|
and not attribute.attisdropped
|
|
where attribute.attnum is null
|
|
or not coalesce(
|
|
pg_catalog.has_column_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
attribute.attrelid,
|
|
attribute.attnum,
|
|
'SELECT'
|
|
),
|
|
false
|
|
)
|
|
),
|
|
'owner_missing_proposal_insert_columns', (
|
|
select pg_catalog.count(*)::int
|
|
from owner_insert_column
|
|
left join pg_catalog.pg_attribute attribute
|
|
on attribute.attrelid = pg_catalog.to_regclass('kb_stage.kb_proposals')
|
|
and attribute.attname = owner_insert_column.attname
|
|
and attribute.attnum > 0
|
|
and not attribute.attisdropped
|
|
where attribute.attnum is null
|
|
or not coalesce(
|
|
pg_catalog.has_column_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
attribute.attrelid,
|
|
attribute.attnum,
|
|
'INSERT'
|
|
),
|
|
false
|
|
)
|
|
),
|
|
'owner_table_dml_grants', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_class relation
|
|
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
|
|
join app_schemas on app_schemas.oid = namespace.oid
|
|
cross join (values ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')) privilege(name)
|
|
where relation.relkind in ('r', 'p', 'v', 'm', 'f')
|
|
and pg_catalog.has_table_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
relation.oid,
|
|
privilege.name
|
|
)
|
|
),
|
|
'owner_unexpected_column_dml_grants', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_class relation
|
|
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
|
|
join app_schemas on app_schemas.oid = namespace.oid
|
|
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
|
|
cross join (values ('INSERT'), ('UPDATE'), ('REFERENCES')) privilege(name)
|
|
where relation.relkind in ('r', 'p', 'v', 'm', 'f')
|
|
and attribute.attnum > 0
|
|
and not attribute.attisdropped
|
|
and pg_catalog.has_column_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
relation.oid,
|
|
attribute.attnum,
|
|
privilege.name
|
|
)
|
|
and not (
|
|
privilege.name = 'INSERT'
|
|
and namespace.nspname = 'kb_stage'
|
|
and relation.relname = 'kb_proposals'
|
|
and attribute.attname in (select attname from owner_insert_column)
|
|
)
|
|
),
|
|
'owner_unexpected_table_selects', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_class relation
|
|
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
|
|
join app_schemas on app_schemas.oid = namespace.oid
|
|
where relation.relkind in ('r', 'p', 'v', 'm', 'f')
|
|
and pg_catalog.has_table_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
relation.oid,
|
|
'SELECT'
|
|
)
|
|
and not (namespace.nspname = 'kb_stage' and relation.relname = 'kb_proposals')
|
|
),
|
|
'owner_unexpected_column_selects', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_class relation
|
|
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
|
|
join app_schemas on app_schemas.oid = namespace.oid
|
|
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
|
|
where relation.relkind in ('r', 'p', 'v', 'm', 'f')
|
|
and attribute.attnum > 0
|
|
and not attribute.attisdropped
|
|
and pg_catalog.has_column_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
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')
|
|
)
|
|
)
|
|
),
|
|
'owner_sequence_grants', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_class relation
|
|
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
|
|
join app_schemas on app_schemas.oid = namespace.oid
|
|
cross join (values ('USAGE'), ('SELECT'), ('UPDATE')) privilege(name)
|
|
where relation.relkind = 'S'
|
|
and pg_catalog.has_sequence_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
relation.oid,
|
|
privilege.name
|
|
)
|
|
),
|
|
'owner_unexpected_routine_execute', (
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_proc function_row
|
|
join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
|
|
join app_schemas on app_schemas.oid = namespace.oid
|
|
cross join stage_function
|
|
where function_row.oid is distinct from stage_function.oid
|
|
and pg_catalog.has_function_privilege(
|
|
{_sql_literal(STAGE_OWNER_DATABASE_ROLE)},
|
|
function_row.oid,
|
|
'EXECUTE'
|
|
)
|
|
)
|
|
)::text;
|
|
""".strip()
|
|
|
|
|
|
def _allowed_read_sql() -> str:
|
|
return """
|
|
select pg_catalog.jsonb_build_object(
|
|
'claims_rows_sampled', (select pg_catalog.count(*) from (select 1 from public.claims limit 1) sampled),
|
|
'proposal_rows_sampled', (
|
|
select pg_catalog.count(*) from (select 1 from kb_stage.kb_proposals limit 1) sampled
|
|
)
|
|
)::text;
|
|
""".strip()
|
|
|
|
|
|
def _legacy_catalog_privilege_posture_sql() -> str:
|
|
return f"""
|
|
with scoped_role(role_name, field_prefix) as (
|
|
values
|
|
({_sql_literal(RUNTIME_DATABASE_ROLE)}, 'runtime'),
|
|
({_sql_literal(STAGE_OWNER_DATABASE_ROLE)}, 'owner')
|
|
), restore_schema as (
|
|
select oid
|
|
from pg_catalog.pg_namespace
|
|
where nspname = 'teleo_restore'
|
|
), posture as (
|
|
select scoped_role.field_prefix,
|
|
(
|
|
select pg_catalog.count(*)::int
|
|
from restore_schema
|
|
cross join (values ('USAGE'), ('CREATE')) privilege(name)
|
|
where pg_catalog.has_schema_privilege(
|
|
scoped_role.role_name,
|
|
restore_schema.oid,
|
|
privilege.name
|
|
)
|
|
) as schema_privileges,
|
|
(
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_class relation
|
|
join restore_schema on restore_schema.oid = relation.relnamespace
|
|
cross join (
|
|
values ('SELECT'), ('INSERT'), ('UPDATE'), ('DELETE'),
|
|
('TRUNCATE'), ('REFERENCES'), ('TRIGGER')
|
|
) privilege(name)
|
|
where relation.relkind in ('r', 'p', 'v', 'm', 'f')
|
|
and pg_catalog.has_table_privilege(
|
|
scoped_role.role_name,
|
|
relation.oid,
|
|
privilege.name
|
|
)
|
|
) as table_privileges,
|
|
(
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_class relation
|
|
join restore_schema on restore_schema.oid = relation.relnamespace
|
|
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
|
|
cross join (values ('SELECT'), ('INSERT'), ('UPDATE'), ('REFERENCES')) privilege(name)
|
|
where relation.relkind in ('r', 'p', 'v', 'm', 'f')
|
|
and attribute.attnum > 0
|
|
and not attribute.attisdropped
|
|
and pg_catalog.has_column_privilege(
|
|
scoped_role.role_name,
|
|
relation.oid,
|
|
attribute.attnum,
|
|
privilege.name
|
|
)
|
|
) as column_privileges,
|
|
(
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_class relation
|
|
join restore_schema on restore_schema.oid = relation.relnamespace
|
|
cross join (values ('USAGE'), ('SELECT'), ('UPDATE')) privilege(name)
|
|
where relation.relkind = 'S'
|
|
and pg_catalog.has_sequence_privilege(
|
|
scoped_role.role_name,
|
|
relation.oid,
|
|
privilege.name
|
|
)
|
|
) as sequence_privileges,
|
|
(
|
|
select pg_catalog.count(*)::int
|
|
from pg_catalog.pg_proc function_row
|
|
join restore_schema on restore_schema.oid = function_row.pronamespace
|
|
where pg_catalog.has_function_privilege(
|
|
scoped_role.role_name,
|
|
function_row.oid,
|
|
'EXECUTE'
|
|
)
|
|
) as routine_privileges
|
|
from scoped_role
|
|
)
|
|
select pg_catalog.jsonb_build_object(
|
|
'runtime_schema_privileges', max(schema_privileges) filter (where field_prefix = 'runtime'),
|
|
'runtime_table_privileges', max(table_privileges) filter (where field_prefix = 'runtime'),
|
|
'runtime_column_privileges', max(column_privileges) filter (where field_prefix = 'runtime'),
|
|
'runtime_sequence_privileges', max(sequence_privileges) filter (where field_prefix = 'runtime'),
|
|
'runtime_routine_privileges', max(routine_privileges) filter (where field_prefix = 'runtime'),
|
|
'owner_schema_privileges', max(schema_privileges) filter (where field_prefix = 'owner'),
|
|
'owner_table_privileges', max(table_privileges) filter (where field_prefix = 'owner'),
|
|
'owner_column_privileges', max(column_privileges) filter (where field_prefix = 'owner'),
|
|
'owner_sequence_privileges', max(sequence_privileges) filter (where field_prefix = 'owner'),
|
|
'owner_routine_privileges', max(routine_privileges) filter (where field_prefix = 'owner')
|
|
)::text
|
|
from posture;
|
|
""".strip()
|
|
|
|
|
|
def _canary_count_sql(source_ref: str) -> str:
|
|
return f"select pg_catalog.count(*)::text from kb_stage.kb_proposals where source_ref = {_sql_literal(source_ref)};"
|
|
|
|
|
|
def _stage_sql(run_id: str, source_ref: str) -> str:
|
|
return f"""
|
|
begin;
|
|
with staged as (
|
|
select kb_stage.stage_leoclean_proposal(
|
|
'revise_claim',
|
|
'runtime-permission-verification',
|
|
{_sql_literal(source_ref)},
|
|
'least-privilege transaction rollback verification',
|
|
pg_catalog.jsonb_build_object('run_id', {_sql_literal(run_id)}, 'verification', true)
|
|
) as proposal
|
|
)
|
|
select pg_catalog.jsonb_build_object(
|
|
'agent_id_matches', coalesce(
|
|
proposal ->> 'proposed_by_agent_id' = (
|
|
select id::text from public.agents where handle = 'leo'
|
|
),
|
|
false
|
|
),
|
|
'proposal', proposal
|
|
)::text
|
|
from staged;
|
|
rollback;
|
|
""".strip()
|
|
|
|
|
|
def _transactional(sql: str) -> str:
|
|
return f"begin;\n{sql.rstrip(';')};\nrollback;"
|
|
|
|
|
|
def _negative_checks(run_id: str, source_ref: str) -> tuple[NegativeCheck, ...]:
|
|
nil_uuid = "00000000-0000-0000-0000-000000000000"
|
|
return (
|
|
NegativeCheck(
|
|
"proposal_insert",
|
|
CANONICAL_DATABASE,
|
|
_transactional("insert into kb_stage.kb_proposals select * from kb_stage.kb_proposals where false"),
|
|
"42501",
|
|
),
|
|
NegativeCheck(
|
|
"proposal_update",
|
|
CANONICAL_DATABASE,
|
|
_transactional("update kb_stage.kb_proposals set status = status where false"),
|
|
"42501",
|
|
),
|
|
NegativeCheck(
|
|
"proposal_delete",
|
|
CANONICAL_DATABASE,
|
|
_transactional("delete from kb_stage.kb_proposals where false"),
|
|
"42501",
|
|
),
|
|
NegativeCheck(
|
|
"canonical_insert",
|
|
CANONICAL_DATABASE,
|
|
_transactional("insert into public.claims select * from public.claims where false"),
|
|
"42501",
|
|
),
|
|
NegativeCheck(
|
|
"canonical_update",
|
|
CANONICAL_DATABASE,
|
|
_transactional("update public.claims set id = id where false"),
|
|
"42501",
|
|
),
|
|
NegativeCheck(
|
|
"canonical_delete",
|
|
CANONICAL_DATABASE,
|
|
_transactional("delete from public.claims where false"),
|
|
"42501",
|
|
),
|
|
NegativeCheck(
|
|
"forged_proposer_overload",
|
|
CANONICAL_DATABASE,
|
|
_transactional(
|
|
"select kb_stage.stage_leoclean_proposal("
|
|
f"'revise_claim', 'forged-principal', 'verification', {_sql_literal(source_ref)}, "
|
|
f"'must be unavailable', pg_catalog.jsonb_build_object('run_id', {_sql_literal(run_id)}))"
|
|
),
|
|
"42883",
|
|
),
|
|
NegativeCheck(
|
|
"reviewer_approve_function",
|
|
CANONICAL_DATABASE,
|
|
_transactional(
|
|
"select kb_stage.approve_strict_proposal("
|
|
f"'{nil_uuid}'::uuid, 'revise_claim', '{{}}'::jsonb, 'leo', 'must be denied')"
|
|
),
|
|
"42501",
|
|
),
|
|
NegativeCheck(
|
|
"apply_assert_function",
|
|
CANONICAL_DATABASE,
|
|
_transactional(
|
|
"select kb_stage.assert_approved_proposal("
|
|
f"'{nil_uuid}'::uuid, 'revise_claim', '{{}}'::jsonb, 'hash', "
|
|
f"'{nil_uuid}'::uuid, '2000-01-01T00:00:00Z'::timestamptz, 'leo')"
|
|
),
|
|
"42501",
|
|
),
|
|
NegativeCheck(
|
|
"apply_finish_function",
|
|
CANONICAL_DATABASE,
|
|
_transactional(
|
|
"select kb_stage.finish_approved_proposal("
|
|
f"'{nil_uuid}'::uuid, 'revise_claim', '{{}}'::jsonb, 'hash', "
|
|
f"'{nil_uuid}'::uuid, '2000-01-01T00:00:00Z'::timestamptz, 'leo', 'leo')"
|
|
),
|
|
"42501",
|
|
),
|
|
NegativeCheck(
|
|
"set_role_stage_owner",
|
|
CANONICAL_DATABASE,
|
|
_transactional("set role leoclean_kb_stage_owner"),
|
|
"42501",
|
|
),
|
|
NegativeCheck(
|
|
"set_role_reviewer",
|
|
CANONICAL_DATABASE,
|
|
_transactional("set role kb_review"),
|
|
"42501",
|
|
),
|
|
NegativeCheck(
|
|
"set_role_apply",
|
|
CANONICAL_DATABASE,
|
|
_transactional("set role kb_apply"),
|
|
"42501",
|
|
),
|
|
NegativeCheck(
|
|
"set_role_administrator",
|
|
CANONICAL_DATABASE,
|
|
_transactional("set role postgres"),
|
|
"42501",
|
|
),
|
|
NegativeCheck(
|
|
"legacy_teleo_restore_read",
|
|
LEGACY_DATABASE,
|
|
_transactional("select 1 from teleo_restore.response_audit limit 1"),
|
|
"42501",
|
|
),
|
|
)
|
|
|
|
|
|
def _expect_sqlstate(
|
|
check: NegativeCheck,
|
|
*,
|
|
env: Mapping[str, str],
|
|
runner: Runner,
|
|
) -> dict[str, str]:
|
|
result = _call(
|
|
_psql_command(check.database, check.sql),
|
|
env=env,
|
|
runner=runner,
|
|
check=check.name,
|
|
)
|
|
if result.returncode == 0:
|
|
raise VerificationError(
|
|
"negative_permission_unexpectedly_succeeded",
|
|
check.name,
|
|
expected_sqlstate=check.expected_sqlstate,
|
|
observed_sqlstate="success",
|
|
)
|
|
|
|
observed = SQLSTATE_RE.findall(result.stderr)
|
|
unique_observed = sorted(set(observed))
|
|
if not observed:
|
|
raise VerificationError(
|
|
"negative_permission_sqlstate_missing",
|
|
check.name,
|
|
expected_sqlstate=check.expected_sqlstate,
|
|
observed_sqlstate="missing",
|
|
)
|
|
if unique_observed != [check.expected_sqlstate]:
|
|
raise VerificationError(
|
|
"negative_permission_sqlstate_mismatch",
|
|
check.name,
|
|
expected_sqlstate=check.expected_sqlstate,
|
|
observed_sqlstate=",".join(unique_observed),
|
|
)
|
|
return {
|
|
"check": check.name,
|
|
"expected_sqlstate": check.expected_sqlstate,
|
|
"observed_sqlstate": check.expected_sqlstate,
|
|
"result": "denied",
|
|
}
|
|
|
|
|
|
def _assert_identity(identity: dict[str, Any]) -> dict[str, Any]:
|
|
expected = {
|
|
"database": CANONICAL_DATABASE,
|
|
"current_user": RUNTIME_DATABASE_ROLE,
|
|
"session_user": RUNTIME_DATABASE_ROLE,
|
|
"server_addr": PRIVATE_CLOUDSQL_HOST,
|
|
"server_port": PRIVATE_CLOUDSQL_PORT,
|
|
"ssl": True,
|
|
}
|
|
if any(identity.get(key) != value for key, value in expected.items()):
|
|
raise VerificationError("private_ssl_identity_mismatch", "database_identity")
|
|
ssl_version = identity.get("ssl_version")
|
|
if not isinstance(ssl_version, str) or not ssl_version:
|
|
raise VerificationError("ssl_version_missing", "database_identity")
|
|
return {**expected, "ssl_version": ssl_version}
|
|
|
|
|
|
def _assert_role_posture(posture: dict[str, Any]) -> dict[str, Any]:
|
|
expected_booleans = {
|
|
"bypasses_rls": False,
|
|
"can_create_db": False,
|
|
"can_create_role": False,
|
|
"can_login": True,
|
|
"can_replicate": False,
|
|
"inherits_privileges": False,
|
|
"is_superuser": False,
|
|
}
|
|
if posture.get("role") != RUNTIME_DATABASE_ROLE:
|
|
raise VerificationError("runtime_role_posture_mismatch", "runtime_role_posture")
|
|
for key, expected in expected_booleans.items():
|
|
if posture.get(key) is not expected:
|
|
raise VerificationError("runtime_role_posture_mismatch", "runtime_role_posture")
|
|
|
|
connection_limit = posture.get("connection_limit")
|
|
membership_edges = posture.get("direct_membership_edges")
|
|
if isinstance(connection_limit, bool) or not isinstance(connection_limit, int) or connection_limit != 8:
|
|
raise VerificationError("runtime_role_posture_mismatch", "runtime_role_posture")
|
|
if isinstance(membership_edges, bool) or not isinstance(membership_edges, int) or membership_edges != 0:
|
|
raise VerificationError("runtime_role_posture_mismatch", "runtime_role_posture")
|
|
|
|
return {
|
|
"role": RUNTIME_DATABASE_ROLE,
|
|
**expected_booleans,
|
|
"connection_limit": connection_limit,
|
|
"direct_membership_edges": membership_edges,
|
|
}
|
|
|
|
|
|
def _assert_stage_owner_role_posture(posture: dict[str, Any]) -> dict[str, Any]:
|
|
expected_booleans = {
|
|
"bypasses_rls": False,
|
|
"can_create_db": False,
|
|
"can_create_role": False,
|
|
"can_login": False,
|
|
"can_replicate": False,
|
|
"inherits_privileges": False,
|
|
"is_superuser": False,
|
|
}
|
|
if posture.get("role") != STAGE_OWNER_DATABASE_ROLE:
|
|
raise VerificationError("stage_owner_role_posture_mismatch", "stage_owner_role_posture")
|
|
for key, expected in expected_booleans.items():
|
|
if posture.get(key) is not expected:
|
|
raise VerificationError("stage_owner_role_posture_mismatch", "stage_owner_role_posture")
|
|
|
|
connection_limit = posture.get("connection_limit")
|
|
membership_edges = posture.get("direct_membership_edges")
|
|
if isinstance(connection_limit, bool) or not isinstance(connection_limit, int) or connection_limit != -1:
|
|
raise VerificationError("stage_owner_role_posture_mismatch", "stage_owner_role_posture")
|
|
if isinstance(membership_edges, bool) or not isinstance(membership_edges, int) or membership_edges != 0:
|
|
raise VerificationError("stage_owner_role_posture_mismatch", "stage_owner_role_posture")
|
|
|
|
return {
|
|
"role": STAGE_OWNER_DATABASE_ROLE,
|
|
**expected_booleans,
|
|
"connection_limit": connection_limit,
|
|
"direct_membership_edges": membership_edges,
|
|
}
|
|
|
|
|
|
def _assert_function_privilege_posture(posture: dict[str, Any]) -> dict[str, dict[str, Any]]:
|
|
sanitized: dict[str, dict[str, Any]] = {}
|
|
for name, signature, expected_exists, expected_execute in FUNCTION_PRIVILEGE_EXPECTATIONS:
|
|
observed = posture.get(name)
|
|
if not isinstance(observed, dict):
|
|
raise VerificationError("function_privilege_posture_mismatch", "function_privilege_posture")
|
|
exists = observed.get("exists")
|
|
execute = observed.get("execute")
|
|
if exists is not expected_exists or execute is not expected_execute:
|
|
raise VerificationError("function_privilege_posture_mismatch", "function_privilege_posture")
|
|
sanitized[name] = {
|
|
"execute": expected_execute,
|
|
"exists": expected_exists,
|
|
"signature": signature,
|
|
}
|
|
return sanitized
|
|
|
|
|
|
def _assert_stage_function_definition(posture: dict[str, Any]) -> dict[str, Any]:
|
|
if posture.get("exists") is not True:
|
|
raise VerificationError("stage_function_definition_mismatch", "stage_function_definition")
|
|
for field, expected in STAGE_FUNCTION_METADATA_EXPECTATIONS:
|
|
observed = posture.get(field)
|
|
if type(observed) is not type(expected) or observed != expected:
|
|
raise VerificationError("stage_function_definition_mismatch", "stage_function_definition")
|
|
if posture.get("argument_modes") is not None:
|
|
raise VerificationError("stage_function_definition_mismatch", "stage_function_definition")
|
|
if posture.get("configuration") != ["search_path=pg_catalog, pg_temp"]:
|
|
raise VerificationError("stage_function_definition_mismatch", "stage_function_definition")
|
|
for field in ("acl_exact", "runtime_execute"):
|
|
if posture.get(field) is not True:
|
|
raise VerificationError("stage_function_definition_mismatch", "stage_function_definition")
|
|
|
|
source = posture.get("source")
|
|
if not isinstance(source, str) or len(source.encode("utf-8")) > MAX_STAGE_FUNCTION_SOURCE_BYTES:
|
|
raise VerificationError("stage_function_definition_mismatch", "stage_function_definition")
|
|
source_sha256 = hashlib.sha256(normalize_stage_function_source(source).encode("utf-8")).hexdigest()
|
|
if source_sha256 != EXPECTED_STAGE_FUNCTION_SOURCE_SHA256:
|
|
raise VerificationError("stage_function_definition_mismatch", "stage_function_definition")
|
|
|
|
return {
|
|
"acl_exact": True,
|
|
"argument_modes": None,
|
|
"configuration": ["search_path=pg_catalog, pg_temp"],
|
|
"exists": True,
|
|
**{field: expected for field, expected in STAGE_FUNCTION_METADATA_EXPECTATIONS},
|
|
"runtime_execute": True,
|
|
"signature": STAGE_FUNCTION_SIGNATURE,
|
|
"source_sha256": source_sha256,
|
|
}
|
|
|
|
|
|
def _assert_catalog_privilege_posture(posture: dict[str, Any]) -> dict[str, int | bool]:
|
|
sanitized: dict[str, int | bool] = {}
|
|
for field in CATALOG_ZERO_COUNT_FIELDS:
|
|
value = posture.get(field)
|
|
if isinstance(value, bool) or not isinstance(value, int) or value != 0:
|
|
raise VerificationError("catalog_privilege_posture_mismatch", "catalog_privilege_posture")
|
|
sanitized[field] = 0
|
|
for field in CATALOG_TRUE_FIELDS:
|
|
if posture.get(field) is not True:
|
|
raise VerificationError("catalog_privilege_posture_mismatch", "catalog_privilege_posture")
|
|
sanitized[field] = True
|
|
return sanitized
|
|
|
|
|
|
def _assert_legacy_catalog_privilege_posture(posture: dict[str, Any]) -> dict[str, int]:
|
|
sanitized: dict[str, int] = {}
|
|
for field in LEGACY_CATALOG_ZERO_COUNT_FIELDS:
|
|
value = posture.get(field)
|
|
if isinstance(value, bool) or not isinstance(value, int) or value != 0:
|
|
raise VerificationError("legacy_catalog_privilege_posture_mismatch", "legacy_catalog_privileges")
|
|
sanitized[field] = 0
|
|
return sanitized
|
|
|
|
|
|
def _assert_allowed_reads(readback: dict[str, Any]) -> dict[str, int]:
|
|
sanitized: dict[str, int] = {}
|
|
for key in ("claims_rows_sampled", "proposal_rows_sampled"):
|
|
value = readback.get(key)
|
|
if isinstance(value, bool) or not isinstance(value, int) or value not in (0, 1):
|
|
raise VerificationError("allowed_read_invalid", "canonical_allowed_reads")
|
|
sanitized[key] = value
|
|
return sanitized
|
|
|
|
|
|
def _assert_stage_result(staged: dict[str, Any], source_ref: str) -> dict[str, Any]:
|
|
proposal = staged.get("proposal")
|
|
if not isinstance(proposal, dict) or staged.get("agent_id_matches") is not True:
|
|
raise VerificationError("staged_proposal_agent_mismatch", "rolled_back_proposal_stage")
|
|
expected = {
|
|
"proposed_by_handle": "leo",
|
|
"source_ref": source_ref,
|
|
"status": "pending_review",
|
|
}
|
|
if any(proposal.get(key) != value for key, value in expected.items()):
|
|
raise VerificationError("staged_proposal_contract_mismatch", "rolled_back_proposal_stage")
|
|
proposed_by_agent_id = proposal.get("proposed_by_agent_id")
|
|
if not isinstance(proposed_by_agent_id, str) or not proposed_by_agent_id:
|
|
raise VerificationError("staged_proposal_agent_missing", "rolled_back_proposal_stage")
|
|
return {**expected, "agent_id_matches": True, "proposed_by_agent_id": proposed_by_agent_id}
|
|
|
|
|
|
def verify_runtime_permissions(
|
|
run_id: str,
|
|
*,
|
|
runner: Runner = subprocess_runner,
|
|
base_env: Mapping[str, str] | None = None,
|
|
effective_user: str | None = None,
|
|
) -> dict[str, Any]:
|
|
try:
|
|
run_id = validate_run_id(run_id)
|
|
except ValueError:
|
|
raise VerificationError("run_id_invalid", "arguments") from None
|
|
|
|
if effective_user is None:
|
|
try:
|
|
effective_user = pwd.getpwuid(os.geteuid()).pw_name
|
|
except KeyError:
|
|
raise VerificationError("unix_user_unknown", "runtime_user") from None
|
|
if effective_user != RUNTIME_UNIX_USER:
|
|
raise VerificationError("unexpected_unix_user", "runtime_user")
|
|
|
|
source = os.environ if base_env is None else base_env
|
|
source_ref = f"leo-runtime-permission:{run_id}"
|
|
generated_at = datetime.now(timezone.utc).isoformat(timespec="seconds").replace("+00:00", "Z")
|
|
|
|
with tempfile.TemporaryDirectory(prefix="leoclean-runtime-permission-gcloud-") as config_name:
|
|
config_dir = Path(config_name)
|
|
config_dir.chmod(0o700)
|
|
gcloud_env = _gcloud_environment(source, config_dir)
|
|
password = _read_scoped_password(env=gcloud_env, runner=runner)
|
|
psql_env = _psql_environment(source, password)
|
|
|
|
identity = _assert_identity(
|
|
_parse_json_object(
|
|
_run_psql_success(
|
|
check="database_identity",
|
|
database=CANONICAL_DATABASE,
|
|
sql=_identity_sql(),
|
|
env=psql_env,
|
|
runner=runner,
|
|
),
|
|
"database_identity",
|
|
)
|
|
)
|
|
role_posture = _assert_role_posture(
|
|
_parse_json_object(
|
|
_run_psql_success(
|
|
check="runtime_role_posture",
|
|
database=CANONICAL_DATABASE,
|
|
sql=_role_posture_sql(),
|
|
env=psql_env,
|
|
runner=runner,
|
|
),
|
|
"runtime_role_posture",
|
|
)
|
|
)
|
|
stage_owner_role_posture = _assert_stage_owner_role_posture(
|
|
_parse_json_object(
|
|
_run_psql_success(
|
|
check="stage_owner_role_posture",
|
|
database=CANONICAL_DATABASE,
|
|
sql=_stage_owner_role_posture_sql(),
|
|
env=psql_env,
|
|
runner=runner,
|
|
),
|
|
"stage_owner_role_posture",
|
|
)
|
|
)
|
|
function_privileges = _assert_function_privilege_posture(
|
|
_parse_json_object(
|
|
_run_psql_success(
|
|
check="function_privilege_posture",
|
|
database=CANONICAL_DATABASE,
|
|
sql=_function_privilege_posture_sql(),
|
|
env=psql_env,
|
|
runner=runner,
|
|
),
|
|
"function_privilege_posture",
|
|
)
|
|
)
|
|
stage_function_definition = _assert_stage_function_definition(
|
|
_parse_json_object(
|
|
_run_psql_success(
|
|
check="stage_function_definition",
|
|
database=CANONICAL_DATABASE,
|
|
sql=_stage_function_definition_sql(),
|
|
env=psql_env,
|
|
runner=runner,
|
|
),
|
|
"stage_function_definition",
|
|
)
|
|
)
|
|
catalog_privileges = _assert_catalog_privilege_posture(
|
|
_parse_json_object(
|
|
_run_psql_success(
|
|
check="catalog_privilege_posture",
|
|
database=CANONICAL_DATABASE,
|
|
sql=_catalog_privilege_posture_sql(),
|
|
env=psql_env,
|
|
runner=runner,
|
|
),
|
|
"catalog_privilege_posture",
|
|
)
|
|
)
|
|
legacy_catalog_privileges = _assert_legacy_catalog_privilege_posture(
|
|
_parse_json_object(
|
|
_run_psql_success(
|
|
check="legacy_catalog_privileges",
|
|
database=LEGACY_DATABASE,
|
|
sql=_legacy_catalog_privilege_posture_sql(),
|
|
env=psql_env,
|
|
runner=runner,
|
|
),
|
|
"legacy_catalog_privileges",
|
|
)
|
|
)
|
|
allowed_reads = _assert_allowed_reads(
|
|
_parse_json_object(
|
|
_run_psql_success(
|
|
check="canonical_allowed_reads",
|
|
database=CANONICAL_DATABASE,
|
|
sql=_allowed_read_sql(),
|
|
env=psql_env,
|
|
runner=runner,
|
|
),
|
|
"canonical_allowed_reads",
|
|
)
|
|
)
|
|
rows_before = _parse_zero_count(
|
|
_run_psql_success(
|
|
check="canary_rows_before",
|
|
database=CANONICAL_DATABASE,
|
|
sql=_canary_count_sql(source_ref),
|
|
env=psql_env,
|
|
runner=runner,
|
|
),
|
|
"canary_rows_before",
|
|
)
|
|
staged = _assert_stage_result(
|
|
_parse_json_object(
|
|
_run_psql_success(
|
|
check="rolled_back_proposal_stage",
|
|
database=CANONICAL_DATABASE,
|
|
sql=_stage_sql(run_id, source_ref),
|
|
env=psql_env,
|
|
runner=runner,
|
|
),
|
|
"rolled_back_proposal_stage",
|
|
),
|
|
source_ref,
|
|
)
|
|
rows_after = _parse_zero_count(
|
|
_run_psql_success(
|
|
check="canary_rows_after",
|
|
database=CANONICAL_DATABASE,
|
|
sql=_canary_count_sql(source_ref),
|
|
env=psql_env,
|
|
runner=runner,
|
|
),
|
|
"canary_rows_after",
|
|
)
|
|
|
|
negative_permissions = [
|
|
_expect_sqlstate(check, env=psql_env, runner=runner) for check in _negative_checks(run_id, source_ref)
|
|
]
|
|
administrator_secret = _assert_administrator_secret_denied(env=gcloud_env, runner=runner)
|
|
|
|
return {
|
|
"artifact": ARTIFACT_NAME,
|
|
"checks": {
|
|
"allowed_reads": allowed_reads,
|
|
"catalog_privileges": catalog_privileges,
|
|
"canary_rows_after": rows_after,
|
|
"canary_rows_before": rows_before,
|
|
"function_privileges": function_privileges,
|
|
"legacy_catalog_privileges": legacy_catalog_privileges,
|
|
"negative_permissions": negative_permissions,
|
|
"role_posture": role_posture,
|
|
"stage_function_definition": stage_function_definition,
|
|
"stage_owner_role_posture": stage_owner_role_posture,
|
|
"rolled_back_proposal": {
|
|
**staged,
|
|
"transaction": "rolled_back",
|
|
},
|
|
},
|
|
"current_tier": REQUIRED_TIER,
|
|
"database_identity": identity,
|
|
"execution": {
|
|
"canonical_writes_committed": False,
|
|
"service_independent": True,
|
|
"unix_user": RUNTIME_UNIX_USER,
|
|
},
|
|
"generated_at_utc": generated_at,
|
|
"mode": "live_private_gcp_staging",
|
|
"required_tier": REQUIRED_TIER,
|
|
"run_id": run_id,
|
|
"schema_version": 1,
|
|
"secret_access": {
|
|
"administrator": {
|
|
**administrator_secret,
|
|
"secret": ADMINISTRATOR_PASSWORD_SECRET,
|
|
},
|
|
"scoped_runtime": {
|
|
"result": "readable",
|
|
"secret": SCOPED_PASSWORD_SECRET,
|
|
"value_retained": False,
|
|
},
|
|
},
|
|
"status": "pass",
|
|
"safety": {
|
|
"canonical_write_committed": False,
|
|
"production_promotion_attempted": False,
|
|
"proposal_write_committed": False,
|
|
"secret_value_retained": False,
|
|
"telegram_send_attempted": False,
|
|
},
|
|
"target": {
|
|
"database": CANONICAL_DATABASE,
|
|
"host": PRIVATE_CLOUDSQL_HOST,
|
|
"port": PRIVATE_CLOUDSQL_PORT,
|
|
"project": PROJECT_ID,
|
|
"role": RUNTIME_DATABASE_ROLE,
|
|
"sslmode": "require",
|
|
},
|
|
}
|
|
|
|
|
|
def failure_receipt(run_id: str, error: VerificationError) -> dict[str, Any]:
|
|
return {
|
|
"artifact": ARTIFACT_NAME,
|
|
"current_tier": LOWER_TIER,
|
|
"error": error.as_dict(),
|
|
"generated_at_utc": datetime.now(timezone.utc).isoformat(timespec="seconds").replace("+00:00", "Z"),
|
|
"required_tier": REQUIRED_TIER,
|
|
"run_id": run_id,
|
|
"schema_version": 1,
|
|
"status": "fail",
|
|
}
|
|
|
|
|
|
def canonical_json(payload: Mapping[str, Any]) -> str:
|
|
return json.dumps(payload, ensure_ascii=True, separators=(",", ":"), sort_keys=True) + "\n"
|
|
|
|
|
|
def write_receipt(path: Path, payload: Mapping[str, Any]) -> None:
|
|
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
|
|
if hasattr(os, "O_NOFOLLOW"):
|
|
flags |= os.O_NOFOLLOW
|
|
descriptor = os.open(path, flags, 0o600)
|
|
try:
|
|
os.fchmod(descriptor, stat.S_IRUSR | stat.S_IWUSR)
|
|
with os.fdopen(descriptor, "w", encoding="utf-8", closefd=False) as handle:
|
|
handle.write(canonical_json(payload))
|
|
handle.flush()
|
|
os.fsync(handle.fileno())
|
|
finally:
|
|
os.close(descriptor)
|
|
|
|
|
|
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--run-id", required=True, type=_run_id_arg)
|
|
parser.add_argument("--output", type=Path, help="optional sanitized JSON receipt (written mode 0600)")
|
|
return parser.parse_args(argv)
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
args = parse_args(argv)
|
|
try:
|
|
receipt = verify_runtime_permissions(args.run_id)
|
|
returncode = 0
|
|
except VerificationError as exc:
|
|
receipt = failure_receipt(args.run_id, exc)
|
|
returncode = 1
|
|
except Exception:
|
|
receipt = failure_receipt(args.run_id, VerificationError("internal_error", "verifier"))
|
|
returncode = 1
|
|
|
|
if args.output is not None:
|
|
try:
|
|
write_receipt(args.output, receipt)
|
|
except OSError:
|
|
receipt = failure_receipt(args.run_id, VerificationError("output_write_failed", "output"))
|
|
returncode = 1
|
|
sys.stdout.write(canonical_json(receipt))
|
|
return returncode
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|