@app4you/cookie-banner 0.0.0-stage → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,27 @@
1
+ import type { ServiceDefinition } from "./types.js";
2
+ export { resolveLocalizedConfig } from "./i18n/locale.js";
3
+ export { buildConsentCsp, consentCspHeaders, readCspConsent } from "./core/csp.js";
4
+ export type { ConsentCspOptions, CspSources } from "./core/csp.js";
5
+ import type { BannerConfig, ConsentReceipt } from "./types.js";
6
+ export { hasServiceConsent, setServiceConsent, onServiceConsent, } from "./core/service-consent.js";
7
+ export type { BannerConfig, LocalizedContent, ConsentState, ServiceDefinition, ConsentCleanup, } from "./types.js";
8
+ /** Safe in server components/SSR: never supplies a live src. */
9
+ export declare function blockedContentProps(service: string, src?: string): {
10
+ "data-cb-service": string;
11
+ "data-cb-src": string | undefined;
12
+ hidden: boolean;
13
+ };
14
+ /** Use a native <script>, not a framework's eager script loader. */
15
+ export declare function blockedScriptProps(service: string, src?: string): {
16
+ type: string;
17
+ "data-cb-type": string;
18
+ "data-cb-service": string;
19
+ "data-cb-src": string | undefined;
20
+ hidden: boolean;
21
+ };
22
+ /** Call in a client effect. Importing this entry on the server is safe. */
23
+ export declare function initializeConsent(config: BannerConfig): Promise<() => void>;
24
+ export declare const getConsentReceipt: () => ConsentReceipt | null;
25
+ export type { ConsentReceipt } from "./types.js";
26
+ /** Current observed services, using the same registry as the consent UI and blocker. */
27
+ export declare function getDetectedServices(): ServiceDefinition[];
package/dist/native.js ADDED
@@ -0,0 +1,15 @@
1
+ import { B as n, E as o, F as t, G as a, H as i, I as r, J as C, K as c, L as p, M as d, y as l, s as v } from "./native/native-B9FXlXDp.js";
2
+ export {
3
+ n as blockedContentProps,
4
+ o as blockedScriptProps,
5
+ t as buildConsentCsp,
6
+ a as consentCspHeaders,
7
+ i as getConsentReceipt,
8
+ r as getDetectedServices,
9
+ C as hasServiceConsent,
10
+ c as initializeConsent,
11
+ p as onServiceConsent,
12
+ d as readCspConsent,
13
+ l as resolveLocalizedConfig,
14
+ v as setServiceConsent
15
+ };
@@ -0,0 +1,145 @@
1
+ export type ConsentCategory = "essential" | "analytics" | "marketing";
2
+ export type ConsentState = Record<ConsentCategory, boolean> & {
3
+ services?: Record<string, boolean>;
4
+ };
5
+ export type BannerMode = "gtm" | "standalone";
6
+ export type BannerVariant = "modal" | "bar";
7
+ export type BannerView = "overlay" | "preferences" | "idle";
8
+ export interface ScriptDef {
9
+ /** Optional scripts require both the category and this provider choice. */
10
+ service: string;
11
+ src?: string;
12
+ inline?: string;
13
+ async?: boolean;
14
+ }
15
+ export interface ThemeConfig {
16
+ primary?: string;
17
+ /** Hover shade for primary buttons; derived from `primary` when omitted. */
18
+ primaryHover?: string;
19
+ background?: string;
20
+ text?: string;
21
+ }
22
+ export interface ConsentCleanup {
23
+ /** Exact first-party cookie names; never use wildcard deletion. */
24
+ cookies?: string[];
25
+ localStorage?: string[];
26
+ sessionStorage?: string[];
27
+ }
28
+ export interface ServiceDefinition {
29
+ id: string;
30
+ name: string;
31
+ description: string;
32
+ privacyUrl?: string;
33
+ /** Reviewed retention period or criteria for this integration. */
34
+ retention?: string;
35
+ cleanup?: ConsentCleanup;
36
+ /** Absolute URL prefixes; matched on hostname and path, not arbitrary substrings. */
37
+ urls?: string[];
38
+ }
39
+ export interface ConsentReceipt {
40
+ id: string;
41
+ version: number;
42
+ at: number;
43
+ services: Record<string, boolean>;
44
+ categories: Record<ConsentCategory, boolean>;
45
+ noticeKey: string;
46
+ notice: {
47
+ lang?: string;
48
+ content?: Record<string, string>;
49
+ overlay: Record<string, string>;
50
+ preferences: Record<string, string | Record<ConsentCategory, {
51
+ label: string;
52
+ description: string;
53
+ }>>;
54
+ services: ServiceDefinition[];
55
+ };
56
+ }
57
+ export interface LocalizedContent {
58
+ controller?: {
59
+ name: string;
60
+ contactUrl?: string;
61
+ };
62
+ purposes?: string;
63
+ privacyPolicyUrl?: string;
64
+ cookiePolicyUrl?: string;
65
+ customTexts?: Record<string, unknown>;
66
+ serviceDisclosures?: Record<string, {
67
+ name?: string;
68
+ description?: string;
69
+ privacyUrl?: string;
70
+ retention?: string;
71
+ }>;
72
+ }
73
+ export interface BannerConfig {
74
+ /** Defaults to standard in npm. WordPress defaults to equal. Never changes blocking or consent requirements. */
75
+ buttonStyle?: "standard" | "equal";
76
+ /** Per-page locale content: base language, then exact regional override. */
77
+ localizations?: Record<string, LocalizedContent>;
78
+ /** Internal isolated admin preview: memory storage, no evidence submission. */
79
+ preview?: boolean;
80
+ /** Legal identity of the website controller, not the software vendor. */
81
+ controller?: {
82
+ name: string;
83
+ contactUrl?: string;
84
+ };
85
+ /** Required; bump whenever policy contents, purposes or recipients change. */
86
+ noticeVersion?: string;
87
+ /** Reviewed, site-specific explanation of the requested optional processing. */
88
+ purposes?: string;
89
+ /** HTTP CSP supplied by the host; consent changes require a fresh document. */
90
+ strictCsp?: boolean;
91
+ services?: ServiceDefinition[];
92
+ /** Disclosure overrides for catalog providers; no duplicate blocking rules. */
93
+ serviceDisclosures?: Record<string, {
94
+ name?: string;
95
+ description?: string;
96
+ privacyUrl?: string;
97
+ retention?: string;
98
+ cleanup?: ConsentCleanup;
99
+ }>;
100
+ categoryCleanup?: Partial<Record<"analytics" | "marketing", ConsentCleanup>>;
101
+ /** Same-origin endpoint receiving minimal, versioned receipts (optional for native hosts). */
102
+ receiptEndpoint?: string;
103
+ receiptNonce?: string;
104
+ /** Early best-effort interception for third-party code. Enabled by the WP plugin. */
105
+ autoBlock?: boolean;
106
+ auditMode?: boolean;
107
+ mode: BannerMode;
108
+ variant?: BannerVariant;
109
+ gtmId?: string;
110
+ loadGtm?: boolean;
111
+ /** @deprecated Ignored: consent always requires a visitor action. */
112
+ testMode?: boolean;
113
+ /** @deprecated Ignored: consent always requires a visitor action. */
114
+ autoAcceptAll?: boolean;
115
+ lang: string;
116
+ privacyPolicyUrl?: string;
117
+ cookiePolicyUrl?: string;
118
+ theme?: ThemeConfig;
119
+ /** @deprecated Custom links always work; the floating withdrawal controls remain visible. */
120
+ useCustomSettingsLink?: boolean;
121
+ customTexts?: Record<string, unknown>;
122
+ detectedServices?: string[];
123
+ scripts?: {
124
+ analytics?: ScriptDef[];
125
+ marketing?: ScriptDef[];
126
+ };
127
+ }
128
+ export declare const STORAGE_PREFIX = "cb_consent_";
129
+ export declare const STORAGE_HAS_CONSENTED = "cb_has_consented";
130
+ export declare const STORAGE_COMMIT = "cb_consent_commit";
131
+ export declare const CONSENT_CATEGORIES: ConsentCategory[];
132
+ export declare const DEFAULT_CONSENT_STATE: ConsentState;
133
+ export declare const GTM_CONSENT_KEYS: readonly ["ad_storage", "analytics_storage", "ad_user_data", "ad_personalization", "functionality_storage", "security_storage", "personalization_storage"];
134
+ export type GTMConsentKey = (typeof GTM_CONSENT_KEYS)[number];
135
+ export type GTMConsentPayload = Record<GTMConsentKey, "granted" | "denied">;
136
+ export declare const DEFAULT_GTM_CONSENT_DENIED: GTMConsentPayload;
137
+ export declare const GTM_CONSENT_MAP: Record<ConsentCategory, readonly GTMConsentKey[]>;
138
+ declare global {
139
+ interface Window {
140
+ __cookieConsent?: BannerConfig;
141
+ dataLayer?: any[];
142
+ gtag?: (...args: unknown[]) => void;
143
+ fbq?: (...args: unknown[]) => void;
144
+ }
145
+ }
@@ -0,0 +1,142 @@
1
+ # Konfiguracja zgód i informacji o prywatności
2
+
3
+ Baner nie uruchamia opcjonalnego przetwarzania,
4
+ jeżeli brakuje danych administratora, celu, wersji informacji lub poprawnego linku
5
+ do polityki prywatności. Stare rekordy zgody są unieważniane. Odmowa działa także
6
+ przy niedostępnym localStorage; opcjonalne zgody wymagają skutecznego zapisu.
7
+
8
+ To mechanizmy wspierające zgodne wdrożenie. Nie są certyfikatem zgodności całej
9
+ witryny ani oceną prawdziwości danych wpisanych przez jej administratora.
10
+
11
+ ## WordPress
12
+
13
+ 1. W „Banner & texts” wpisz rzeczywistą nazwę administratora, konkretne cele
14
+ opcjonalnego przetwarzania, URL opublikowanej polityki i jej wersję. Zmiana
15
+ celów, odbiorców lub polityki pod zewnętrznym URL wymaga zmiany wersji.
16
+ Zmiana treści lokalnej strony polityki oraz buildu bannera jest wykrywana automatycznie.
17
+ 2. Uruchom istniejący skaner. W „Services” sprawdź informacje o wykrytych
18
+ dostawcach: rzeczywisty cel na tej stronie, politykę dostawcy oraz okres
19
+ przechowywania danych lub kryteria jego określenia. Sugestie korzystają
20
+ z tej samej bazy i identyfikatorów co skaner i blokowanie, bez drugiej tabeli
21
+ cookies. Opisy bazy są sugestiami, nie potwierdzeniem konkretnego wdrożenia.
22
+ 3. Nierozpoznane lub nieopisane usługi pozostają zablokowane. Zarejestruj
23
+ własną integrację z jej URL-ami i pełnymi informacjami, zamiast zgadywać cel
24
+ nieznanej domeny. Zmiana integracji wymaga nowego identyfikatora usługi.
25
+ 4. Link `.open-consent-settings` i shortcode `[cookie_preferences_link]`
26
+ nadal działają. Kontrolka ustawień pozostaje widoczna; przy aktywnych zgodach
27
+ jest też bezpośredni przycisk wycofania wszystkich opcjonalnych zgód.
28
+ 5. Zachowaj blokowanie CSP, wyczyść stare cache HTML i sprawdź CDN/reverse proxy.
29
+ HTML zależny od zgód nie może być współdzielony między odwiedzającymi.
30
+
31
+ Samo zaznaczenie kategorii nie zastępuje zgody na odrębnie pokazanego dostawcę.
32
+ Nie należy oznaczać wszystkich „funkcjonalnych” cookies jako ściśle niezbędnych.
33
+ Tabela cookies domyślnie pokazuje także nierozpoznane pozycje.
34
+
35
+ ## npm / Next.js
36
+
37
+ Serwer i klient muszą używać tej samej `noticeVersion`. Uaktualniaj ją przy
38
+ zmianach informacji, celów, odbiorców lub polityki. Bez wersji serwerowy helper
39
+ pozostawia opcjonalne domeny zablokowane. Sam import JS nie zastąpi nagłówka HTTP
40
+ CSP i nieaktywnych atrybutów SSR.
41
+
42
+ ```ts
43
+ const noticeVersion = '2026-11-06.1';
44
+ const config = {
45
+ mode: 'standalone', lang: 'pl', strictCsp: true, noticeVersion,
46
+ controller: {name: 'RZECZYWISTA NAZWA ADMINISTRATORA'},
47
+ purposes: 'Konkretne cele opcjonalnego przetwarzania na tej stronie.',
48
+ privacyPolicyUrl: '/polityka-prywatnosci',
49
+ services: [{
50
+ id: 'video', name: 'Dostawca filmu',
51
+ description: 'Konkretny cel i zakres integracji, w tym przekazywane dane.',
52
+ privacyUrl: 'https://video.example/privacy',
53
+ retention: 'Rzeczywisty okres lub kryteria usunięcia danych — po weryfikacji.',
54
+ urls: ['https://video.example/'],
55
+ cleanup: {cookies: ['video_session'], localStorage: ['video-id']},
56
+ }],
57
+ };
58
+ // Te dane są wzorem konfiguracji, nie gotową polityką konkretnej witryny.
59
+ const headers = consentCspHeaders({noticeVersion, services: {
60
+ video: {'frame-src': ['https://video.example']},
61
+ }}, request.headers.get('cookie') || '');
62
+ ```
63
+
64
+ Dla usług z katalogu można ustawić `serviceDisclosures[id]` z polami
65
+ `description`, `privacyUrl`, `retention` i opcjonalnym `cleanup`, bez powielania
66
+ reguł rozpoznawania URL. Dotychczasowe `scripts.analytics` / `scripts.marketing`
67
+ wymagają teraz `service` w każdym skrypcie; wykonanie wymaga zarówno zgody na
68
+ kategorię, jak i daną usługę. API `setServiceConsent` wywołuj w odpowiedzi na
69
+ świadomy wybór użytkownika, a nie podczas automatycznego startu aplikacji.
70
+
71
+ ## Zapis decyzji i dowody
72
+
73
+ Przeglądarka zachowuje jeden aktualny rekord: losowy identyfikator decyzji, czas,
74
+ kategorie, wybory dostawców, wersję i kopię pokazanych informacji. `getConsentReceipt()`
75
+ i zdarzenie `cb:consent-receipt` udostępniają go integratorowi. Rekord może służyć
76
+ powiązaniu decyzji z procesami własnego backendu; nie należy wysyłać dodatkowych
77
+ identyfikatorów dostawcom bez potrzeby i odpowiedniej informacji.
78
+
79
+ WordPress zapisuje minimalną kopię decyzji i wersjonowane informacje po stronie
80
+ serwera, z czasem serwera i hashem runtime. Nie zapisuje w tym rekordzie IP,
81
+ user-agenta ani trwałego identyfikatora odwiedzającego. Powtórzenie ID nie zmienia
82
+ istniejącego dowodu. Krótkotrwały, solony skrót adresu służy osobno do ograniczania
83
+ nadużyć endpointu i wygasa po minucie. Zapis nie jest podpisem użytkownika ani
84
+ potwierdzeniem jego tożsamości.
85
+
86
+ - `GET /wp-json/cookie4/v1/consent-receipts?offset=0`: eksport dla administratora,
87
+ 100 pozycji na stronę, `nextOffset` wskazuje kolejną partię; wymaga zwykłego
88
+ uwierzytelnienia WordPress REST. Panel Diagnostics eksportuje ostatnie 100.
89
+ - `POST` tego endpointu przyjmuje aktualną wersję i ściśle sprawdzone wybory;
90
+ wymaga nonce. Brak uprawnienia nie daje dostępu do eksportu.
91
+ - Hook `cookie4_consent_recorded` pozwala administratorowi integrować własną
92
+ retencję lub archiwum. `cb:receipt-error` sygnalizuje klientowi błąd wysyłki;
93
+ lokalny pełny rekord pozostaje dostępny dla integratora.
94
+ - Domyślna ważność wyboru i retencja dowodów wynoszą 180 dni. To ustawienie
95
+ produktu, nie uniwersalny termin ustawowy. Backendowy filtr
96
+ `cookie4_receipt_retention_days` pozwala ustalić 1–365 dni. Zasady retencji
97
+ muszą być opisane przez administratora. Usuwanie wykonuje też dzienny WP-Cron;
98
+ przy wyłączonym WP-Cron trzeba zapewnić jego uruchamianie na serwerze.
99
+
100
+ ## Wycofanie i ograniczenia techniczne
101
+
102
+ Wycofanie natychmiast zaostrza CSP bieżącego dokumentu przed wywołaniem SDK
103
+ i przeładowaniem, żeby zatrzymać także pingi consent-update i pagehide. Następnie
104
+ aktualizuje decyzję, wyłącza elementy korzystające z subskrypcji,
105
+ wysyła sygnały cofnięcia do używanego mechanizmu GTM/Meta i przeładowuje dokument
106
+ z odmawiającym CSP. Zmiana w innej karcie również odświeża politykę. Stary grant
107
+ serwerowy jest usuwany przed zmianami localStorage, aby inna karta nie odświeżyła
108
+ się ze starym zezwoleniem. Przywrócenie strony z bfcache odświeża ścisłe CSP. Zmiana wersji informacji
109
+ podczas ponownej inicjalizacji nawigacji klientowej również wymusza nowy dokument.
110
+
111
+ Usuwane są tylko jawnie wskazane identyfikatory first-party. WordPress korzysta
112
+ z nazw cookies sklasyfikowanych przez istniejący skaner jako analityczne lub
113
+ marketingowe; należy zweryfikować tę klasyfikację. npm ma `cleanup` per dostawca
114
+ oraz `categoryCleanup`. Nie usuwamy koszyka ani sesji strony „na wszelki wypadek”.
115
+ HttpOnly i dane u dostawcy wymagają odpowiedniej obsługi serwera/dostawcy;
116
+ przeglądarkowy JS nie ma uprawnień do ich dowolnego kasowania. Żądań już
117
+ rozpoczętych przed wycofaniem nie da się cofnąć; aktywność obcego dokumentu iframe
118
+ pozostaje ograniczeniem opisanym poniżej.
119
+
120
+ Polityka CSP kontroluje żądania dokumentu strony, nie całą wewnętrzną aktywność
121
+ zdalnego dokumentu iframe. Zgoda na osadzenie musi uwzględniać przetwarzanie tego
122
+ dostawcy. Lokalne iframe i workery są domyślnie zablokowane, ponieważ ich własne dokumenty
123
+ nie muszą dziedziczyć reguł sieciowych rodzica. Jawnie dopuszczone niezbędne
124
+ workery lub ramki wymagają właściwych nagłówków także na ich odpowiedziach.
125
+ Tracking first-party, proxy, backend, konfiguracja GTM, transfery poza
126
+ EOG, rzeczywiste cele i pełny obowiązek informacyjny wymagają oceny konkretnej
127
+ witryny. Kolory HEX z panelu dostają korektę kontrastu; zaawansowane kolory CSS
128
+ podane w integracji własnej trzeba zweryfikować w jej rzeczywistym otoczeniu.
129
+
130
+ Nie da się rzetelnie zamienić tych testów w gwarancję „100% zgodności każdej
131
+ strony z RODO”. Podstawy: [PKE art. 399–400](https://eli.gov.pl/api/acts/DU/2024/1221/text.html),
132
+ [RODO art. 4, 7, 12–13, 25 i rozdział V](https://eur-lex.europa.eu/legal-content/PL/TXT/?uri=CELEX:32016R0679),
133
+ [raport EROD o bannerach](https://www.edpb.europa.eu/system/files/2023-01/edpb_20230118_report_cookie_banner_taskforce_en.pdf).
134
+
135
+ ### Styl przycisków
136
+
137
+ WordPress: **Banner & texts → Consent button style**. Standardowy oznacza
138
+ wypełnioną akceptację i obrysowaną odmowę; ścisły oznacza identyczne przyciski
139
+ decyzji. Zapis preferencji ma neutralny styl. Ustawienie zmienia tylko wygląd,
140
+ nie blokowanie, CSP ani wymagania zgody. Nazwy trybów nie stanowią oceny prawnej.
141
+ W npm domyślną wartością `buttonStyle` jest `standard`. W WordPressie domyślną
142
+ wartością jest `equal`.
@@ -0,0 +1,262 @@
1
+ # Integracja zgód: npm i WordPress
2
+
3
+ Pakiet i wtyczka korzystają z jednego modelu zgód oraz rejestru rozpoznawania usług. Wyniki skanowania pozostają w istniejącym module `plugin/src/features/scanner` i dotychczasowych opcjach WordPressa. Nie ma drugiej tabeli cookies.
4
+
5
+ [Wymagane informacje i konfiguracja zgód](consent-compliance.md).
6
+
7
+ ## Npm: kod własnej strony / Next.js
8
+
9
+ Punkt wejścia: `@app4you/cookie-banner/native`. Sam import i funkcje generujące atrybuty są bezpieczne w SSR. Klient pobiera kod bannera dopiero podczas inicjalizacji. Dotychczasowy bundle IIFE pozostaje w `@app4you/cookie-banner` i `@app4you/cookie-banner/auto`.
10
+
11
+ W komponencie serwerowym można od razu wygenerować nieaktywny iframe:
12
+
13
+ ```tsx
14
+ import { blockedContentProps } from "@app4you/cookie-banner/native";
15
+
16
+ export function Video() {
17
+ return <iframe
18
+ {...blockedContentProps("youtube", "https://www.youtube-nocookie.com/embed/VIDEO_ID")}
19
+ title="Film o naszej firmie"
20
+ width={800}
21
+ height={450}
22
+ allowFullScreen
23
+ />;
24
+ }
25
+ ```
26
+
27
+ W HTML nie ma `src`, tylko `data-cb-src`. Brak JS nie uruchomi dostawcy. Identyczne właściwości działają z Reactem i Preactem. Przykład w tym repo faktycznie korzysta z API: `src/demo.tsx`.
28
+
29
+ Inicjalizacja po hydratacji, np. w jednym komponencie klientowym Next.js:
30
+
31
+ ```tsx
32
+ "use client";
33
+ import { useEffect } from "react";
34
+ import { initializeConsent } from "@app4you/cookie-banner/native";
35
+
36
+ export function ConsentProvider() {
37
+ useEffect(() => {
38
+ let cancelled = false;
39
+ let dispose: (() => void) | undefined;
40
+ initializeConsent({
41
+ mode: "standalone",
42
+ lang: "pl",
43
+ variant: "bar",
44
+ noticeVersion: "2026-11-06.1",
45
+ controller: {name: "RZECZYWISTA NAZWA ADMINISTRATORA"},
46
+ purposes: "Odtwarzanie opcjonalnych filmów przez zewnętrznego dostawcę.",
47
+ privacyPolicyUrl: "/polityka-prywatnosci",
48
+ services: [{
49
+ id: "youtube",
50
+ name: "YouTube",
51
+ description: "Odtwarzanie osadzonych filmów. Po zgodzie Google otrzyma adres IP i dane przeglądarki.",
52
+ privacyUrl: "https://policies.google.com/privacy",
53
+ retention: "Wpisz zweryfikowany okres lub kryteria usunięcia danych dla tego wdrożenia.",
54
+ }],
55
+ }).then(cleanup => {
56
+ if (cancelled) cleanup();
57
+ else dispose = cleanup;
58
+ });
59
+ return () => { cancelled = true; dispose?.(); };
60
+ }, []);
61
+ return null;
62
+ }
63
+ ```
64
+
65
+ Dla zwykłego skryptu użyj `blockedScriptProps("service-id", "https://provider.example/sdk.js")`. Skrypt inline również musi dostać te atrybuty i `type="text/plain"`. Nie przekazuj aktywnego `src` do `next/script`, preloadu ani frameworkowego komponentu, który sam rozpoczyna połączenie. Identyfikator usługi musi być taki sam w konfiguracji i atrybutach.
66
+
67
+ Dla komponentu wykonującego żądania podczas montowania zastosuj `onServiceConsent` **przed jego zamontowaniem**:
68
+
69
+ ```ts
70
+ import { onServiceConsent } from "@app4you/cookie-banner/native";
71
+
72
+ const unsubscribe = onServiceConsent("support-chat", granted => {
73
+ if (granted) mountChat();
74
+ else unmountChat();
75
+ });
76
+ // Przy odmontowaniu właściciela: unsubscribe().
77
+ ```
78
+
79
+ `mountChat()` powinno zawierać również import/uruchomienie SDK — nie importuj wcześniej modułu mającego efekty uboczne. Zarejestruj własną usługę w `services` z jej rzeczywistym celem i polityką. `urls` pozwala przypisać dokładne prefiksy URL (porównywane po origin i ścieżce), także własnych skryptów. Domyślnie natywna integracja nie zmienia prototypów przeglądarki; `autoBlock: true` włącza dodatkowy guard dla zewnętrznego, niekontrolowanego kodu.
80
+
81
+ Dostępne są też `hasServiceConsent(id)` i `setServiceConsent(id, boolean)`. Wycofanie zgody przeładowuje dokument, ponieważ usunięcie znacznika script nie usuwa uruchomionych timerów i listenerów SDK. Zgody usług są niezależne od kategorii analityka/marketing.
82
+
83
+ ## WordPress
84
+
85
+ Warstwy działania:
86
+
87
+ 1. `script_loader_tag`, `wp_inline_script_attributes`, `render_block`, `embed_oembed_html` przekształcają rozpoznane zasoby i kod powiązany z enqueue.
88
+ 2. Bufor całej odpowiedzi PHP obejmuje również surowe `echo`, iframe i rozpoznane inline loadery. Parser WP neutralizuje `src`, alternatywne adresy lazy load, srcset i odpowiednie wskazówki połączeń. Skrypty są `text/plain`.
89
+ 3. Synchronous bootstrap jest przenoszony na początek gotowego head, przed kod wydrukowany przed `wp_head`. Ma atrybuty wyłączenia optymalizacji dla popularnych loaderów.
90
+ 4. Guard przechwytuje natywne właściwości URL, `setAttribute`, wstawianie węzłów i typowe operacje HTML przed wywołaniem implementacji przeglądarki. Dodatkowo zatrzymuje rozpoznane opcjonalne żądania `fetch`, XHR i `sendBeacon`. Nieznane iframe są grupowane osobno według dostawcy/domeny i pozostają nieaktywne do uzupełnienia informacji; celu nieznanych skryptów nie zgadujemy.
91
+ 5. Runtime pokazuje lokalny boks bez zewnętrznej miniatury, aktywuje wyłącznie wybraną usługę i zachowuje kolejność odblokowywanych skryptów. Piksele śledzące i wskazówki połączeń nie dostają dużych widocznych boksów.
92
+ 6. Istniejący skaner rozróżnia zablokowany element od requestu zaobserwowanego w Performance API. Audyt administratora wymusza odmowę bez nadpisywania jego zapamiętanych wyborów.
93
+
94
+ Własny kod WordPressa może emitować:
95
+
96
+ ```html
97
+ <iframe hidden data-cb-service="youtube"
98
+ data-cb-src="https://www.youtube.com/embed/VIDEO_ID" title="Film"></iframe>
99
+ <script type="text/plain" data-cb-service="custom-analytics"
100
+ data-cb-src="https://metrics.example/sdk.js"></script>
101
+ ```
102
+
103
+ Filtr `cookie4_config` pozwala dodać `services` (ten sam format co npm) i ustawić `autoBlock: false` w razie konfliktu konkretnej witryny. To wyłącza dodatkowy guard; transformacja PHP pozostaje. Własne usługi oznaczone w HTML pozostają blokowane bez zgody niezależnie od obecności w bazie.
104
+
105
+ Boksy dziedziczą rodzinę fontów i kolor tekstu strony, z kompaktowymi rozmiarami tekstu i przycisków. Tło oraz obramowanie są delikatnymi odcieniami bieżącego koloru, a YouTube ma lokalną ikonę odtwarzania bez pobierania miniatury. Strona może nadpisać zmienne CSS: `--cb-content-background`, `--cb-content-color`, `--cb-content-border`, `--cb-content-radius`, `--cb-content-primary`, `--cb-content-button-text` oraz klasę `.cb-content-placeholder`.
106
+
107
+ ## Bazy i aktualizacje
108
+
109
+ - `plugin/includes/cookie-database.json`: istniejąca Open Cookie Database, zaktualizowana w miejscu — 2280 wpisów.
110
+ - `plugin/includes/service-database.json`: dane Third Party Web — 2141 dostawców, 3132 wzorce domen.
111
+ - `tracker-database.json`: niewielka warstwa dokładniejszych reguł produktów i metadanych (np. odróżnienie GA od kontenera GTM).
112
+ - `database-sources.json`: wersje źródłowe; `bun run update:databases` odtwarza import. Aktualizacja rewizji jest świadomą zmianą kodu, bez pobierania bazy podczas wizyty użytkownika. Import nie wykonuje pobranego JS.
113
+ - Licencje i atrybucja są w `plugin/NOTICE` oraz `plugin/licenses/`.
114
+
115
+ Preferencje pokazują usługi skonfigurowane lub zaobserwowane na witrynie, a nie całą bazę. Trzeba zweryfikować rzeczywisty cel przetwarzania, administratorów i polityki nieznanych usług. Kategorie z bazy są wskazówką, a nie oceną prawną konkretnej konfiguracji.
116
+
117
+ ## Ograniczenia integracji
118
+
119
+ Nie deklarujemy automatycznej pełnej zgodności całej witryny z RODO. Baza nie zna wszystkich celów; guard nie stanowi kompletnego firewalla przeglądarki. Nie obejmuje każdej ścieżki, np. Workerów, obcych realmów, wcześniej przechwyconych API, dowolnego `document.write`, zasobów CSS, Early Hints i serwerowego trackingu. Nie usuwa cudzych cookies HttpOnly ani cookies na domenie dostawcy. Te przypadki wymagają integracji u źródła i audytu docelowej strony.
120
+
121
+ `testMode` i `autoAcceptAll` nie udzielają już zgód. Dotychczasowe zgody bez aktualnego rekordu wersji są uznawane za nieważne; nowy zapis ma wersję, czas i okres ważności 180 dni (wybór produktu, nie uniwersalny termin ustawowy). Google działa w Basic Consent Mode: GTM wymaga aktywnej zgody na usługę oraz cel, a przy odmowie nie jest ładowany. Tagi wewnątrz kontenera muszą nadal mieć poprawnie skonfigurowane wymagania zgód.
122
+
123
+
124
+ ## Strict network CSP (server integration)
125
+
126
+ `@app4you/cookie-banner/native` exports server-safe `consentCspHeaders`,
127
+ `buildConsentCsp` and `readCspConsent`. This is an HTTP policy, not a policy
128
+ installed by a client effect. Apply it to the initial HTML response:
129
+
130
+ ```ts
131
+ import { consentCspHeaders } from '@app4you/cookie-banner/native';
132
+
133
+ const csp = {
134
+ noticeVersion: "2026-11-06.1", // Same value in the client config.
135
+ services: {
136
+ youtube: {
137
+ 'script-src': ['https://www.youtube.com', 'https://s.ytimg.com'],
138
+ 'frame-src': ['https://www.youtube.com', 'https://www.youtube-nocookie.com'],
139
+ },
140
+ analytics: {
141
+ 'script-src': ['https://analytics.example.com'],
142
+ 'connect-src': ['https://analytics.example.com'],
143
+ },
144
+ },
145
+ };
146
+ const headers = consentCspHeaders(csp, request.headers.get('cookie') || '');
147
+ // Set every returned header on the HTML response. Preserve other Vary tokens.
148
+ // Initialize the client banner with strictCsp: true and the same service IDs.
149
+ ```
150
+
151
+ For Next.js, compute these headers in middleware/proxy for document routes;
152
+ make the protected route/layout dynamic (`export const dynamic = 'force-dynamic'`)
153
+ and bypass CDN/full-page caching. Remove external HTTP Link/103 Early Hints and
154
+ HTML preconnect/prefetch hints; the helper disables DNS prefetch but cannot rewrite
155
+ HTML or a proxy configuration. A static export cannot emit a different HTTP
156
+ policy per visitor. Keep `blockedContentProps` / `blockedScriptProps` on optional
157
+ resources, including server-rendered markup. `initializeConsent` belongs in a
158
+ client effect; constructing response headers belongs on the server. The included
159
+
160
+ The cookie `cb_csp_consent` contains only explicitly granted service IDs, expires
161
+ in 180 days, and uses Path=/, SameSite=Lax and Secure on HTTPS. The server ignores
162
+ unknown IDs; the cookie cannot add arbitrary origins. Every consent change reloads
163
+ the document before the client dispatches integration activation. Cross-tab storage
164
+ changes reload strict-CSP documents too. Serve one consent scope per host/path-root;
165
+ applications sharing an origin must use a common service registry.
166
+
167
+ Default policy: `default-src 'none'`, self-hosted resources, inline scripts/styles,
168
+ data images/fonts; no external sources, objects or external form submissions.
169
+ Service allowlists accept HTTPS origins only, optionally with a leading wildcard.
170
+ `essential` allows explicitly reviewed necessary destinations. `allowEval: true`
171
+ is an explicit optimizer compatibility escape hatch, not enabled by default.
172
+ Inline scripts remain allowed for framework/WordPress compatibility: this is a
173
+ network privacy policy, not a nonce-based XSS protection policy.
174
+
175
+ ## WordPress CSP and caches
176
+
177
+ Strict CSP is enabled by default on frontend HTML, both as a response header and
178
+ as the first head meta policy (the header enforces additional directives such as
179
+ frame-ancestors). Registered service domains reuse the existing scanner registry;
180
+ unknown destinations stay blocked. Unknown HTTPS iframe origins are registered
181
+ for frame-src only from the actual HTML of the current response; consent cookies
182
+ cannot invent a destination. Customize `cookie4_csp_services` with a map of
183
+ service IDs to directive/origin arrays; use `cookie4_csp_essential` for necessary
184
+ origins. `cookie4_strict_csp` controls the whole mode. `cookie4_csp_allow_eval` can
185
+ force-disable the eval compatibility required by WP Rocket Delay JS, at the cost
186
+ of disabling that loader's behavior. Eval is otherwise allowed only while that
187
+ active optimizer has Delay JS enabled. LiteSpeed Delay JS instead needs local
188
+ `blob:` scripts; `cookie4_csp_allow_blob_scripts` can override that compatibility
189
+ exception. Neither exception adds an external network destination.
190
+
191
+ The plugin sets private/no-store HTML, Vary: Cookie, DONOTCACHEPAGE and LiteSpeed
192
+ no-cache controls, prevents LiteSpeed page-cache storage and WP Rocket HTML cache writes.
193
+ It purges both optimizers on deployment/activation and regenerates Rocket config
194
+ and Apache rules. Rocket's DONOTCACHEPAGE override is installed only when this
195
+ plugin defined that constant itself; cache writes remain disabled independently.
196
+ Local JS/CSS minification, combining and delay remain testable and active.
197
+ External HTML resource hints are removed, HTTP Link hints removed, and DNS prefetch
198
+ is disabled. Assets themselves remain cacheable.
199
+
200
+ Deploy through plugin activation/admin or explicitly run
201
+ `wp eval 'cookie4_csp_refresh_cache();'` **before reopening public traffic**. An
202
+ already cached HTML response can be served before WordPress loads the updated
203
+ plugin. Purge and bypass HTML at any additional CDN/reverse proxy/server cache;
204
+ PHP cannot reconfigure an unidentified upstream system or remove 103 Early Hints
205
+ it emits. Do not cache granted HTML variants, and do not let a CDN override no-store.
206
+ The meta policy is defense in depth, not permission to ignore these requirements.
207
+
208
+ CSP does not stop first-party tracking/proxy endpoints, server-to-server requests,
209
+ ordinary user navigation, or control requests inside an allowed cross-origin
210
+ iframe. YouTube's nested third-party dependencies become possible after YouTube
211
+ consent. Review the chosen integration's whole purpose and vendor dependencies.
212
+ This implementation does not itself establish legal compliance for a website.
213
+
214
+ References: [MDN CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP),
215
+ [WP Rocket caching](https://docs.wp-rocket.me/article/1528-page-caching).
216
+
217
+
218
+ ## WordPress administrator workspace
219
+
220
+ The WordPress panel now has Overview, Banner & texts, Services, Cookies & scan,
221
+ WordPress integration and Diagnostics sections. Banner and service drafts remain
222
+ mounted when switching sections; the banner save indicator preserves edits made
223
+ while an earlier save is in flight.
224
+
225
+ Services can be registered through the panel with an ID, name, purpose, HTTPS URL
226
+ prefixes and optional privacy policy URL. The same saved definitions feed scanner
227
+ detection, HTML blocking, frontend service preferences and CSP. Built-in IDs cannot
228
+ be overridden. To change an integration's purpose/destinations, create a new ID:
229
+ the server retains a hash of prior definitions so deleting/recreating a service
230
+ cannot reuse earlier consent for something different.
231
+
232
+ Diagnostics exposes local prerequisites, detected cache adapters and known other
233
+ active consent plugins. Its explicit homepage check fetches HTML without executing
234
+ it and reports actual response headers for the administrator session; this does
235
+ not replace anonymous full-site request testing. Native WordPress Site Health also
236
+ shows these prerequisites and support information.
237
+
238
+ The live banner preview uses a sandbox that cannot read/write the site's actual
239
+ consent storage. It loads locally bundled assets; preview choices and changing
240
+ appearance do not reset a real visitor's consent.
241
+
242
+ ### Te same funkcje poza WordPressem
243
+
244
+ Rdzeń npm i wtyczka używają tych samych komponentów i mechanizmów zgody.
245
+ W npm domyślny `buttonStyle: 'standard'` daje wypełnioną akceptację i obrysowaną
246
+ odmowę. WordPress domyślnie wybiera `'equal'`, czyli jednakowy wygląd obu
247
+ przycisków. Ustawienie dotyczy wyglądu przycisków.
248
+
249
+ W konfiguracji npm dostępne są również `localizations`, `customTexts`,
250
+ `serviceDisclosures`, `categoryCleanup`, `autoBlock: true`, `strictCsp: true`,
251
+ `receiptEndpoint` i `receiptNonce`. CSP wymaga nagłówka z serwera aplikacji;
252
+ przykład Next.js/SSR znajduje się w `docs/multilingual.md`.
253
+
254
+ Z `@app4you/cookie-banner/native` można odczytać `getDetectedServices()` (zaobserwowane
255
+ usługi z tego samego rejestru), `getConsentReceipt()` i korzystać z
256
+ `hasServiceConsent`, `setServiceConsent`, `onServiceConsent` oraz nieaktywnych
257
+ atrybutów `blockedContentProps` / `blockedScriptProps`.
258
+
259
+ Panel, zapis w tabeli bazy danych WordPressa, WPML/Polylang i czyszczenie cache
260
+ wtyczek są adapterami WordPressa. W innej aplikacji jej backend odbiera dowody przez
261
+ `receiptEndpoint`, a ustawienia przekazuje się jako konfigurację npm. Sam pakiet
262
+ przeglądarkowy nie tworzy bazy danych ani panelu administracyjnego hosta.
@@ -0,0 +1,28 @@
1
+ # Bezpieczny deployment i limity dowodów zgody
2
+
3
+ ## Przesyłanie do WordPressa
4
+
5
+ Usunięto grunt-ftp-push i transport FTP. `grunt deploy` i `grunt dev:ssh` korzystają z rsync przez SSH; `grunt dev:ftp` jest wyłącznie aliasem do bezpiecznej ścieżki. Nie ma powrotu do zwykłego FTP. Wymagane są rsync, OpenSSH, klucz lub agent SSH i wcześniej zweryfikowany klucz serwera w known_hosts.
6
+
7
+ ```dotenv
8
+ SSH_HOST=example.com
9
+ SSH_USER=deploy
10
+ SSH_PORT=22
11
+ SSH_WP_PLUGINS_FOLDER_PATH=/var/www/wp-content/plugins
12
+ ```
13
+
14
+ Ścieżka musi być bezwzględna, bez spacji i metaznaków powłoki. Stare FTP_HOST/FTP_USERNAME/FTP_PASSWORD nie uruchomią transmisji. BatchMode i StrictHostKeyChecking=yes powodują zatrzymanie przy braku uwierzytelnienia lub nieznanym/zmienionym kluczu. Potwierdź fingerprint niezależnym kanałem przed dodaniem klucza — samo pobranie przez ssh-keyscan nie jest weryfikacją.
15
+
16
+ ## WordPress.org SVN
17
+
18
+ SVN_USERNAME i SVN_PASSWORD nadal działają. SVN 1.10+ odczytuje hasło przez --password-from-stdin, hasło nie trafia do argv ani konfiguracji Grunta i jest usuwane ze środowiska procesu potomnego svn. Polecenia uwierzytelniane nie przechodzą przez sh -c. Weryfikacja certyfikatu pozostaje włączona, cache hasła jest wyłączony. Hasło zawierające CR/LF jest odrzucane — stdin wymaga pojedynczej linii.
19
+
20
+ ## Publiczne dowody zgody
21
+
22
+ Kategorie muszą zawierać dokładnie essential=true, analytics=boolean i marketing=boolean. Inne pola kategorii, zagnieżdżone payloady i skalary są odrzucane. Limit body: 16 000 bajtów; pełny zapis z informacjami administratora: 16 384 bajty. Długie konfiguracje przekraczające ten limit zwracają 413 i wymagają skrócenia informacji.
23
+
24
+ Atomowe limity: 600 poprawnych żądań na minutę globalnie i 120 na minutę dla jednego z 256 stałych koszyków adresów. Koszyki mogą współdzielić limit; nie przechowują surowych adresów. Spóźnione żądanie nie może cofnąć okna czasu. Liczba liczników wynosi najwyżej 258 (capacity, minute, ip_0…ip_255).
25
+
26
+ Domyślny budżet to 10 000 dowodów, zmieniany filtrem cookie4_receipt_max_records (zakres 1–100 000). Przy wyczerpaniu nowe dowody zwracają 503, a już zapisany identyfikator pozostaje idempotentny. W tym momencie wykonywane jest także usuwanie przeterminowanych dowodów, niezależnie od WP-Cron. Retencja nadal korzysta z cookie4_receipt_retention_days (domyślnie 180 dni). Frontend nadal emituje cb:receipt-error przy nieudanym zapisie serwerowym; należy monitorować ten sygnał i pojemność na witrynach o większym ruchu. Odmowa i cofnięcie zgody nie zależą od dostępności archiwum.
27
+
28
+ Rezerwacja budżetu i zapis są jedną transakcją. Przerwanie procesu lub rollback nie zużywa miejsca. Usuwanie i zwalnianie budżetu również jest transakcyjne. Migracja przygotowuje licznik przed uruchomieniem nowej retencji; na MySQL wymaga InnoDB i w razie potrzeby konwertuje te dwie tabele. Przy błędzie migracji zapis jest odrzucany. Dotychczasowe dowody nie są przycinane ani usuwane z powodu samej migracji. Stare transients z poprzedniej wersji mogą pozostać do standardowego sprzątania WordPressa.