Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-- Deploy schemas/function_resolution/procedures/grants/grant_execute_create_invocation to pg

-- requires: schemas/function_resolution/schema
-- requires: schemas/function_resolution/procedures/create_invocation

BEGIN;

-- The request roles reach exactly one function in this schema. Schema USAGE is
-- not a widening: pgpm-defaults revokes EXECUTE on new functions from PUBLIC and
-- this schema grants it only to administrator, so the sibling resolver functions
-- stay unreachable and the grant below is the whole surface.
GRANT USAGE ON SCHEMA function_resolution TO anonymous;

GRANT EXECUTE ON FUNCTION function_resolution.create_invocation(uuid, text, text, jsonb, text, jsonb, uuid, uuid) TO authenticated;
GRANT EXECUTE ON FUNCTION function_resolution.create_invocation(uuid, text, text, jsonb, text, jsonb, uuid, uuid) TO anonymous;

COMMIT;
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ BEGIN;
--
-- The bindings document is a JSON array whose every entry NAMES ITS TARGET KIND:
--
-- {"path": "/login", "target": "function", "task_identifier": "mantra:signin"}
-- {"path": "/login", "target": "function", "task_identifier": "mantra:signin", "anonymous": true}
-- {"path": "/app", "target": "service", "service_id": "<uuid>"}
--
-- An entry may declare `anonymous`, which is the route's half of the anonymous
-- contract: the URL answers callers carrying no identity. It opens nothing on
-- its own — the definition behind it must declare anonymous_callable too — and
-- defaults to false, so a document that says nothing installs closed routes.
--
-- The kind is never inferred from which key happens to be present, and an entry
-- carrying keys for two kinds is a malformed document rather than a precedence
-- question: guessing is how a deployment silently binds /app to the wrong plane.
Expand Down Expand Up @@ -124,6 +129,7 @@ DECLARE
entry_target text;
entry_task text;
entry_service uuid;
entry_anonymous boolean;
target_column text;
target_id uuid;
service_found boolean;
Expand Down Expand Up @@ -374,6 +380,7 @@ BEGIN
LOOP
entry_path := entry ->> 'path';
entry_target := entry ->> 'target';
entry_anonymous := coalesce((entry ->> 'anonymous')::boolean, false);

IF entry_target = 'function' THEN
entry_task := entry ->> 'task_identifier';
Expand Down Expand Up @@ -426,8 +433,8 @@ BEGIN

-- pgsql-lint-disable-next-line no-dynamic-sql -- write-only: insert into the routes plane named by app_scope.routing_tables; every value is a bound parameter
query := format(
'INSERT INTO %I.%I (%s%sdomain_id, path, %I)
SELECT %s%s$1, $2, $3
'INSERT INTO %I.%I (%s%sdomain_id, path, anonymous, %I)
SELECT %s%s$1, $2, $6, $3
WHERE NOT EXISTS (
SELECT 1 FROM %I.%I AS x
WHERE x.domain_id = $1 AND x.path = $2%s)',
Expand All @@ -447,7 +454,7 @@ BEGIN
);

EXECUTE query USING domain_id, entry_path, target_id, key_value,
install_route_bindings.site_id;
install_route_bindings.site_id, entry_anonymous;
Comment on lines 436 to +457

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 security · high

Route anonymous flag is immutable, fail-open on revocation

install_route_bindings writes the route's anonymous column only in an INSERT guarded by WHERE NOT EXISTS on (domain_id, path) (install_route_bindings.sql:436); its only UPDATE (lines 475-489) backfills serving_site_field and never touches anonymous. Re-running with a document that removes anonymous: true to close a URL leaves the existing row anonymous = true, and create_invocation (which enforces r.anonymous for anonymous callers at line 287) keeps authorizing it, so the operator's revocation silently does not take effect and the URL stays publicly reachable. The same immutability also means a route newly declared anonymous never opens.

📋 Prompt for AI Agents

In packages/function-resolution/deploy/schemas/function_resolution/procedures/install_route_bindings.sql around lines 434-464, the anonymous flag is only written on INSERT and the WHERE NOT EXISTS guard (lines 438-440) skips existing routes, so a route once installed anonymous=true can never be revoked by re-running the install. Add an UPDATE, after the insert, that reconciles anonymous on already-present rows for the same (domain_id, path) and ownership key, mirroring the existing serving_site_field backfill at lines 475-489: UPDATE <routes_schema>.<routes_table> AS r SET anonymous = $6 WHERE r.domain_id = $1 AND r.path = $2 AND r.anonymous IS DISTINCT FROM $6 AND <routes_key pin>. This ensures removing anonymous: true from the document actually closes the route, preventing the fail-open case where a URL the operator closed remains anonymously reachable via create_invocation.

GET DIAGNOSTICS inserted = ROW_COUNT;

IF inserted > 0 THEN
Expand Down
2 changes: 2 additions & 0 deletions packages/function-resolution/pgpm.plan
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ schemas/function_resolution/procedures/validate_capabilities [schemas/function_r
schemas/function_resolution/procedures/image_catalog_row [schemas/function_resolution/schema schemas/function_resolution/procedures/frame_candidates] 2017-08-11T08:11:51Z constructive <constructive@5b0c196eeb62> # reachable image by name (nearest frame wins)
schemas/function_resolution/procedures/install_route_bindings [schemas/function_resolution/schema schemas/function_resolution/procedures/resolve metaschema-modules:schemas/metaschema_modules_public/tables/site_surface_module/table metaschema-modules:schemas/metaschema_modules_public/tables/route_module/table metaschema-modules:schemas/metaschema_modules_public/tables/resource_module/table pgpm-app-scope:schemas/app_scope/procedures/routing_tables] 2026-08-19T22:00:01Z devin <devin@cognition.ai> # install a set of route bindings (function or service targets) onto a site at one scope for one entity
schemas/function_resolution/procedures/install_mantra [schemas/function_resolution/schema schemas/function_resolution/procedures/install_route_bindings] 2026-08-19T22:00:02Z devin <devin@cognition.ai> # the mantra page set as function-target bindings over the general engine
schemas/function_resolution/procedures/create_invocation [schemas/function_resolution/schema schemas/function_resolution/procedures/resolve schemas/function_resolution/procedures/definitions_location metaschema-modules:schemas/metaschema_modules_public/tables/function_invocation_module/table metaschema-modules:schemas/metaschema_modules_public/tables/route_module/table pgpm-app-scope:schemas/app_scope/procedures/frames pgpm-app-scope:schemas/app_scope/procedures/routing_tables pgpm-jwt-claims:schemas/jwt_private/procedures/require_database_id pgpm-jwt-claims:schemas/jwt_public/procedures/current_user_id] 2026-09-01T03:00:00Z devin <devin@cognition.ai> # the one request-role insert surface for a sync invocation, with route-validated anonymous access
schemas/function_resolution/procedures/grants/grant_execute_create_invocation [schemas/function_resolution/schema schemas/function_resolution/procedures/create_invocation] 2026-09-01T03:00:01Z devin <devin@cognition.ai> # grant the request roles EXECUTE on create_invocation (and anonymous USAGE on the schema)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-- Revert schemas/function_resolution/procedures/create_invocation from pg

BEGIN;

DROP FUNCTION function_resolution.create_invocation(uuid, text, text, jsonb, text, jsonb, uuid, uuid);

COMMIT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- Revert schemas/function_resolution/procedures/grants/grant_execute_create_invocation from pg

BEGIN;

REVOKE EXECUTE ON FUNCTION function_resolution.create_invocation(uuid, text, text, jsonb, text, jsonb, uuid, uuid) FROM anonymous;
REVOKE EXECUTE ON FUNCTION function_resolution.create_invocation(uuid, text, text, jsonb, text, jsonb, uuid, uuid) FROM authenticated;

REVOKE USAGE ON SCHEMA function_resolution FROM anonymous;

COMMIT;
Binary file not shown.
Loading
Loading