Problem/Motivation
drush audit-chain:verify reports SEAL BROKEN permanently on any environment whose database was refreshed from another environment's dump, when the two environments hold different HMAC signing keys (the correct secret topology). Observed on a staging estate refreshed from production: the sealed prefix's stored row_hash values are byte-identical to production's (sampled and compared), production verifies clean, and the only failing component is the seal MAC — computed under the source environment's key, unverifiable under the copy's key by construction.
The failure class is the real problem: a permanent red "tampering of historical evidence" alarm on every refreshed environment trains operators to ignore the one alarm that must never be ignored. With scheduled verification (1.4.0) enabled on such an environment, it becomes a standing ERROR firing the failure event on every run.
Proposed resolution
When the seal digest over stored hashes matches but the MAC does not, report a distinct verdict (e.g. "SEAL FOREIGN — sealed under a key this environment does not hold") instead of the tampering message, so SEAL BROKEN keeps meaning tampering. Alternatives: document a post-refresh re-seal procedure, or a config flag marking an environment as a refresh target (derivative evidence).
Mirror: https://github.com/Wilkes-Liberty/audit_chain/issues/23
Comments
Comment #2
jmcerdaImplemented on the 1.x issue branch and submitted for review.
The fix keeps verification fail-closed while separating the two conditions:
- If the sealed-prefix digest no longer matches the stored row hashes, verification reports seal_broken / SEAL BROKEN and scheduled verification retains the error plus failure-event behavior.
- If the digest still matches but no current or retired key can authenticate the copied seal MAC, verification reports seal_foreign / SEAL FOREIGN. Drush still exits non-zero, scheduled state remains ok=false, and evidence export remains blocked; the status report and scheduled log use a warning, and no integrity-failure event is dispatched.
Seal MAC verification now rejects an unkeyed substitute. Seal creation likewise refuses unless the active signing key itself resolves, even when retired verification keys are present, and reuses that exact active key material for the MAC.
Kernel coverage exercises the production-to-staging refresh case, retired-key authentication, read-only verification, warning/event behavior, unchanged SEAL BROKEN behavior, unsigned-MAC rejection, and the retired-key-only creation edge case. The complete local suite passes: 55 tests and 321 assertions, plus Drupal/DrupalPractice coding standards and static analysis.
Comment #4
jmcerdaReleased in Audit Chain 1.5.1: https://www.drupal.org/project/audit_chain/releases/1.5.1
The release preserves fail-closed verification and export behavior while distinguishing foreign seals from actual sealed-prefix tampering. The release gate passed on Drupal 10.6, Drupal 11.3, and current Drupal 11, along with coding standards, static analysis, CodeQL, and attribution checks.