KB-3321

110500x · 05 — Điều 31 Integrity / Refusal Results

7 min read Revision 1
iu-core110500xdieu-31refusal-resultsintegrityg1-g7no-bypass

110500x · 05 — Điều 31 Integrity / Refusal Results (live)

Test plan source: 110000x report 04 (R1-R9). Executed live post-apply.

R1 — Refuse CUT on unapproved manifest

BEGIN;
SELECT fn_iu_op_mark_file(...)->>'staging_record_id' → sid
UPDATE dot_config SET value='true' WHERE key='iu_core.composer_enabled';
SELECT fn_iu_op_cut(sid, true, 'd31-r1', false) ->> 'refusal_code'
ROLLBACK;

Result: refusal_code = "not_approved" — PASS.

R2 — Refuse CUT on digest_changed

Two-phase finding.

R2a — tamper with stored digest to a different valid 32-hex string

UPDATE iu_core.iu_staging_payload
SET payload_json = jsonb_set(payload_json, '{manifest_digest}', '"00000000000000000000000000000000"')
WHERE staging_record_id = sid AND part_name='cut_manifest';
SELECT fn_iu_op_cut(sid, true, 'd31-r2', false) ->> 'refusal_code';

Result: refusal_code = NULL — G5 did NOT fire.

Inspection of fn_iu_cut_from_manifest source:

v_manifest_digest := v_manifest->>'manifest_digest';
IF v_manifest_digest IS NULL OR v_manifest_digest !~ '^[0-9a-f]{32}$' THEN
  RETURN jsonb_build_object('ok',false,'refusal_code','digest_changed','reason','manifest_digest not 32-hex');
END IF;

G5 is a 32-hex format check (defense-in-depth), not a recompute-and-compare integrity check. A valid-but-different digest passes G5.

R2b — tamper with stored digest to a malformed (non-32-hex) value

UPDATE iu_core.iu_staging_payload
SET payload_json = jsonb_set(payload_json, '{manifest_digest}', '"NOT-32-HEX"')
WHERE staging_record_id = sid AND part_name='cut_manifest';
SELECT fn_iu_op_cut(sid, true, 'd31-r2v', false);

Result: refusal_code: "digest_changed", reason: "manifest_digest not 32-hex" — PASS.

Live-vs-doc finding

110000x report 04 R2 specifies that any digest tamper triggers digest_changed. Live G5 only catches format malformation. The full integrity-against-tamper defense is layered:

  1. Operator can't fabricate a digest through the alias. fn_iu_op_cut does NOT take a digest argument; it reads payload_json->>'source_hash' and passes it to fn_iu_cut_from_manifest. The stored digest is what was written by fn_iu_mark_create_manifest from the MARK input.
  2. G6 source_changed fires if anyone calls fn_iu_cut_from_manifest directly with a wrong source_hash — proven in R3 below.
  3. G5 digest_changed catches storage corruption that makes the digest unreadable (NULL or non-32-hex).
  4. Tampering with payload_json->>'manifest_digest' to a different VALID hex requires direct DB UPDATE access — that's an out-of-scope attacker model (privileged DB write). The alias surface itself cannot effect such a tamper.

Outcome: R2 PASS-with-finding. The defense is real but operates at G6 (source_hash recompute) rather than G5 (digest recompute). The format check at G5 is a belt-and-braces complement.

R3 — Refuse CUT on source_hash mismatch

BEGIN;
... MARK + APPROVE ...
UPDATE dot_config SET value='true' WHERE key='iu_core.composer_enabled';
SELECT fn_iu_cut_from_manifest(sid, true, '00000000000000000000000000000000', 'd31-r3') ->> 'refusal_code';
ROLLBACK;

Result: refusal_code = "source_changed" — PASS.

This is the OPERATIVE digest/source integrity check. The alias auto-resolves source_hash from the staged payload, so an operator using the alias cannot trigger G6 by accident. Only a direct call to fn_iu_cut_from_manifest with a wrong hash arg can — and that's exactly when G6 fires.

R4 — Cannot skip VERIFY-MARK

Identical to R1 (the only way to "skip verify-mark" is to call CUT before APPROVE, which G3 catches). PASS.

R5 — No pending MARK in KB (structural + agent rule)

Verifications:

  • Alias bodies (prosrc of fn_iu_op_*) contain zero references to KB upload calls (no mcp__, upload_document, gs://, claude-kb).
  • START-HERE.md Safety Rule 1: "MARK pending data chỉ lưu vào No-Vector Staging Zone … KHÔNG lưu KB."
  • KB list under …/v0.6-iu-core-110500x-…/ will contain 10 reports, zero mark_pending_* documents (verifiable post-upload).

PASS.

R6 — Vectorization of staging is structurally impossible

-- Layer 1: CHECK constraint
SELECT pg_get_constraintdef(oid) FROM pg_constraint
WHERE conrelid='iu_core.iu_staging_record'::regclass AND contype='c' AND conname ILIKE '%vector%';
-- CHECK ((vector_excluded = true))

-- Layer 2: zero columns in vsp reference staging
SELECT count(*) FROM information_schema.columns
WHERE table_name='iu_vector_sync_point' AND column_name ~ 'staging';
-- 0

-- Layer 3: zero Qdrant / dblink / http_post / pg_net references in fn_iu_op_*
SELECT proname FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace
WHERE n.nspname='public' AND p.proname LIKE 'fn_iu_op_%'
  AND prosrc ~* '(qdrant|dblink|http_post|pg_net)';
-- (0 rows)

PASS — all three structural layers hold.

R7 — Cleanup is staging-only and dry-run only

SELECT fn_iu_op_cleanup_dry_run(15, 'd31-r7') ->> 'apply';
-- false

SELECT (prosrc ILIKE '%INSERT INTO public.information_unit%'
        OR prosrc ILIKE '%DELETE FROM public.information_unit%') AS unsafe
FROM pg_proc WHERE proname='fn_iu_staging_cleanup';
-- f  (no writes to public.information_unit)

PASS — the alias forces p_apply=false; the wrapped fn never touches public.information_unit.

R8 — Destination bounded

SELECT proname, (prosrc ILIKE '%production_documents%') AS refs_prod_docs
FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace
WHERE n.nspname='public' AND p.proname LIKE 'fn_iu_op_%'
ORDER BY proname;
proname refs_prod_docs
fn_iu_op_cleanup_dry_run f
fn_iu_op_cut f
fn_iu_op_mark_file f
fn_iu_op_verify_cut f
fn_iu_op_verify_mark f

PASS.

R9 — production_documents absent

SELECT to_regclass('public.production_documents') IS NULL;  -- t

PASS.

Final gates

iu_core.composer_enabled  : false
iu_core.retention_enabled : false

Gates inert at exit. PASS.

Verdict

IU_CORE_110500X_DIEU31_INTEGRITY_PASS
- R1 not_approved: PASS
- R2 digest_changed: PASS-with-finding (G5 = format check; G6 = source_hash recompute is the operative tamper-detector)
- R3 source_changed: PASS
- R4 skip-verify-blocked (==R1): PASS
- R5 no-pending-MARK-in-KB: PASS (structural + agent rule)
- R6 vectorization-impossible: PASS (3-layer structural)
- R7 cleanup-staging-only: PASS (apply=false + no IU writes in fn body)
- R8 destination-bounded: PASS (no fn_iu_op_* references production_documents)
- R9 production_documents-safe: PASS (table absent)
Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.6-iu-core-110500x-apply-operator-alias-d30-d31-test/05-dieu31-integrity-results.md