From 0b7ef113338bc18400bbe955bb80e5baa62abdb1 Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Sun, 7 Jun 2026 20:45:16 +0200 Subject: [PATCH] fix(manager-core): F-M-002 coupled-nullness CHECK on encrypted-secret column pairs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two (encrypted, nonce) column pairs are each nullable independently in the current schema: - email_trigger_details.inbound_secret_encrypted / _nonce - app_secrets.realtime_signing_key_encrypted / _nonce A bug or partial write could leave one populated and the other NULL, silently bypassing signature verification (receivers decrypt only if the encrypted column is set). Migration 0038 adds CHECK ((enc IS NULL) = (nonce IS NULL)) on both tables — defence-in-depth: catches an invariant violation at the DB boundary even if the writing code regresses. AUDIT.md anchor: F-M-002. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../0038_email_realtime_secret_check.sql | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 crates/manager-core/migrations/0038_email_realtime_secret_check.sql diff --git a/crates/manager-core/migrations/0038_email_realtime_secret_check.sql b/crates/manager-core/migrations/0038_email_realtime_secret_check.sql new file mode 100644 index 0000000..7d0096f --- /dev/null +++ b/crates/manager-core/migrations/0038_email_realtime_secret_check.sql @@ -0,0 +1,19 @@ +-- F-M-002 (audit 2026-06-07): coupled-nullness CHECK constraints on +-- (encrypted, nonce) pairs. +-- +-- The current schema has each column nullable independently: +-- email_trigger_details.inbound_secret_encrypted / _nonce +-- app_secrets.realtime_signing_key_encrypted / _nonce +-- +-- A bug or partial write could leave one populated and the other NULL +-- — silently bypassing signature verification (receiver decrypts only +-- if the encrypted column is set). Defend with a coupled-nullness +-- CHECK on each pair so a partial state is rejected at the DB +-- boundary. +ALTER TABLE email_trigger_details + ADD CONSTRAINT email_trigger_details_inbound_secret_pair + CHECK ((inbound_secret_encrypted IS NULL) = (inbound_secret_nonce IS NULL)); + +ALTER TABLE app_secrets + ADD CONSTRAINT app_secrets_realtime_signing_key_pair + CHECK ((realtime_signing_key_encrypted IS NULL) = (realtime_signing_key_nonce IS NULL));