Skip to content

fix(core): consent expiry, onRevoke callback, domain cookie clearing - #97

Open
DaSchTour wants to merge 3 commits into
mainfrom
fix/consent-expiry-compliance
Open

fix(core): consent expiry, onRevoke callback, domain cookie clearing#97
DaSchTour wants to merge 3 commits into
mainfrom
fix/consent-expiry-compliance

Conversation

@DaSchTour

Copy link
Copy Markdown
Member

Summary

Three compliance-related bugfixes in the core consent engine:

1. Expired consent now behaves like no consent (critical). restore() restored choices and set hasConsented = true whenever the stored revision matched, ignoring isConsentExpired. Once consent aged past the re-prompt window (consentMaxAgeDays, default 180 days per CNIL guidance), the banner re-showed but isAccepted() still returned true — so ScriptBlocker kept tracking scripts unblocked before the user re-consented, defeating the legal purpose of the re-prompt. Expired consent now leaves choices empty and hasConsented false; the pseudonymous subjectId is still carried forward so re-consent stays linked to the same audit subject.

2. config.onRevoke is now invoked. It was declared in KeksmeisterConfig but never called anywhere. It now fires once per category that transitions from accepted to declined, both through applyChoices() (custom save, accept/reject-all) and revokeAll() (for every previously accepted, non-required category). It does not fire on first consent grant or when a category stays accepted.

3. Auto-clear can now delete domain-scoped cookies. clearCookies() only attempted deletion without a domain attribute (plus the configured cookieDomain), which fails for cookies like GA4's _ga that are scoped to domain=.example.com. It now also retries across the current hostname and its parent domains down to the 2-label registrable domain. setCookie() gained an optional domain override parameter so this reuses the existing cookie-string building instead of duplicating it.

Test plan

  • bun run test — 126 tests pass (new tests cover all three fixes)
  • bun run typecheck — clean
  • bun run build — succeeds

🤖 Generated with Claude Code

DaSchTour and others added 3 commits July 17, 2026 11:25
restore() previously restored choices and set _hasConsented whenever
the stored revision matched, ignoring isConsentExpired. That meant
the banner re-showed after the CNIL-guided re-prompt window, but
isAccepted() still returned true — letting ScriptBlocker activate
tracking scripts before the user re-consented, defeating the
re-prompt. Expired consent now leaves choices empty and hasConsented
false, while the pseudonymous subjectId is still carried forward for
audit continuity across re-consent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
KeksmeisterConfig.onRevoke was declared but never called, so
integrations couldn't react to a category being withdrawn (e.g. to
tear down a script/service). Now fired once per category that
transitions from accepted to declined via applyChoices() (custom
save, accept/reject-all updates), and once per previously-accepted,
non-required category in revokeAll(). Not fired on first consent
grant or when a category stays accepted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
clearCookies() only deleted cookies with no domain attribute (plus
the configured cookieDomain). Third-party scripts commonly scope
cookies to a domain attribute (e.g. GA4's `_ga` on `.example.com`),
which a bare deletion from `www.example.com` cannot remove. Now also
retries deletion across the current hostname and its parent domains
down to the 2-label registrable domain. setCookie() gained an
optional domain override so this reuses the existing cookie-string
logic instead of duplicating it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 1.09kB (1.49%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
keksmeister-Keksmeister-esm 39.49kB 595 bytes (1.53%) ⬆️
keksmeister-Keksmeister-umd 34.43kB 491 bytes (1.45%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: keksmeister-Keksmeister-umd

Assets Changed:

Asset Name Size Change Total Size Change (%)
keksmeister.umd.cjs 491 bytes 34.43kB 1.45%

Files in keksmeister.umd.cjs:

  • ./src/core/cookie-store.ts → Total Size: 2.41kB

  • ./src/core/consent-manager.ts → Total Size: 7.69kB

view changes for bundle: keksmeister-Keksmeister-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
keksmeister.js 595 bytes 39.49kB 1.53%

Files in keksmeister.js:

  • ./src/core/consent-manager.ts → Total Size: 7.48kB

  • ./src/core/cookie-store.ts → Total Size: 2.34kB

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant