Include data value previews in the email.
This commit is contained in:
parent
b98a061d96
commit
a84a8f8c10
32
mark.ts
Normal file
32
mark.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { Classifications, StolenDataEntry } from "./stolen-data-entry";
|
||||
|
||||
export default class Mark {
|
||||
classification: keyof typeof Classifications;
|
||||
constructor(public entry: StolenDataEntry, public key: string) {
|
||||
this.classification = entry.classification;
|
||||
}
|
||||
|
||||
getParsedValue() {
|
||||
return this.entry.getParsedValue(this.key);
|
||||
}
|
||||
|
||||
get shorthost() {
|
||||
return this.entry.request.shorthost;
|
||||
}
|
||||
|
||||
get source() {
|
||||
return this.entry.source;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.entry.name;
|
||||
}
|
||||
|
||||
get originalURL() {
|
||||
return this.entry.request.originalURL;
|
||||
}
|
||||
|
||||
get valuePreview(): string {
|
||||
return this.entry.getValuePreview(this.key);
|
||||
}
|
||||
}
|
|
@ -26,22 +26,22 @@ export default function DomainSummary({
|
|||
<ul>
|
||||
<li>Mój adres IP</li>
|
||||
{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) => (
|
||||
.getMarks()
|
||||
.sort((markA, markB) =>
|
||||
markA.entry.value > markB.entry.value ? -1 : 1
|
||||
)
|
||||
.map((mark) => (
|
||||
<li>
|
||||
{emailClassifications[entry.classification]}{" "}
|
||||
{emailSources[entry.source]}
|
||||
(<code>{entry.name.trim()}</code>)
|
||||
{emailClassifications[mark.classification]}{" "}
|
||||
{emailSources[mark.source]} (nazwa: {mark.name},{" "}
|
||||
{mark.key ? (
|
||||
<>
|
||||
pozycja <code>{mark.key}</code>,
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
wartość: <code>{mark.valuePreview}</code>)
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
|
|
@ -1,24 +1,27 @@
|
|||
import React, { useState } from "react";
|
||||
import Mark from "../mark";
|
||||
import { RequestCluster } from "../request-cluster";
|
||||
import { StolenDataEntry } from "../stolen-data-entry";
|
||||
import { getDate, toBase64 } from "../util";
|
||||
import DomainSummary from "./domain-summary";
|
||||
|
||||
type PopupState = "not_clicked" | "clicked_but_invalid";
|
||||
|
||||
export default function EmailTemplate({
|
||||
marked_entries,
|
||||
marks,
|
||||
clusters,
|
||||
version,
|
||||
}: {
|
||||
marked_entries: StolenDataEntry[];
|
||||
marks: Mark[];
|
||||
clusters: Record<string, RequestCluster>;
|
||||
version: number;
|
||||
}): JSX.Element {
|
||||
const [popupState, setPopupState] = useState<PopupState>("not_clicked");
|
||||
const [acceptAllName, setAcceptAllName] = useState<string>(
|
||||
"Zaakceptuj wszystkie"
|
||||
);
|
||||
const [popupScreenshotBase64, setPopupScreenshotBase64] =
|
||||
useState<string>(null);
|
||||
const [popupScreenshotBase64, setPopupScreenshotBase64] = useState<string>(
|
||||
null
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -64,8 +67,8 @@ export default function EmailTemplate({
|
|||
) : null}
|
||||
<p>
|
||||
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.{" "}
|
||||
{marks[0].originalURL}. Strona ta wysłała moje dane osobowe do podmiotów
|
||||
trzecich - bez mojej zgody.{" "}
|
||||
</p>
|
||||
<ul>
|
||||
{Object.values(clusters)
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { HAREntry } from "../extended-request";
|
||||
import { StolenDataEntry } from "../stolen-data-entry";
|
||||
import Mark from "../mark";
|
||||
import { getshorthost, unique } from "../util";
|
||||
|
||||
function handleNewFile(
|
||||
element: HTMLInputElement,
|
||||
marked_entries: StolenDataEntry[],
|
||||
marks: Mark[],
|
||||
setFiltered: (Blob) => void
|
||||
): 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)
|
||||
)
|
||||
marks.some((mark) => mark.entry.matchesHAREntry(har_entry))
|
||||
);
|
||||
setFiltered(
|
||||
new Blob([JSON.stringify(content)], { type: "application/json" })
|
||||
|
@ -23,8 +21,8 @@ function handleNewFile(
|
|||
reader.readAsText(element.files[0]);
|
||||
}
|
||||
|
||||
function generateFakeHAR(marked_entries: StolenDataEntry[]) {
|
||||
const requests = marked_entries.map((entry) => entry.request);
|
||||
function generateFakeHAR(marks: Mark[]) {
|
||||
const requests = marks.map((mark) => mark.entry.request);
|
||||
return {
|
||||
log: {
|
||||
version: "1.2",
|
||||
|
@ -52,14 +50,14 @@ function generateFakeHAR(marked_entries: StolenDataEntry[]) {
|
|||
};
|
||||
}
|
||||
|
||||
export default function HARConverter({
|
||||
marked_entries,
|
||||
}: {
|
||||
marked_entries: StolenDataEntry[];
|
||||
}) {
|
||||
export default function HARConverter({ marks }: { marks: Mark[] }) {
|
||||
const [filtered, setFiltered] = useState<Blob | null>(null);
|
||||
const [filename, setFilename] = useState("");
|
||||
const fakeHAR = generateFakeHAR(marked_entries);
|
||||
const [fakeHAR, setFakeHAR] = useState<ReturnType<typeof generateFakeHAR>>();
|
||||
useEffect(() => {
|
||||
setFakeHAR(generateFakeHAR(marks));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
|
@ -67,7 +65,7 @@ export default function HARConverter({
|
|||
accept=".har"
|
||||
onChange={(e) => {
|
||||
setFilename(e.target.files[0].name);
|
||||
handleNewFile(e.target, marked_entries, setFiltered);
|
||||
handleNewFile(e.target, marks, setFiltered);
|
||||
}}
|
||||
/>
|
||||
{(filtered && (
|
||||
|
@ -84,7 +82,7 @@ export default function HARConverter({
|
|||
new Blob([JSON.stringify(fakeHAR)], { type: "application/json" })
|
||||
)}
|
||||
download={`${getshorthost(
|
||||
marked_entries[0].request.originalURL
|
||||
marks[0].originalURL
|
||||
)}-${new Date().toJSON()}-trimmed.har`}
|
||||
>
|
||||
Pobierz "zfałszowany" HAR
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from "react";
|
|||
import ReactDOM from "react-dom";
|
||||
import { getMemory } from "../memory";
|
||||
import { Classifications } from "../stolen-data-entry";
|
||||
import { useEmitter } from "../util";
|
||||
import { reduceConcat, useEmitter } from "../util";
|
||||
import EmailTemplate from "./email-template";
|
||||
import HARConverter from "./har-converter";
|
||||
|
||||
|
@ -15,13 +15,15 @@ function Report() {
|
|||
setCounter((c) => c + 1);
|
||||
}
|
||||
const clusters = getMemory().getClustersForOrigin(origin);
|
||||
const marked_entries = Object.values(clusters)
|
||||
const marks = Object.values(clusters)
|
||||
.map((cluster) => cluster.getMarkedRequests())
|
||||
.reduce((a, b) => a.concat(b), [])
|
||||
.reduce(reduceConcat, [])
|
||||
.map((request) => request.getMarkedEntries())
|
||||
.reduce((a, b) => a.concat(b), []);
|
||||
.reduce(reduceConcat, [])
|
||||
.map((entry) => entry.marks)
|
||||
.reduce(reduceConcat, []);
|
||||
return (
|
||||
<div>
|
||||
<div {...{ "data-version": counter }}>
|
||||
<h1>Generuj treść maila dla {origin}</h1>
|
||||
<table>
|
||||
<thead>
|
||||
|
@ -33,42 +35,46 @@ function Report() {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{marked_entries.map((entry) => (
|
||||
{marks.map((mark) => (
|
||||
<tr
|
||||
key={mark.entry.request.originalURL + ";" + mark.key}
|
||||
style={{
|
||||
backgroundColor:
|
||||
entry.classification == "id" ? "yellow" : "white",
|
||||
mark.classification == "id" ? "yellow" : "white",
|
||||
}}
|
||||
>
|
||||
<td>{entry.request.shorthost}</td>
|
||||
<td>{mark.shorthost}</td>
|
||||
<td style={{ overflowWrap: "anywhere" }}>
|
||||
{entry.source}:{entry.name}
|
||||
{entry.markedKeys.join(",")}
|
||||
{mark.source}:{mark.name}
|
||||
{mark.key}
|
||||
</td>
|
||||
<td
|
||||
style={{
|
||||
width: "400px",
|
||||
overflowWrap: "anywhere",
|
||||
backgroundColor: entry.isRelatedToID()
|
||||
backgroundColor: mark.entry.isRelatedToID()
|
||||
? "#ffff0054"
|
||||
: "white",
|
||||
}}
|
||||
>
|
||||
{entry.value}
|
||||
{mark.valuePreview}
|
||||
{/* always gonna have
|
||||
one key, because unwrapEntry is calle above */}
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
value={entry.classification}
|
||||
value={mark.classification}
|
||||
onChange={(e) => {
|
||||
entry.classification = e.target
|
||||
mark.classification = e.target
|
||||
.value as keyof typeof Classifications;
|
||||
console.log("changed classification!");
|
||||
refresh();
|
||||
}}
|
||||
>
|
||||
{[
|
||||
["history", "Historia przeglądania"],
|
||||
["id", "Sztucznie nadane id"],
|
||||
["location", "Informacje na temat mojej lokalizacji"],
|
||||
["location", "Lokalizacja"],
|
||||
].map(([key, name]) => (
|
||||
<option key={key} value={key}>
|
||||
{name}
|
||||
|
@ -80,8 +86,8 @@ function Report() {
|
|||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<EmailTemplate {...{ marked_entries, clusters }} />
|
||||
<HARConverter {...{ marked_entries }} />
|
||||
<EmailTemplate {...{ marks, clusters, version: counter }} />
|
||||
<HARConverter {...{ marks }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ export class RequestCluster extends EventEmitter {
|
|||
return this.requests.some((request) => request.hasMark());
|
||||
}
|
||||
|
||||
getMarkedEntries() {
|
||||
getMarkedEntries(): StolenDataEntry[] {
|
||||
return this.requests
|
||||
.map((request) => request.getMarkedEntries())
|
||||
.reduce(reduceConcat, []);
|
||||
|
@ -114,4 +114,12 @@ export class RequestCluster extends EventEmitter {
|
|||
exposesOrigin() {
|
||||
return this.requests.some((request) => request.exposesOrigin());
|
||||
}
|
||||
|
||||
getMarks() {
|
||||
return this.requests
|
||||
.map((request) => request.getMarkedEntries())
|
||||
.reduce(reduceConcat, [])
|
||||
.map((entry) => entry.marks)
|
||||
.reduce(reduceConcat, []);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,7 +118,14 @@ export default function StolenDataCluster({
|
|||
key={origin + cluster.id + entry.getUniqueKey()}
|
||||
data-key={origin + cluster.id + entry.getUniqueKey()}
|
||||
>
|
||||
<th style={{ width: "100px", overflowWrap: "anywhere" }}>
|
||||
<th
|
||||
style={{
|
||||
width: "100px",
|
||||
overflowWrap: "anywhere",
|
||||
border: entry.hasMark("") ? "2px solid red" : "",
|
||||
}}
|
||||
onClick={() => entry.addMark("")}
|
||||
>
|
||||
{entry.getNames().map(hyphenate).join(", ")}
|
||||
</th>
|
||||
<td>{entry.getSources().map((source) => icons[source])}</td>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { TCModel } from "@iabtcf/core";
|
||||
import ExtendedRequest, { HAREntry } from "./extended-request";
|
||||
import Mark from "./mark";
|
||||
import { getMemory } from "./memory";
|
||||
import {
|
||||
getshorthost,
|
||||
|
@ -18,6 +19,8 @@ export const Classifications = <const>{
|
|||
location: "Informacje na temat mojego położenia",
|
||||
};
|
||||
|
||||
const ID_PREVIEW_MAX_LENGTH = 20;
|
||||
|
||||
const id = (function* id() {
|
||||
let i = 0;
|
||||
while (true) {
|
||||
|
@ -30,7 +33,7 @@ export class StolenDataEntry {
|
|||
public isIAB = false;
|
||||
public iab: TCModel | null = null;
|
||||
public id: number;
|
||||
public markedKeys: string[] = [];
|
||||
public marks: Mark[] = [];
|
||||
public classification: keyof typeof Classifications;
|
||||
|
||||
constructor(
|
||||
|
@ -79,6 +82,7 @@ export class StolenDataEntry {
|
|||
} else if (isURL(value)) {
|
||||
const url = new URL(value);
|
||||
const object = {
|
||||
[Symbol.for("originalURL")]: value,
|
||||
host: url.host,
|
||||
path: url.pathname,
|
||||
...Object.fromEntries(
|
||||
|
@ -93,7 +97,7 @@ export class StolenDataEntry {
|
|||
}
|
||||
}
|
||||
|
||||
getParsedValue(key_path: string): string | Record<string, unknown> {
|
||||
getParsedValue(key_path: string): string | Record<string | symbol, unknown> {
|
||||
let object = StolenDataEntry.parseValue(this.value);
|
||||
for (const key of key_path.split(".")) {
|
||||
if (key === "") continue;
|
||||
|
@ -103,20 +107,20 @@ export class StolenDataEntry {
|
|||
}
|
||||
|
||||
addMark(key: string) {
|
||||
this.markedKeys.push(key);
|
||||
getMemory().emit("change"); // to trigger rerender
|
||||
this.marks.push(new Mark(this, key));
|
||||
getMemory().emit("change", true); // to trigger rerender
|
||||
}
|
||||
|
||||
hasMark(key?: string) {
|
||||
if (key) {
|
||||
return this.markedKeys.some((k) => k == key);
|
||||
return this.marks.some((k) => k.key == key);
|
||||
} else {
|
||||
return this.markedKeys.length > 0;
|
||||
return this.marks.length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
removeMark(key: string) {
|
||||
this.markedKeys = this.markedKeys.filter((e) => e != key);
|
||||
this.marks = this.marks.filter((mark) => mark.key != key);
|
||||
getMemory().emit("change"); // to trigger rerender
|
||||
}
|
||||
|
||||
|
@ -155,15 +159,29 @@ export class StolenDataEntry {
|
|||
matchesHAREntry(har: HAREntry): boolean {
|
||||
return this.request.matchesHAREntry(har);
|
||||
}
|
||||
|
||||
getValuePreview(key = ""): string {
|
||||
const value = this.getParsedValue(key);
|
||||
const str = value.toString();
|
||||
if (this.classification == "id") {
|
||||
return (
|
||||
str.slice(0, Math.min(str.length / 3, ID_PREVIEW_MAX_LENGTH)) + "(...)"
|
||||
);
|
||||
} else if (typeof value === "object" && value[Symbol.for("originalURL")]) {
|
||||
return value[Symbol.for("originalURL")] as string;
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MergedStolenDataEntry {
|
||||
constructor(public entries: StolenDataEntry[]) {
|
||||
const all_marks = unique(
|
||||
entries.map((entry) => entry.markedKeys).reduce(reduceConcat, [])
|
||||
entries.map((entry) => entry.marks).reduce(reduceConcat, [])
|
||||
);
|
||||
for (const entry of entries) {
|
||||
entry.markedKeys = all_marks;
|
||||
entry.marks = all_marks;
|
||||
}
|
||||
// getMemory().emit("change"); // to trigger render
|
||||
}
|
||||
|
@ -211,7 +229,7 @@ export class MergedStolenDataEntry {
|
|||
|
||||
getMarkedValues() {
|
||||
return this.entries
|
||||
.map((entry) => entry.markedKeys)
|
||||
.map((entry) => entry.marks)
|
||||
.reduce((a, b) => a.concat(b), []);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"esModuleInterop": true,
|
||||
"lib": ["es2017", "dom", "es2019"],
|
||||
"typeRoots": ["node_modules/@types", "node_modules/web-ext-types"],
|
||||
"target": "es2019",
|
||||
"outDir": "lib"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user