diff --git a/extended-request.ts b/extended-request.ts index df4a0a8..073f689 100644 --- a/extended-request.ts +++ b/extended-request.ts @@ -1,6 +1,33 @@ import { StolenDataEntry } from "./stolen-data-entry"; import { getshorthost, parseCookie, Request } from "./util"; +export type HAREntry = { + pageref: string; + startedDateTime: string; + request: { + bodySize: number; + cookies: {}[]; + headers: {}[]; + headersSize: number; + httpVersion: string; + method: string; + postData: { + mimeType: string; + params: { name: string; value: string }[]; + text: string; + }; + queryString: { name: string; value: string }[]; + url: string; + }; + response: {}; // not relevant + cache: {}; + timings: {}; + time: number; + _securityState: string; + serverIPAddress: string; + connection: string; +}; + const whitelisted_cookies = [ /^Accept.*$/, /^Host$/, @@ -163,4 +190,10 @@ export default class ExtendedRequest { getHost() { return new URL(this.url).host; } + + matchesHAREntry(har: HAREntry): boolean { + const rq = this.data; + const hrq = har.request; + return rq.url == hrq.url; + } } diff --git a/report-window/domain-summary.tsx b/report-window/domain-summary.tsx new file mode 100644 index 0000000..759f09b --- /dev/null +++ b/report-window/domain-summary.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { RequestCluster } from "../request-cluster"; +import { Classifications, Sources } from "../stolen-data-entry"; + +const emailClassifications: Record = { + id: "sztucznie nadane mi ID", + history: "część mojej historii przeglądania", +}; + +const emailSources: Record = { + header: "w nagłówku HTTP", + cookie: "z pliku Cookie", + pathname: "jako części adresu URL", + queryparams: "jako część adresu URL (query-params)", +}; + +export default function DomainSummary({ + cluster, +}: { + cluster: RequestCluster; +}) { + return ( +
  • + Właściciel domeny {cluster.id} otrzymał:{" "} +
      + {cluster + .getMarkedEntries() + .sort((entryA, entryB) => (entryA.value > entryB.value ? -1 : 1)) + .reduce((acc, entry, index, arr) => { + if (index === 0) { + return [entry]; + } + if (entry.value != arr[index - 1].value) { + acc.push(entry); + } + return acc; + }, []) + .map((entry) => ( +
    • + {emailClassifications[entry.classification]}{" "} + {emailSources[entry.source]} +  ({entry.name.trim()}) +
    • + ))} +
    +
  • + ); +} diff --git a/report-window/email-template.tsx b/report-window/email-template.tsx new file mode 100644 index 0000000..510015a --- /dev/null +++ b/report-window/email-template.tsx @@ -0,0 +1,121 @@ +import React, { useState } from "react"; +import { RequestCluster } from "../request-cluster"; +import { StolenDataEntry } from "../stolen-data-entry"; +import { getDate } from "../util"; +import DomainSummary from "./domain-summary"; + +type PopupState = "not_clicked" | "clicked_but_invalid"; + +export default function EmailTemplate({ + marked_entries, + clusters, +}: { + marked_entries: StolenDataEntry[]; + clusters: Record; +}): JSX.Element { + const [popupState, setPopupState] = useState("not_clicked"); + + return ( +
    + + +

    + Dzień dobry, w dniu {getDate()} odwiedziłem stronę{" "} + {marked_entries[0].request.originalURL}. Strona ta wysłała moje dane + osobowe do podmiotów trzecich - bez mojej zgody.{" "} +

    +
      + {Object.values(clusters) + .filter((cluster) => cluster.hasMarks()) + .map((cluster) => ( + + ))} +
    + {popupState === "not_clicked" ? ( +

    + Dane te zostały wysłane przez Państwa stronę zanim zdążyłem w ogóle + przeczytać treść wyskakującego okienka ze zgodami. +

    + ) : null} +

    + Nie widzę zatem przesłanki legalizującej takie przetwarzanie moich + danych osobowych (na pewno nie jest to przetwarzanie konieczne do + wyświetlenia strony z technicznego punktu widzenia). Jeżeli takie + przesłanki legalizujące jednak występują, proszę o ich wskazanie, + dla każdego z celów i podmiotów z osobna. +

    +

    + Jeżeli wskazaną przez Państwa przesłanką legalizującą dany element + procesu przetwarzania danych osobowych przez Państwa stronę jest Art 6. + pkt 1 lit. a) RODO (zgoda), na mocy Art. 7 pkt 1 RODO proszę o + wykazanie, że udzieliłem Państwu zgodę na takie przetwarzanie moich + danych osobowych zanim to przetwarzanie nastąpiło, oraz że ta zgoda jest + ważna w świetle RODO (odnosząc się w szczególności do art. 7 ust. 3 + RODO). Z góry zaznaczam, że „ustawienia przeglądarki” nie stanowią + ważnej w świetle RODO zgody. +

    +

    + Jeżeli wskazaną przez Państwa przesłanką legalizującą dany element + procesu przetwarzania danych osobowych przez Państwa stronę jest Art 6. + pkt 1 lit. b) RODO (niezbędność takiego przetwarzania do wykonania + umowy), proszę o wskazanie, w jaki sposób ta konieczność zachodzi, oraz + co sprawia, że Państwa zdaniem nie można wykonać umowy związanej z + wyświetleniem Państwa strony bez przekazywania sztucznie nadanego ID w + plikach Cookies lub historii przeglądania w nagłówku Referer do + wskazanych podmiotów trzecich. +

    +

    + Jeżeli wskazaną przez Państwa przesłanką legalizującą dany element + procesu przetwarzania danych osobowych przez Państwa stronę jest Art 6. + pkt 1 lit. f) RODO (uzasadniony interes), proszę o wskazanie, jaki to + jest konkretny interes (prosze o bardziej dokładny opis + niż np. tylko "marketing"), oraz o wynik testu równowagi pomiędzy + Państwa interesem a moimi podstawowymi wolnościami i prawami - ze + wskazaniem tego, co sprawia, że w Państwa ocenie Państwa uzasadniony + interes przeważa moje prawa i interesy w kontekście wspomnianych powyżej + procesów przetwarzania danych. +

    +

    + Niniejszym zwracam się także z żądaniem wycofania przesłanych przez + Państwa stronę moich danych osobowych z baz wyżej wymienionych podmiotów + oraz przesłania potwierdzenia uwiarygadniającego pomyślne wycofanie tych + danych. Proszę też o przesłanie tożsamości podmiotów, które są + właścicielami wyżej wymienionych domen, abym mógł zapoznać się z ich + politykami prywatności. +

    +

    + Proszę też o wysłanie kopii danych zebranych na mój temat i wysłanych do + wyżej wymienionych podmiotów. +

    +

    + W odpowiedzi proszę się nie powoływać na IAB Europe i ich rzekomą renomę + w tworzeniu rozwiązań zgodnych z RODO. IAB chroni interes reklamodawców, + a nie Użytkowników i ich rozwiązania (np. TCF) są notorycznie niezgodne + z RODO i pozbawione szacunku dla Użytkowników. +

    +

    + Apeluję także o wprowadzenie stosownych zmian na stronie tak, aby nie + pozostawiać cienia wątpliwości odnośnie tego, na mocy jakiej przesłanki + legalizującej dane są przetwarzane przez wspomniane podmioty trzecie, + lub tak, aby te dane po prostu nie były wysyłane. Pomoże to zachować + prywatność innym użytkownikom Państwa strony. Polecam Państwa uwadze + oficjalne wytyczne EROD dotyczące zgody w kontekście RODO: + https://edpb.europa.eu/sites/default/files/files/file1/edpb_guidelines_202005_consent_pl.pdf + ). Aby na przykład zapobiec automatycznemu wysyłaniu historii + przeglądania do podmiotów trzecich przez Państwa stronę, można po prostu + ustawić odpowiednio treść nagłówka{" "} + + Referrer-Policy{" "} + + . +

    +
    + ); +} diff --git a/report-window/har-converter.tsx b/report-window/har-converter.tsx new file mode 100644 index 0000000..4bd6258 --- /dev/null +++ b/report-window/har-converter.tsx @@ -0,0 +1,53 @@ +import React, { useState } from "react"; +import { HAREntry } from "../extended-request"; +import { StolenDataEntry } from "../stolen-data-entry"; + +function handleNewFile( + element: HTMLInputElement, + marked_entries: StolenDataEntry[], + setFiltered: (Blob) => void +) { + const reader = new FileReader(); + reader.addEventListener("load", () => { + const content = JSON.parse(reader.result as string); + content.log.entries = content.log.entries.filter((har_entry: HAREntry) => + marked_entries.some((stolen_entry) => + stolen_entry.matchesHAREntry(har_entry) + ) + ); + setFiltered( + new Blob([JSON.stringify(content)], { type: "application/json" }) + ); + }); + reader.readAsText(element.files[0]); +} + +export default function HARConverter({ + marked_entries, +}: { + marked_entries: StolenDataEntry[]; +}) { + const [filtered, setFiltered] = useState(null); + const [filename, setFilename] = useState(""); + return ( +
    + { + setFilename(e.target.files[0].name); + handleNewFile(e.target, marked_entries, setFiltered); + }} + /> + {(filtered && ( + + Pobierz wyfiltrowany HAR + + )) || + null} +
    + ); +} diff --git a/report-window/report-window.tsx b/report-window/report-window.tsx index 1b2400b..c15fb11 100644 --- a/report-window/report-window.tsx +++ b/report-window/report-window.tsx @@ -1,55 +1,10 @@ -import React, { useState } from "react"; +import React from "react"; import ReactDOM from "react-dom"; import { getMemory } from "../memory"; -import { RequestCluster } from "../request-cluster"; -import { Classifications, Sources } from "../stolen-data-entry"; -import { getDate } from "../util"; - -const emailClassifications: Record = { - id: "sztucznie nadane mi ID", - history: "część mojej historii przeglądania", -}; - -const emailSources: Record = { - header: "w nagłówku HTTP", - cookie: "z pliku Cookie", - pathname: "jako części adresu URL", - queryparams: "jako część adresu URL (query-params)", -}; - -type PopupState = "not_clicked" | "clicked_but_invalid"; - -function DomainSummary({ cluster }: { cluster: RequestCluster }) { - return ( -
  • - Właściciel domeny {cluster.id} otrzymał:{" "} -
      - {cluster - .getMarkedEntries() - .sort((entryA, entryB) => (entryA.value > entryB.value ? -1 : 1)) - .reduce((acc, entry, index, arr) => { - if (index === 0) { - return [entry]; - } - if (entry.value != arr[index - 1].value) { - acc.push(entry); - } - return acc; - }, []) - .map((entry) => ( -
    • - {emailClassifications[entry.classification]}{" "} - {emailSources[entry.source]} -  ({entry.name.trim()}) -
    • - ))} -
    -
  • - ); -} +import EmailTemplate from "./email-template"; +import HARConverter from "./har-converter"; function Report() { - const [popupState, setPopupState] = useState("not_clicked"); const origin = new URL(document.location.toString()).searchParams.get( "origin" ); @@ -111,108 +66,8 @@ function Report() { ))} - - -
    -

    - Dzień dobry, w dniu {getDate()} odwiedziłem stronę{" "} - {marked_entries[0].request.originalURL}. Strona ta wysłała moje dane - osobowe do podmiotów trzecich - bez mojej zgody.{" "} -

    -
      - {Object.values(clusters) - .filter((cluster) => cluster.hasMarks()) - .map((cluster) => ( - - ))} -
    - {popupState === "not_clicked" ? ( -

    - Dane te zostały wysłane przez Państwa stronę zanim zdążyłem w ogóle - przeczytać treść wyskakującego okienka ze zgodami. -

    - ) : null} -

    - Nie widzę zatem przesłanki legalizującej takie przetwarzanie moich - danych osobowych (na pewno nie jest to przetwarzanie konieczne do - wyświetlenia strony z technicznego punktu widzenia). Jeżeli takie - przesłanki legalizujące jednak występują, proszę o ich wskazanie, - dla każdego z celów i podmiotów z osobna. -

    -

    - Jeżeli wskazaną przez Państwa przesłanką legalizującą dany element - procesu przetwarzania danych osobowych przez Państwa stronę jest Art - 6. pkt 1 lit. a) RODO (zgoda), na mocy Art. 7 pkt 1 RODO proszę o - wykazanie, że udzieliłem Państwu zgodę na takie przetwarzanie moich - danych osobowych zanim to przetwarzanie nastąpiło, oraz że ta zgoda - jest ważna w świetle RODO (odnosząc się w szczególności do art. 7 ust. - 3 RODO). Z góry zaznaczam, że „ustawienia przeglądarki” nie stanowią - ważnej w świetle RODO zgody. -

    -

    - Jeżeli wskazaną przez Państwa przesłanką legalizującą dany element - procesu przetwarzania danych osobowych przez Państwa stronę jest Art - 6. pkt 1 lit. b) RODO (niezbędność takiego przetwarzania do wykonania - umowy), proszę o wskazanie, w jaki sposób ta konieczność zachodzi, - oraz co sprawia, że Państwa zdaniem nie można wykonać umowy związanej - z wyświetleniem Państwa strony bez przekazywania sztucznie nadanego ID - w plikach Cookies lub historii przeglądania w nagłówku Referer do - wskazanych podmiotów trzecich. -

    -

    - Jeżeli wskazaną przez Państwa przesłanką legalizującą dany element - procesu przetwarzania danych osobowych przez Państwa stronę jest Art - 6. pkt 1 lit. f) RODO (uzasadniony interes), proszę o wskazanie, jaki - to jest konkretny interes (prosze o bardziej dokładny - opis niż np. tylko "marketing"), oraz o wynik testu równowagi pomiędzy - Państwa interesem a moimi podstawowymi wolnościami i prawami - ze - wskazaniem tego, co sprawia, że w Państwa ocenie Państwa uzasadniony - interes przeważa moje prawa i interesy w kontekście wspomnianych - powyżej procesów przetwarzania danych. -

    -

    - Niniejszym zwracam się także z żądaniem wycofania przesłanych przez - Państwa stronę moich danych osobowych z baz wyżej wymienionych - podmiotów oraz przesłania potwierdzenia uwiarygadniającego pomyślne - wycofanie tych danych. Proszę też o przesłanie tożsamości podmiotów, - które są właścicielami wyżej wymienionych domen, abym mógł zapoznać - się z ich politykami prywatności. -

    -

    - Proszę też o wysłanie kopii danych zebranych na mój temat i wysłanych - do wyżej wymienionych podmiotów. -

    -

    - W odpowiedzi proszę się nie powoływać na IAB Europe i ich rzekomą - renomę w tworzeniu rozwiązań zgodnych z RODO. IAB chroni interes - reklamodawców, a nie Użytkowników i ich rozwiązania (np. TCF) są - notorycznie niezgodne z RODO i pozbawione szacunku dla Użytkowników. -

    -

    - Apeluję także o wprowadzenie stosownych zmian na stronie tak, aby nie - pozostawiać cienia wątpliwości odnośnie tego, na mocy jakiej - przesłanki legalizującej dane są przetwarzane przez wspomniane - podmioty trzecie, lub tak, aby te dane po prostu nie były wysyłane. - Pomoże to zachować prywatność innym użytkownikom Państwa strony. - Polecam Państwa uwadze oficjalne wytyczne EROD dotyczące zgody w - kontekście RODO: - https://edpb.europa.eu/sites/default/files/files/file1/edpb_guidelines_202005_consent_pl.pdf - ). Aby na przykład zapobiec automatycznemu wysyłaniu historii - przeglądania do podmiotów trzecich przez Państwa stronę, można po - prostu ustawić odpowiednio treść nagłówka{" "} - - Referrer-Policy{" "} - - . -

    -
    + + ); } diff --git a/stolen-data-entry.ts b/stolen-data-entry.ts index 5529c0a..1020670 100644 --- a/stolen-data-entry.ts +++ b/stolen-data-entry.ts @@ -1,5 +1,5 @@ import { TCModel } from "@iabtcf/core"; -import ExtendedRequest from "./extended-request"; +import ExtendedRequest, { HAREntry } from "./extended-request"; import { getMemory } from "./memory"; import { getshorthost, @@ -142,7 +142,6 @@ export class StolenDataEntry { } else { result = "id"; } - console.log("classifying", this.value, result, this.request.origin); return result; } @@ -151,6 +150,10 @@ export class StolenDataEntry { (entry) => (entry.classification = "id") ); } + + matchesHAREntry(har: HAREntry): boolean { + return this.request.matchesHAREntry(har); + } } export class MergedStolenDataEntry {