Compare commits

..

3 Commits

Author SHA1 Message Date
b0dd58fa9c Report improvements 2021-11-25 21:14:55 +01:00
de13980609 Performance improvements 2021-11-25 21:14:40 +01:00
acaa9430a1 Include only the worst requests in HAR export 2021-11-25 21:13:15 +01:00
8 changed files with 175 additions and 92 deletions

View File

@ -303,4 +303,8 @@ export default class ExtendedRequest {
connection: "443", connection: "443",
}; };
} }
getMaxPriority() {
return Math.max(...this.stolenData.map((entry) => entry.getPriority()));
}
} }

View File

@ -1,6 +1,10 @@
import React from "react"; import React from "react";
import { RequestCluster } from "../request-cluster"; import { RequestCluster } from "../request-cluster";
import { Classifications, Sources } from "../stolen-data-entry"; import {
Classifications,
Sources,
StolenDataEntry,
} from "../stolen-data-entry";
const emailClassifications: Record<keyof typeof Classifications, string> = { const emailClassifications: Record<keyof typeof Classifications, string> = {
id: "sztucznie nadane mi ID", id: "sztucznie nadane mi ID",
@ -25,11 +29,10 @@ export default function DomainSummary({
Właścicielowi domeny <strong>{cluster.id}</strong> zostały ujawnione:{" "} Właścicielowi domeny <strong>{cluster.id}</strong> zostały ujawnione:{" "}
<ul> <ul>
<li>Mój adres IP</li> <li>Mój adres IP</li>
{cluster {cluster.representativeStolenData
.getRepresentativeStolenData()
.filter((entry) => entry.isMarked) .filter((entry) => entry.isMarked)
.map((entry) => ( .map((entry) => (
<li> <li key={entry.id}>
{emailClassifications[entry.classification]}{" "} {emailClassifications[entry.classification]}{" "}
{emailSources[entry.source]} (nazwa: <code>{entry.name}</code>,{" "} {emailSources[entry.source]} (nazwa: <code>{entry.name}</code>,{" "}
wartość: <code>{entry.getValuePreview()}</code>) wartość: <code>{entry.getValuePreview()}</code>)

View File

@ -192,18 +192,20 @@ export default function EmailTemplate2({
. .
</> </>
) : ( ) : (
/* HTML */ `o ile po wejściu na stronę wcisnąłem w wyskakującym <>
okienku przycisk ${config.popup_accept_all_text}, o tyle nie stanowi o ile po wejściu na stronę wcisnąłem w wyskakującym okienku przycisk
to według mnie ważnej w świetle RODO zgody, gdyż brakowało w tym {config.popup_accept_all_text}, o tyle nie stanowi to według mnie
okienku równie łatwo osiągalnego przycisku, którego kliknięcie ważnej w świetle RODO zgody, gdyż brakowało w tym okienku równie
skutkowałoby zasygnalizowaniem braku mojej zgody na takie łatwo osiągalnego przycisku, którego kliknięcie skutkowałoby
przetwarzanie moich danych. Mówiąc wprost &mdash; wyrażenie zgody zasygnalizowaniem braku mojej zgody na takie przetwarzanie moich
było łatwiejsze niż jej niewyrażenie. Niewyrażenie zgody wiąże się z danych. Mówiąc wprost &mdash; wyrażenie zgody było łatwiejsze niż
negatywną konsekwencją konieczności przechodzenia przez dodatkowe jej niewyrażenie. Niewyrażenie zgody wiąże się z negatywną
kroki w wyskakującym okienku. Zatem tak otrzymana przez Państwo moja konsekwencją konieczności przechodzenia przez dodatkowe kroki w
zgoda nie jest poprawną podstawą prawną do przetwarzania moich wyskakującym okienku. Zatem tak otrzymana przez Państwo moja zgoda
danych osobowych, gdyż nie spełnia warunku dobrowolności wspomnianego nie jest poprawną podstawą prawną do przetwarzania moich danych
w Art. 4. pkt 11. RODO.` osobowych, gdyż nie spełnia warunku dobrowolności wspomnianego w
Art. 4. pkt 11. RODO.
</>
)}{" "} )}{" "}
Za zgodę nie można też uznać posiadania włączonej obsługi cookies w Za zgodę nie można też uznać posiadania włączonej obsługi cookies w
przeglądarce, jakichkolwiek innych ustawień przeglądarki, ani pasywnych przeglądarce, jakichkolwiek innych ustawień przeglądarki, ani pasywnych
@ -380,4 +382,5 @@ export default function EmailTemplate2({
</p> </p>
</> </>
); );
return result;
} }

View File

@ -22,9 +22,36 @@ function handleNewFile(
} }
function generateFakeHAR(entries: StolenDataEntry[]) { function generateFakeHAR(entries: StolenDataEntry[]) {
const requests = entries const requests = unique(entries.map((entry) => entry.request))
.sort((entry1, entry2) => entry2.getPriority() - entry1.getPriority()) .sort((request1, request2) => {
.map((entry) => entry.request); if (request1.shorthost < request2.shorthost) {
return -1;
} else if (request1.shorthost > request2.shorthost) {
return 1;
} else {
return request2.getMaxPriority() - request1.getMaxPriority();
}
})
.filter((_, index, array) => {
if (index !== 0 && array[index].shorthost == array[index - 1].shorthost) {
return false;
}
return true;
})
.sort(
(entry1, entry2) => entry2.getMaxPriority() - entry1.getMaxPriority()
);
console.log(
"GENERATEHAR! Got",
entries.length,
"entries, ",
unique(entries.map((e) => e.request)),
"requests. Filtered down to",
requests.length,
"requests"
);
return { return {
log: { log: {
version: "1.2", version: "1.2",
@ -47,7 +74,7 @@ function generateFakeHAR(entries: StolenDataEntry[]) {
}, },
}, },
], ],
entries: unique(requests).map((r) => r.toHAR()), entries: requests.map((r) => r.toHAR()),
}, },
}; };
} }

View File

@ -1,90 +1,123 @@
import React from "react"; import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { getMemory } from "../memory"; import { getMemory } from "../memory";
import { Classifications } from "../stolen-data-entry"; import { Classifications, StolenDataEntry } from "../stolen-data-entry";
import { reduceConcat, useEmitter } from "../util"; import { reduceConcat, useEmitter } from "../util";
import EmailTemplate from "./email-template"; import EmailTemplate from "./email-template";
import HARConverter from "./har-converter"; import HARConverter from "./har-converter";
function DataPreview({
entries,
refresh,
}: {
entries: StolenDataEntry[];
refresh: () => void;
}) {
// currently not used, maybe scraped entirely in the future
return (
<table>
<thead>
<tr>
<th>Adres docelowy</th>
<th>Źródło danych</th>
<th>Treść danych</th>
<th>Klasyfikacja</th>
</tr>
</thead>
<tbody>
{entries.map((entry) => (
<tr
key={entry.id}
style={{
backgroundColor:
entry.classification == "id" ? "yellow" : "white",
}}
>
<td>{entry.request.shorthost}</td>
<td style={{ overflowWrap: "anywhere" }}>
{entry.source}:{entry.name}
</td>
<td
style={{
width: "400px",
overflowWrap: "anywhere",
backgroundColor: entry.isRelatedToID() ? "#ffff0054" : "white",
}}
>
{entry.getValuePreview()}
{/* always gonna have
one key, because unwrapEntry is called above */}
</td>
<td>
<select
value={entry.classification}
onChange={(e) => {
entry.classification = e.target
.value as keyof typeof Classifications;
refresh();
}}
>
{[
["history", "Historia przeglądania"],
["id", "Sztucznie nadane id"],
["location", "Lokalizacja"],
].map(([key, name]) => (
<option key={key} value={key}>
{name}
</option>
))}
</select>
</td>
</tr>
))}
</tbody>
</table>
);
}
function Report() { function Report() {
console.time("getOrigin");
const origin = new URL(document.location.toString()).searchParams.get( const origin = new URL(document.location.toString()).searchParams.get(
"origin" "origin"
); );
console.timeEnd("getOrigin");
console.time("useMemory");
const [counter, setCounter] = useEmitter(getMemory()); const [counter, setCounter] = useEmitter(getMemory());
console.timeEnd("useMemory");
function refresh() { function refresh() {
setCounter((c) => c + 1); setCounter((c) => c + 1);
} }
console.time("getClustersForOrigin");
const clusters = getMemory().getClustersForOrigin(origin); const clusters = getMemory().getClustersForOrigin(origin);
const entries = Object.values(clusters) console.timeEnd("getClustersForOrigin");
.map((cluster) => cluster.getRepresentativeStolenData()) const [entries, setEntries] = useState<StolenDataEntry[]>([]);
.reduce(reduceConcat, []) console.time("useEffect report-window");
.filter((entry) => entry.isMarked); useEffect(() => {
return ( setEntries(
Object.values(clusters)
.map((cluster) => {
cluster.calculatetRepresentativeStolenData();
return cluster.representativeStolenData;
})
.reduce(reduceConcat, [])
.filter((entry) => entry.isMarked)
);
}, []);
console.timeEnd("useEffect report-window");
if (entries.length == 0) {
return <>Wczytywanie...</>;
}
console.time("rendering template");
const result = (
<div {...{ "data-version": counter }}> <div {...{ "data-version": counter }}>
{/*<DataPreview {...{entries, refresh}} */}
<h1>Generuj treść maila dla {origin}</h1> <h1>Generuj treść maila dla {origin}</h1>
<table>
<thead>
<tr>
<th>Adres docelowy</th>
<th>Źródło danych</th>
<th>Treść danych</th>
<th>Klasyfikacja</th>
</tr>
</thead>
<tbody>
{entries.map((entry) => (
<tr
key={entry.id}
style={{
backgroundColor:
entry.classification == "id" ? "yellow" : "white",
}}
>
<td>{entry.request.shorthost}</td>
<td style={{ overflowWrap: "anywhere" }}>
{entry.source}:{entry.name}
</td>
<td
style={{
width: "400px",
overflowWrap: "anywhere",
backgroundColor: entry.isRelatedToID()
? "#ffff0054"
: "white",
}}
>
{entry.getValuePreview()}
{/* always gonna have
one key, because unwrapEntry is called above */}
</td>
<td>
<select
value={entry.classification}
onChange={(e) => {
entry.classification = e.target
.value as keyof typeof Classifications;
refresh();
}}
>
{[
["history", "Historia przeglądania"],
["id", "Sztucznie nadane id"],
["location", "Lokalizacja"],
].map(([key, name]) => (
<option key={key} value={key}>
{name}
</option>
))}
</select>
</td>
</tr>
))}
</tbody>
</table>
<EmailTemplate {...{ entries, clusters, version: counter }} /> <EmailTemplate {...{ entries, clusters, version: counter }} />
<HARConverter {...{ entries }} /> <HARConverter {...{ entries }} />
</div> </div>
); );
console.timeEnd("rendering template");
return result;
} }
ReactDOM.render(<Report />, document.getElementById("app")); ReactDOM.render(<Report />, document.getElementById("app"));

View File

@ -13,6 +13,7 @@ const source_priority: Array<Sources> = [
export class RequestCluster extends EventEmitter { export class RequestCluster extends EventEmitter {
public requests: ExtendedRequest[] = []; public requests: ExtendedRequest[] = [];
public representativeStolenData: StolenDataEntry[] = [];
constructor(public id: string) { constructor(public id: string) {
super(); super();
} }
@ -30,14 +31,14 @@ export class RequestCluster extends EventEmitter {
return false; return false;
} }
getRepresentativeStolenData( calculatetRepresentativeStolenData(
filter: { filter: {
minValueLength: number; minValueLength: number;
cookiesOnly: boolean; cookiesOnly: boolean;
cookiesOrOriginOnly: boolean; cookiesOrOriginOnly: boolean;
} = { minValueLength: 0, cookiesOnly: false, cookiesOrOriginOnly: false } } = { minValueLength: 0, cookiesOnly: false, cookiesOrOriginOnly: false }
): StolenDataEntry[] { ): StolenDataEntry[] {
return this.requests this.representativeStolenData = this.requests
.map((request) => request.stolenData) .map((request) => request.stolenData)
.reduce((a, b) => a.concat(b), []) .reduce((a, b) => a.concat(b), [])
@ -120,6 +121,7 @@ export class RequestCluster extends EventEmitter {
.sort((entry1, entry2) => .sort((entry1, entry2) =>
entry1.getPriority() > entry2.getPriority() ? -1 : 1 entry1.getPriority() > entry2.getPriority() ? -1 : 1
); );
return this.representativeStolenData;
} }
static sortCompare(a: RequestCluster, b: RequestCluster) { static sortCompare(a: RequestCluster, b: RequestCluster) {
@ -165,7 +167,8 @@ export class RequestCluster extends EventEmitter {
} }
autoMark() { autoMark() {
this.getRepresentativeStolenData().forEach((entry) => { this.calculatetRepresentativeStolenData();
this.representativeStolenData.forEach((entry) => {
entry.autoMark(); entry.autoMark();
}); });
} }

View File

@ -125,13 +125,15 @@ export default function StolenDataCluster({
</h2> </h2>
<div> <div>
{cluster.getFullHosts().map((host) => ( {cluster.getFullHosts().map((host) => (
<a href={`https://${host}`}>{host}, </a> <a key={host} href={`https://${host}`}>
{host},{" "}
</a>
))} ))}
</div> </div>
<table> <table>
<tbody> <tbody>
{cluster {cluster
.getRepresentativeStolenData({ .calculatetRepresentativeStolenData({
minValueLength, minValueLength,
cookiesOnly, cookiesOnly,
cookiesOrOriginOnly, cookiesOrOriginOnly,

10
util.ts
View File

@ -1,5 +1,11 @@
import { EventEmitter } from "events"; import { EventEmitter } from "events";
import { Dispatch, SetStateAction, useEffect, useState } from "react"; import {
Dispatch,
ReactChildren,
SetStateAction,
useEffect,
useState,
} from "react";
export type Unpromisify<T> = T extends Promise<infer R> ? R : T; export type Unpromisify<T> = T extends Promise<infer R> ? R : T;
export type Unarray<T> = T extends Array<infer R> ? R : T; export type Unarray<T> = T extends Array<infer R> ? R : T;
@ -39,6 +45,8 @@ export function getshorthost(host: string) {
let lookback = parts.at(-2).length > 3 ? -2 : -3; let lookback = parts.at(-2).length > 3 ? -2 : -3;
if (parts.at(-2) == "doubleclick") { if (parts.at(-2) == "doubleclick") {
lookback = -4; // to distinguish between google ads and stats lookback = -4; // to distinguish between google ads and stats
} else if (parts.at(-2) == "google") {
lookback = -3; // to distinguish various google services
} }
return parts.slice(lookback).join("."); return parts.slice(lookback).join(".");
} }