KB-34E3 rev 4
23-P3A — IU Gateway Allow-list Patch — Execution Prompt (rev4)
20 min read Revision 4
pack-23p3apromptrev4gatewayallow-listexecution
23-P3A — IU Gateway Allow-list Patch — Execution Prompt (rev4)
Date: 2026-05-06 Author: Opus (Claude) Status: PROMPT rev4 — chờ GPT/User final review. CHƯA dispatch. Rev4: 8 fixes (named params, idempotent, exact unique, PUBLIC check, T8 fuller, T5B blocker, row-leak, verdict SKIPPED).
Mục tiêu
Patch fn_iu_gateway_write_guard() exact-match → allow-list.
Allow-list: fn_iu_create, fn_iu_apply_edit_draft.
fn_iu_apply_edit_draft chưa tồn tại — P3A chỉ mở cổng tương lai.
Report: knowledge/dev/laws/dieu44-trien-khai/reports/23-p3a-iu-gateway-allow-list-patch-report.md
Setup
#!/usr/bin/env bash
CONTAINER="postgres"
DB="directus"
DBUSER="directus"
PSQL=(docker exec -i "$CONTAINER" psql -U "$DBUSER" -d "$DB" -v ON_ERROR_STOP=1)
PSQL_NOSTOP=(docker exec -i "$CONTAINER" psql -U "$DBUSER" -d "$DB")
TS=$(date +%Y%m%d-%H%M%S)
LOG="/tmp/23-p3a.${TS}.log"
exec > >(tee -a "$LOG") 2>&1
PREFLIGHT_STATUS=""
PATCH_STATUS="NOT_RUN"
PATCH_EXIT=""
TEST_FAIL=0
PHASE_STATUS=""
P3B_READINESS="BLOCKED"
ROLLBACK_STATUS="N/A"
SOURCE_HASH_BEFORE=""
SOURCE_HASH_AFTER=""
GUARD_OID=""
FN_OWNER=""
FN_SECDEF=""
FN_ACL_BEFORE=""
FN_ACL_AFTER=""
FUNC_DEF_BEFORE=""
ALLOW_KEY_EXISTED_BEFORE="false"
ALLOW_KEY_VALUE_BEFORE=""
T1="NOT_RUN"; T2="NOT_RUN"; T3="NOT_RUN"; T4="NOT_RUN"
T5="NOT_RUN"; T5B="NOT_RUN"; T6="NOT_RUN"; T7="NOT_RUN"
T8="NOT_RUN"; T9="NOT_RUN"; T10="NOT_RUN"; T11="NOT_RUN"
PILOT_ADDR=""
IU_COUNT_BEFORE=""; UV_COUNT_BEFORE=""
IU_COUNT_AFTER=""; UV_COUNT_AFTER=""
EXISTING_UV_ID=""
ALREADY_PATCHED="false"
echo "=== P3A START $TS ==="
§1. Preflight
echo "=== PREFLIGHT ==="
# Gate 1: Guard exists once, public, zero args
GUARD_COUNT=$("${PSQL[@]}" -t -c "
SELECT count(*) FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE p.proname='fn_iu_gateway_write_guard' AND n.nspname='public' AND p.pronargs=0;
" | tr -d ' ')
echo "PREFLIGHT_GUARD_COUNT=$GUARD_COUNT"
[ "$GUARD_COUNT" = "1" ] || { PREFLIGHT_STATUS="FAIL"; echo "FAIL: guard_count=$GUARD_COUNT"; }
# Gate 2: Capture full definition + metadata
if [ -z "$PREFLIGHT_STATUS" ]; then
GUARD_OID=$("${PSQL[@]}" -t -c "
SELECT p.oid FROM pg_proc p JOIN pg_namespace n ON p.pronamespace=n.oid
WHERE p.proname='fn_iu_gateway_write_guard' AND n.nspname='public' AND p.pronargs=0;
" | tr -d ' ')
FUNC_DEF_BEFORE=$("${PSQL[@]}" -t -A -c "SELECT pg_get_functiondef($GUARD_OID);")
SOURCE_HASH_BEFORE=$("${PSQL[@]}" -t -c "SELECT md5(prosrc) FROM pg_proc WHERE oid=$GUARD_OID;" | tr -d ' ')
FN_OWNER=$("${PSQL[@]}" -t -c "SELECT proowner::regrole FROM pg_proc WHERE oid=$GUARD_OID;" | tr -d ' ')
FN_SECDEF=$("${PSQL[@]}" -t -c "SELECT prosecdef FROM pg_proc WHERE oid=$GUARD_OID;" | tr -d ' ')
FN_ACL_BEFORE=$("${PSQL[@]}" -t -A -c "SELECT proacl FROM pg_proc WHERE oid=$GUARD_OID;")
echo "$FUNC_DEF_BEFORE" > "/tmp/p3a-guard-backup-${TS}.sql"
echo "PREFLIGHT_HASH=$SOURCE_HASH_BEFORE OWNER=$FN_OWNER SECDEF=$FN_SECDEF"
fi
# Gate 3: Marker config
if [ -z "$PREFLIGHT_STATUS" ]; then
MARKER_KEY=$("${PSQL[@]}" -t -A -c "SELECT value FROM dot_config WHERE key='iu_create.gateway.marker_key';")
MARKER_VALUE=$("${PSQL[@]}" -t -A -c "SELECT value FROM dot_config WHERE key='iu_create.gateway.marker_value';")
if [ "$MARKER_KEY" != "app.canonical_writer" ] || [ "$MARKER_VALUE" != "fn_iu_create" ]; then
PREFLIGHT_STATUS="FAIL"; echo "FAIL: marker_key=$MARKER_KEY marker_value=$MARKER_VALUE"
fi
fi
# Gate 4: Triggers
if [ -z "$PREFLIGHT_STATUS" ]; then
TRIGGER_COUNT=$("${PSQL[@]}" -t -c "
SELECT count(*) FROM pg_trigger
WHERE tgname IN ('trg_aa_iu_gateway_write_guard','trg_aa_uv_gateway_write_guard')
AND tgenabled IN ('O','A');
" | tr -d ' ')
[ "$TRIGGER_COUNT" = "2" ] || { PREFLIGHT_STATUS="FAIL"; echo "FAIL: triggers=$TRIGGER_COUNT"; }
fi
# Gate 5: Allow-list key + idempotent check
if [ -z "$PREFLIGHT_STATUS" ]; then
ALLOW_KEY_CHECK=$("${PSQL_NOSTOP[@]}" -t -A -c "
SELECT value FROM dot_config WHERE key='iu_create.gateway.allowed_marker_values';
" 2>/dev/null)
if [ -n "$ALLOW_KEY_CHECK" ]; then
ALLOW_KEY_EXISTED_BEFORE="true"
ALLOW_KEY_VALUE_BEFORE="$ALLOW_KEY_CHECK"
if [ "$ALLOW_KEY_CHECK" = "fn_iu_create,fn_iu_apply_edit_draft" ]; then
echo "PREFLIGHT_ALLOW_LIST=already_seeded_exact"
else
PREFLIGHT_STATUS="FAIL"; echo "FAIL: incompatible allow-list=$ALLOW_KEY_CHECK"
fi
fi
fi
# Gate 6: No duplicate gateway keys
if [ -z "$PREFLIGHT_STATUS" ]; then
DUP=$("${PSQL[@]}" -t -c "
SELECT count(*) FROM (SELECT key FROM dot_config WHERE key LIKE 'iu_create.gateway.%' GROUP BY key HAVING count(*)>1) d;
" | tr -d ' ')
[ "$DUP" = "0" ] || { PREFLIGHT_STATUS="FAIL"; echo "FAIL: dup_keys=$DUP"; }
fi
# Gate 7: Guard already patched? (idempotent logic)
if [ -z "$PREFLIGHT_STATUS" ]; then
HAS_ALLOW_LOGIC=$("${PSQL[@]}" -t -c "
SELECT prosrc LIKE '%allowed_marker_values%' FROM pg_proc WHERE oid=$GUARD_OID;
" | tr -d ' ')
if [ "$HAS_ALLOW_LOGIC" = "t" ]; then
if [ "$ALLOW_KEY_CHECK" = "fn_iu_create,fn_iu_apply_edit_draft" ]; then
ALREADY_PATCHED="true"
echo "PREFLIGHT: guard already patched + key exact match → SKIPPED_ALREADY_PATCHED"
else
PREFLIGHT_STATUS="FAIL"; echo "FAIL: guard patched but key mismatch"
fi
fi
fi
# Gate 8: dot_config unique constraint exactly on column 'key'
if [ -z "$PREFLIGHT_STATUS" ]; then
KEY_UNIQUE=$("${PSQL[@]}" -t -c "
SELECT count(*) FROM pg_index i
WHERE i.indrelid = 'dot_config'::regclass
AND i.indisunique = true
AND i.indnatts = 1
AND (SELECT a.attname FROM pg_attribute a
WHERE a.attrelid = i.indrelid AND a.attnum = i.indkey[0]) = 'key';
" | tr -d ' ')
echo "PREFLIGHT_KEY_UNIQUE=$KEY_UNIQUE"
[ "$KEY_UNIQUE" != "0" ] || { PREFLIGHT_STATUS="FAIL"; echo "FAIL: no single-column unique on dot_config.key"; }
fi
# Gate 9: Discover fn_iu_create signature + assert required params
if [ -z "$PREFLIGHT_STATUS" ]; then
FN_CREATE_SIG=$("${PSQL[@]}" -t -A -c "
SELECT p.oid::regprocedure::text FROM pg_proc p
JOIN pg_namespace n ON n.oid=p.pronamespace
WHERE n.nspname='public' AND p.proname='fn_iu_create';
")
FN_CREATE_ARGS=$("${PSQL[@]}" -t -A -c "
SELECT pg_get_function_arguments(p.oid) FROM pg_proc p
JOIN pg_namespace n ON n.oid=p.pronamespace
WHERE n.nspname='public' AND p.proname='fn_iu_create';
")
echo "PREFLIGHT_FN_CREATE_SIG=$FN_CREATE_SIG"
echo "PREFLIGHT_FN_CREATE_ARGS=$FN_CREATE_ARGS"
# Assert 4 required named params exist
for PARAM in p_canonical_address p_title p_body p_actor; do
if ! echo "$FN_CREATE_ARGS" | grep -q "$PARAM"; then
PREFLIGHT_STATUS="FAIL"; echo "FAIL: fn_iu_create missing param $PARAM"
fi
done
fi
# Gate 10: Counts + existing UV for update test
if [ -z "$PREFLIGHT_STATUS" ]; then
IU_COUNT_BEFORE=$("${PSQL[@]}" -t -c "SELECT count(*) FROM information_unit;" | tr -d ' ')
UV_COUNT_BEFORE=$("${PSQL[@]}" -t -c "SELECT count(*) FROM unit_version;" | tr -d ' ')
EXISTING_UV_ID=$("${PSQL[@]}" -t -A -c "SELECT id FROM unit_version LIMIT 1;")
echo "IU_COUNT_BEFORE=$IU_COUNT_BEFORE UV_COUNT_BEFORE=$UV_COUNT_BEFORE EXISTING_UV_ID=$EXISTING_UV_ID"
fi
# Final preflight
if [ -z "$PREFLIGHT_STATUS" ]; then PREFLIGHT_STATUS="PASS"; fi
echo "PREFLIGHT_STATUS=$PREFLIGHT_STATUS ALREADY_PATCHED=$ALREADY_PATCHED"
if [ "$PREFLIGHT_STATUS" != "PASS" ]; then
PATCH_STATUS="NOT_RUN"; PHASE_STATUS="FAIL"; P3B_READINESS="BLOCKED"
echo "PREFLIGHT FAILED. Skip patch/tests."
fi
§2. Patch (skip if already patched)
if [ "$PREFLIGHT_STATUS" = "PASS" ] && [ "$ALREADY_PATCHED" = "true" ]; then
PATCH_STATUS="SKIPPED_ALREADY_PATCHED"
SOURCE_HASH_AFTER=$SOURCE_HASH_BEFORE
echo "PATCH_STATUS=SKIPPED_ALREADY_PATCHED"
fi
if [ "$PREFLIGHT_STATUS" = "PASS" ] && [ "$ALREADY_PATCHED" = "false" ]; then
echo "=== PATCH ==="
PATCH_EXIT=0
"${PSQL[@]}" <<'PATCH_SQL' || PATCH_EXIT=$?
BEGIN;
INSERT INTO dot_config (key, value)
VALUES ('iu_create.gateway.allowed_marker_values', 'fn_iu_create,fn_iu_apply_edit_draft')
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
CREATE OR REPLACE FUNCTION public.fn_iu_gateway_write_guard()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = pg_catalog, public
AS $fn$
DECLARE
v_marker_key text;
v_marker_value text;
v_current text;
v_allowed_csv text;
v_allowed text[];
BEGIN
SELECT value INTO v_marker_key
FROM public.dot_config WHERE key = 'iu_create.gateway.marker_key';
SELECT value INTO v_marker_value
FROM public.dot_config WHERE key = 'iu_create.gateway.marker_value';
IF v_marker_key IS NULL THEN
RETURN NEW;
END IF;
v_current := current_setting(v_marker_key, true);
SELECT value INTO v_allowed_csv
FROM public.dot_config
WHERE key = 'iu_create.gateway.allowed_marker_values';
IF v_allowed_csv IS NOT NULL AND v_allowed_csv <> '' THEN
SELECT array_agg(btrim(elem))
INTO v_allowed
FROM unnest(string_to_array(v_allowed_csv, ',')) AS elem
WHERE btrim(elem) <> '';
IF v_allowed IS NOT NULL AND v_current = ANY(v_allowed) THEN
RETURN NEW;
END IF;
ELSE
IF v_current IS NOT NULL AND v_current = v_marker_value THEN
RETURN NEW;
END IF;
END IF;
RAISE EXCEPTION
'IU Gateway blocked: direct write to % not allowed. '
'Use canonical functions (fn_iu_create, fn_iu_apply_edit_draft). '
'See README: %',
TG_TABLE_NAME,
COALESCE(
(SELECT value FROM public.dot_config WHERE key = 'iu_create.gateway.readme_path'),
'knowledge/dev/laws/dieu44-trien-khai/readme/iu-create-gateway-readme.md'
);
END;
$fn$;
REVOKE ALL ON FUNCTION public.fn_iu_gateway_write_guard() FROM PUBLIC;
COMMIT;
PATCH_SQL
echo "PATCH_EXIT=$PATCH_EXIT"
if [ "$PATCH_EXIT" != "0" ]; then
PATCH_STATUS="FAIL"; PHASE_STATUS="FAIL"
else
PATCH_STATUS="OK"
SOURCE_HASH_AFTER=$("${PSQL[@]}" -t -c "SELECT md5(prosrc) FROM pg_proc WHERE oid=$GUARD_OID;" | tr -d ' ')
FN_ACL_AFTER=$("${PSQL[@]}" -t -A -c "SELECT proacl FROM pg_proc WHERE oid=$GUARD_OID;")
echo "SOURCE_HASH_AFTER=$SOURCE_HASH_AFTER"
fi
fi # patch needed
§3. Tests (run for both OK and SKIPPED_ALREADY_PATCHED)
if [ "$PATCH_STATUS" = "OK" ] || [ "$PATCH_STATUS" = "SKIPPED_ALREADY_PATCHED" ]; then
echo "=== TESTS ==="
# If skipped, capture current metadata for T10
if [ "$PATCH_STATUS" = "SKIPPED_ALREADY_PATCHED" ]; then
FN_ACL_AFTER=$FN_ACL_BEFORE
fi
# --- T1: Source has allow-list logic ---
T1_RAW=$("${PSQL[@]}" -t -c "SELECT prosrc LIKE '%allowed_marker_values%' FROM pg_proc WHERE oid=$GUARD_OID;" | tr -d ' ')
[ "$T1_RAW" = "t" ] && T1="PASS" || { T1="FAIL"; TEST_FAIL=$((TEST_FAIL+1)); }
echo "T1=$T1"
# --- T2: dot_config key correct ---
T2_RAW=$("${PSQL[@]}" -t -A -c "SELECT value FROM dot_config WHERE key='iu_create.gateway.allowed_marker_values';")
[ "$T2_RAW" = "fn_iu_create,fn_iu_apply_edit_draft" ] && T2="PASS" || { T2="FAIL"; TEST_FAIL=$((TEST_FAIL+1)); }
echo "T2=$T2 value=$T2_RAW"
# --- T3: fn_iu_create REAL (named params, retain pilot) ---
PILOT_ADDR="test/p3a/gateway-verify-${TS}"
T3_RAW=$("${PSQL[@]}" -t -A -c "
WITH r AS (
SELECT public.fn_iu_create(
p_canonical_address => '${PILOT_ADDR}',
p_title => 'P3A gateway verify pilot',
p_body => 'Body: fn_iu_create after allow-list patch.',
p_actor => 'agent:p3a-test'
) AS j
)
SELECT j->>'status' FROM r;
")
echo "T3_STATUS=$T3_RAW PILOT=$PILOT_ADDR"
if [ "$T3_RAW" = "created" ] || [ "$T3_RAW" = "exists_complete" ]; then T3="PASS"; else T3="FAIL"; TEST_FAIL=$((TEST_FAIL+1)); fi
echo "T3=$T3"
IU_COUNT_AFTER=$("${PSQL[@]}" -t -c "SELECT count(*) FROM information_unit;" | tr -d ' ')
UV_COUNT_AFTER=$("${PSQL[@]}" -t -c "SELECT count(*) FROM unit_version;" | tr -d ' ')
echo "IU_AFTER=$IU_COUNT_AFTER UV_AFTER=$UV_COUNT_AFTER"
# --- T4: Direct IU INSERT blocked ---
T4_OUTPUT=$("${PSQL_NOSTOP[@]}" -c "
INSERT INTO information_unit (id,canonical_address,unit_kind,lifecycle_status,owner_ref,identity_profile)
VALUES (gen_random_uuid(),'test/p3a/direct-block-${TS}','test','draft','test','{}');
" 2>&1) || true
if echo "$T4_OUTPUT" | grep -q "IU Gateway blocked"; then T4="PASS"; else T4="FAIL"; TEST_FAIL=$((TEST_FAIL+1)); fi
echo "T4=$T4"
# --- T5: Direct UV INSERT ---
T5_OUTPUT=$("${PSQL_NOSTOP[@]}" -c "
INSERT INTO unit_version (id,unit_id,body,content_hash,version_seq,lifecycle_status,created_by)
VALUES (gen_random_uuid(),gen_random_uuid(),'t','t',999,'draft','t');
" 2>&1) || true
if echo "$T5_OUTPUT" | grep -q "IU Gateway blocked"; then
T5="PASS_GATEWAY_BLOCKED"
elif echo "$T5_OUTPUT" | grep -q "violates foreign key"; then
T5="PASS_WRITE_PREVENTED_FK_FIRST"
else
T5="FAIL"; TEST_FAIL=$((TEST_FAIL+1))
fi
echo "T5=$T5"
# --- T5B: Direct UV UPDATE blocked (proves UPDATE guard) ---
if [ -n "$EXISTING_UV_ID" ]; then
T5B_OUTPUT=$("${PSQL_NOSTOP[@]}" <<EOF 2>&1) || true
BEGIN;
UPDATE unit_version SET body = body || ' p3a-test' WHERE id = '${EXISTING_UV_ID}';
ROLLBACK;
EOF
if echo "$T5B_OUTPUT" | grep -q "IU Gateway blocked"; then
T5B="PASS_GATEWAY_BLOCKED_UPDATE"
else
T5B="FAIL"; TEST_FAIL=$((TEST_FAIL+1))
fi
else
T5B="FAIL_NO_EXISTING_UV"
TEST_FAIL=$((TEST_FAIL+1))
fi
echo "T5B=$T5B"
# --- T6: fn_iu_apply_edit_draft marker accepted ---
T6_OUTPUT=$("${PSQL_NOSTOP[@]}" <<'T6SQL' 2>&1) || true
BEGIN;
SELECT set_config('app.canonical_writer', 'fn_iu_apply_edit_draft', true);
INSERT INTO information_unit (id,canonical_address,unit_kind,lifecycle_status,owner_ref,identity_profile)
VALUES (gen_random_uuid(),'test/p3a/apply-marker-ROLLBACK','test','draft','test','{"title":"t6"}');
ROLLBACK;
T6SQL
if echo "$T6_OUTPUT" | grep -q "IU Gateway blocked"; then
T6="FAIL_REJECTED_BY_GATEWAY"; TEST_FAIL=$((TEST_FAIL+1))
elif echo "$T6_OUTPUT" | grep -q "ROLLBACK"; then
T6="PASS_GATEWAY_ACCEPTED_INSERT_ROLLED_BACK"
elif echo "$T6_OUTPUT" | grep -q "ERROR"; then
T6="PASS_GATEWAY_ACCEPTED_BUT_OTHER_ERROR"
else
T6="PASS_GATEWAY_ACCEPTED_INSERT_ROLLED_BACK"
fi
echo "T6=$T6"
"${PSQL_NOSTOP[@]}" -c "SELECT set_config('app.canonical_writer','',true);" >/dev/null 2>&1
# --- T7: Unknown marker blocked ---
T7_OUTPUT=$("${PSQL_NOSTOP[@]}" <<'T7SQL' 2>&1) || true
BEGIN;
SELECT set_config('app.canonical_writer', 'fn_unknown_bad_actor', true);
INSERT INTO information_unit (id,canonical_address,unit_kind,lifecycle_status,owner_ref,identity_profile)
VALUES (gen_random_uuid(),'test/p3a/unknown-block-ROLLBACK','test','draft','test','{}');
ROLLBACK;
T7SQL
if echo "$T7_OUTPUT" | grep -q "IU Gateway blocked"; then T7="PASS"; else T7="FAIL"; TEST_FAIL=$((TEST_FAIL+1)); fi
echo "T7=$T7"
"${PSQL_NOSTOP[@]}" -c "SELECT set_config('app.canonical_writer','',true);" >/dev/null 2>&1
# --- T8: Error message guidance ---
T8="PASS"
if ! echo "$T4_OUTPUT" | grep -q "IU Gateway blocked"; then T8="FAIL_NO_GATEWAY_MSG"; TEST_FAIL=$((TEST_FAIL+1)); fi
if ! echo "$T4_OUTPUT" | grep -q "fn_iu_create"; then T8="FAIL_NO_CANONICAL_FN"; TEST_FAIL=$((TEST_FAIL+1)); fi
if ! echo "$T4_OUTPUT" | grep -qi "readme"; then
T8="WARN_NO_README_IN_MSG" # non-critical
fi
echo "T8=$T8"
# --- T9: Triggers still enabled ---
T9_RAW=$("${PSQL[@]}" -t -c "
SELECT count(*) FROM pg_trigger
WHERE tgname IN ('trg_aa_iu_gateway_write_guard','trg_aa_uv_gateway_write_guard')
AND tgenabled IN ('O','A');
" | tr -d ' ')
[ "$T9_RAW" = "2" ] && T9="PASS" || { T9="FAIL"; TEST_FAIL=$((TEST_FAIL+1)); }
echo "T9=$T9"
# --- T10: Metadata preserved ---
T10="PASS"
FN_OWNER_AFTER=$("${PSQL[@]}" -t -c "SELECT proowner::regrole FROM pg_proc WHERE oid=$GUARD_OID;" | tr -d ' ')
FN_SECDEF_AFTER=$("${PSQL[@]}" -t -c "SELECT prosecdef FROM pg_proc WHERE oid=$GUARD_OID;" | tr -d ' ')
FN_CONFIG_AFTER=$("${PSQL[@]}" -t -A -c "SELECT proconfig FROM pg_proc WHERE oid=$GUARD_OID;")
[ "$FN_OWNER_AFTER" = "$FN_OWNER" ] || { T10="FAIL_OWNER"; TEST_FAIL=$((TEST_FAIL+1)); }
[ "$FN_SECDEF_AFTER" = "t" ] || { T10="FAIL_SECDEF"; TEST_FAIL=$((TEST_FAIL+1)); }
echo "$FN_CONFIG_AFTER" | grep -q "pg_catalog" || { T10="FAIL_SEARCHPATH"; TEST_FAIL=$((TEST_FAIL+1)); }
# PUBLIC not broadened: check via aclexplode or routine_privileges
PUBLIC_GRANT=$("${PSQL_NOSTOP[@]}" -t -c "
SELECT count(*) FROM (
SELECT (aclexplode(proacl)).grantee AS gid,
(aclexplode(proacl)).privilege_type AS priv
FROM pg_proc WHERE oid=$GUARD_OID
) x WHERE x.gid = 0 AND x.priv = 'EXECUTE';
" 2>/dev/null | tr -d ' ')
# grantee=0 is PUBLIC in aclexplode
if [ "$PUBLIC_GRANT" != "" ] && [ "$PUBLIC_GRANT" != "0" ]; then
T10="FAIL_PUBLIC_BROADENED"; TEST_FAIL=$((TEST_FAIL+1))
fi
echo "T10=$T10 owner=$FN_OWNER_AFTER secdef=$FN_SECDEF_AFTER public_grant=$PUBLIC_GRANT"
# --- T11: Row-leak check (T6/T7 rollback rows must not exist) ---
LEAK_COUNT=$("${PSQL[@]}" -t -c "
SELECT count(*) FROM information_unit
WHERE canonical_address IN ('test/p3a/apply-marker-ROLLBACK','test/p3a/unknown-block-ROLLBACK');
" | tr -d ' ')
if [ "$LEAK_COUNT" = "0" ]; then T11="PASS"; else T11="FAIL_ROW_LEAK"; TEST_FAIL=$((TEST_FAIL+1)); fi
echo "T11_ROW_LEAK=$T11 count=$LEAK_COUNT"
echo "TEST_FAIL_COUNT=$TEST_FAIL"
fi # tests
§4. Rollback (only if patch ran AND tests failed)
if [ "$TEST_FAIL" != "0" ] && [ "$PATCH_STATUS" = "OK" ]; then
echo "=== ROLLBACK ==="
ROLLBACK_STATUS="ATTEMPTING"
RESTORE_FN=1; RESTORE_KEY=1
if [ -f "/tmp/p3a-guard-backup-${TS}.sql" ]; then
"${PSQL_NOSTOP[@]}" -f "/tmp/p3a-guard-backup-${TS}.sql" 2>&1 && RESTORE_FN=0
fi
if [ "$ALLOW_KEY_EXISTED_BEFORE" = "true" ]; then
"${PSQL_NOSTOP[@]}" -c "UPDATE dot_config SET value='${ALLOW_KEY_VALUE_BEFORE}' WHERE key='iu_create.gateway.allowed_marker_values';" 2>&1 && RESTORE_KEY=0
else
"${PSQL_NOSTOP[@]}" -c "DELETE FROM dot_config WHERE key='iu_create.gateway.allowed_marker_values';" 2>&1 && RESTORE_KEY=0
fi
RESTORED_HASH=$("${PSQL[@]}" -t -c "SELECT md5(prosrc) FROM pg_proc WHERE oid=$GUARD_OID;" | tr -d ' ')
if [ "$RESTORED_HASH" = "$SOURCE_HASH_BEFORE" ] && [ "$RESTORE_FN" = "0" ] && [ "$RESTORE_KEY" = "0" ]; then
ROLLBACK_STATUS="RESTORED"; PHASE_STATUS="FAIL"
else
ROLLBACK_STATUS="CRITICAL"; PHASE_STATUS="CRITICAL"
fi
echo "ROLLBACK_STATUS=$ROLLBACK_STATUS"
fi
§5. Report
echo "=== FINAL VERDICT ==="
if [ "$PREFLIGHT_STATUS" != "PASS" ]; then
PHASE_STATUS="FAIL"; P3B_READINESS="BLOCKED"
elif [ "$PATCH_STATUS" = "FAIL" ]; then
PHASE_STATUS="FAIL"; P3B_READINESS="BLOCKED"
elif [ "$TEST_FAIL" = "0" ]; then
PHASE_STATUS="PASS"; P3B_READINESS="READY"
fi
echo "phase_status=$PHASE_STATUS"
echo "p3b_readiness=$P3B_READINESS"
echo "patch_status=$PATCH_STATUS"
echo "preflight_status=$PREFLIGHT_STATUS"
echo "source_hash_before=$SOURCE_HASH_BEFORE"
echo "source_hash_after=$SOURCE_HASH_AFTER"
echo "rollback_status=$ROLLBACK_STATUS"
echo "test_fail_count=$TEST_FAIL"
echo "T1=$T1 T2=$T2 T3=$T3 T4=$T4 T5=$T5 T5B=$T5B T6=$T6 T7=$T7 T8=$T8 T9=$T9 T10=$T10 T11=$T11"
echo "pilot=$PILOT_ADDR iu=$IU_COUNT_BEFORE→$IU_COUNT_AFTER uv=$UV_COUNT_BEFORE→$UV_COUNT_AFTER"
echo "LOG=$LOG"
echo ""
echo "=== AGENT: UPLOAD REPORT NOW ==="
echo "Path: knowledge/dev/laws/dieu44-trien-khai/reports/23-p3a-iu-gateway-allow-list-patch-report.md"
echo "Include: full verdict, all T1-T11, hash before/after, pilot address, counts, log path."
echo "Upload even on FAIL/CRITICAL."
Hard Boundaries
- ❌ No table DDL
- ❌ No new functions
- ❌ No trigger changes
- ❌ No vector mutation
- ❌ No cleanup
- ❌ No retry / No improvise
23-P3A Prompt rev4 | 2026-05-06 | 8 fixes | Chờ GPT/User final review