Fix typechecks. Closes #54

This commit is contained in:
Kuba Orlik 2022-07-09 15:51:34 +02:00
parent e1d97f0411
commit 416a6aa340
11 changed files with 82 additions and 64 deletions

View File

@ -25,6 +25,7 @@ export default function EmailContent({
downloadFiles: Function; downloadFiles: Function;
user_role: string; user_role: string;
}) { }) {
console.log('rendering email!', answers);
const _ = (key: string) => v(key, answers.zaimek); const _ = (key: string) => v(key, answers.zaimek);
const problems = deduceProblems(answers, clusters); const problems = deduceProblems(answers, clusters);
const explainers = Array.from( const explainers = Array.from(

View File

@ -105,7 +105,7 @@ export default function HARConverter({ entries }: { entries: StolenDataEntry[] }
}) })
)} )}
download={`${getshorthost( download={`${getshorthost(
entries[0].request.originalURL entries[0].request.origin
)}-${new Date().toJSON()}-trimmed.har`} )}-${new Date().toJSON()}-trimmed.har`}
> >
Pobierz "zredukowany" HAR Pobierz "zredukowany" HAR

View File

@ -21,6 +21,9 @@ function Report() {
try { try {
const url = new URL(document.location.toString()); const url = new URL(document.location.toString());
const origin = url.searchParams.get('origin'); const origin = url.searchParams.get('origin');
if (!origin) {
return <div>Błąd: brak parametru "origin"</div>;
}
const [counter] = useEmitter(getMemory()); const [counter] = useEmitter(getMemory());
const rawAnswers = url.searchParams.get('answers'); const rawAnswers = url.searchParams.get('answers');
const [answers, setAnswers] = React.useState<ParsedAnswers>( const [answers, setAnswers] = React.useState<ParsedAnswers>(
@ -40,7 +43,7 @@ function Report() {
history.pushState({}, 'Rentgen', url.toString()); history.pushState({}, 'Rentgen', url.toString());
}, [mode, answers, origin]); }, [mode, answers, origin]);
const visited_url = Object.values(clusters) const visited_url = Object.values(clusters)
.sort((clusterA, clusterB) => (clusterA.lastModified > clusterB.lastModified ? -1 : 1)) .sort((a, b) => (a.lastModified > b.lastModified ? -1 : 1))
.find((cluster) => !!cluster.lastFullUrl)?.lastFullUrl; .find((cluster) => !!cluster.lastFullUrl)?.lastFullUrl;
if (!visited_url) { if (!visited_url) {

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { getMemory } from '../../memory'; import { getMemory } from '../../memory';
import Options from '../../options'; import Options from '../../options';
import { useEmitter } from '../../util'; import { useEmitter } from '../../util';

View File

@ -31,8 +31,7 @@ const Toolbar = () => {
const listener = async () => { const listener = async () => {
const tab = await getCurrentTab(); const tab = await getCurrentTab();
if (tab !== undefined) { if (tab !== undefined && tab.url) {
if (!tab.url) return;
const url = new URL(tab.url); const url = new URL(tab.url);
if (url.origin.startsWith('moz-extension')) { if (url.origin.startsWith('moz-extension')) {
return; return;
@ -51,6 +50,7 @@ const Toolbar = () => {
}); });
React.useEffect(() => { React.useEffect(() => {
if (!origin) return;
const exposedOriginDomains = Object.values(getMemory().getClustersForOrigin(origin)) const exposedOriginDomains = Object.values(getMemory().getClustersForOrigin(origin))
.filter((cluster) => cluster.exposesOrigin()) .filter((cluster) => cluster.exposesOrigin())
.map((cluster) => cluster.id); .map((cluster) => cluster.id);
@ -83,6 +83,7 @@ const Toolbar = () => {
}, [eventCounts['*'], origin]); }, [eventCounts['*'], origin]);
React.useEffect(() => { React.useEffect(() => {
if (!origin) return;
const cookieDomains = Object.values(getMemory().getClustersForOrigin(origin)) const cookieDomains = Object.values(getMemory().getClustersForOrigin(origin))
.filter((cluster) => cluster.hasCookies()) .filter((cluster) => cluster.hasCookies())
.map((cluster) => cluster.id); .map((cluster) => cluster.id);
@ -90,7 +91,7 @@ const Toolbar = () => {
switch (cookieDomains.length) { switch (cookieDomains.length) {
case 0: case 0:
null; break;
case 1: case 1:
setCookieDomainCopy(`${cookieDomains[0]}.`); setCookieDomainCopy(`${cookieDomains[0]}.`);
break; break;

View File

@ -1,3 +1,4 @@
'use strict';
import { StolenDataEntry } from './stolen-data-entry'; import { StolenDataEntry } from './stolen-data-entry';
import { import {
flattenObjectEntries, flattenObjectEntries,
@ -76,11 +77,12 @@ export default class ExtendedRequest {
public url: string; public url: string;
public shorthost: string; public shorthost: string;
public requestHeaders: { name: string; value?: string; binaryValue?: number[] }[] = []; public requestHeaders: { name: string; value?: string; binaryValue?: number[] }[] = [];
public originalURL: string | null = null;
public origin: string; public origin: string;
public initialized = false; public initialized = false;
public stolenData: StolenDataEntry[] = []; public stolenData: StolenDataEntry[] = [];
public originalPathname: string | null = null; public originalURL: string | null = null; // sometimes we can only establish that the given request applied to a certain origin, not a full URL from the address bar - in case of service workers, for example. Hence the null
public originalPathname: string | null = null; // same as above
public originalHost: string;
public requestBody: RequestBody; public requestBody: RequestBody;
static by_id = {} as Record<string, ExtendedRequest>; static by_id = {} as Record<string, ExtendedRequest>;
@ -95,9 +97,35 @@ export default class ExtendedRequest {
this.data = Object.assign({}, data); this.data = Object.assign({}, data);
(this.data as any).frameAncestors = [ (this.data as any).frameAncestors = [
...(data as any).frameAncestors.map((e: any) => ({ url: e.url })), ...((data as any)?.frameAncestors?.map((e: any) => ({ url: e.url })) || []),
]; ]; // making a copy?
this.origin = this.cacheOrigin();
// console.log('→→→',(this.data as any).frameAncestors, (data as any).frameAncestors);
let url: string;
let is_full_url = true;
if (this.data.type === 'main_frame') {
url = this.data.url;
} else if (this.data.frameId === 0 && this.data.documentUrl) {
url = this.data.documentUrl;
if (this.data.tabId == -1) {
//a service worker?
is_full_url = false;
}
} else if (
(this.data as any)?.frameAncestors &&
(this.data as any).frameAncestors[0] !== undefined
) {
url = (this.data as any).frameAncestors[0].url || '';
} else {
url = this.data.documentUrl || this.data.originUrl;
}
this.originalURL = is_full_url ? url : null;
this.origin = new URL(url).origin;
this.originalHost = new URL(url).host;
this.originalPathname = is_full_url ? new URL(url).pathname : null;
} }
addHeaders(headers: Request['requestHeaders']) { addHeaders(headers: Request['requestHeaders']) {
@ -106,50 +134,20 @@ export default class ExtendedRequest {
} }
init() { init() {
this.cacheOrigin();
this.initialized = true; this.initialized = true;
this.stolenData = this.getAllStolenData(); this.stolenData = this.getAllStolenData();
} }
cacheOrigin(): string {
let url: string;
if (this.data.type === 'main_frame') {
url = this.data.url;
} else if (this.data.originUrl) {
url = this.data.originUrl;
} else if (
(this.data as any)?.frameAncestors &&
(this.data as any).frameAncestors[0] !== undefined
) {
url = (this.data as any).frameAncestors[0].url || '';
} else {
const headers = Object.fromEntries(
this.requestHeaders.map(({ name, value }) => [name, value])
);
if (headers.Referer) {
url = headers.Referer;
} else {
url = this.data.url;
}
}
this.originalURL = url;
this.origin = new URL(url).origin;
this.originalPathname = new URL(url).pathname;
return this.origin;
}
isThirdParty() { isThirdParty() {
const request_url = new URL(this.data.url); const request_url = new URL(this.data.url);
const origin_url = new URL(this.originalURL); if (request_url.host.includes(this.originalHost)) {
if (request_url.host.includes(origin_url.host)) {
return false; return false;
} }
if (getshorthost(request_url.host) == getshorthost(origin_url.host)) { if (getshorthost(request_url.host) == getshorthost(this.originalHost)) {
return false; return false;
} }
return ( return (
request_url.origin != origin_url.origin || request_url.origin != this.origin ||
(this.data as any).urlClassification.thirdParty.length > 0 (this.data as any).urlClassification.thirdParty.length > 0
); );
} }
@ -161,9 +159,8 @@ export default class ExtendedRequest {
} }
exposesOrigin() { exposesOrigin() {
const url = new URL(this.originalURL); const host = this.originalHost;
const host = url.host; const path = this.originalPathname || '/';
const path = url.pathname;
const shorthost = getshorthost(host); const shorthost = getshorthost(host);
if (this.getReferer().includes(shorthost)) { if (this.getReferer().includes(shorthost)) {
return true; return true;
@ -215,7 +212,10 @@ export default class ExtendedRequest {
if ((Array.isArray(value) && value.length === 1 && !value[0]) || !value) { if ((Array.isArray(value) && value.length === 1 && !value[0]) || !value) {
return ['requestBody', key]; return ['requestBody', key];
} else if (!Array.isArray(value)) { } else if (!Array.isArray(value)) {
return ['raw', String.fromCharCode.apply(null, new Uint8Array(value.bytes))]; return [
'raw',
String.fromCharCode.apply(null, Array.from(new Uint8Array(value.bytes))),
];
} else { } else {
return [key, value || '']; return [key, value || ''];
} }
@ -234,7 +234,7 @@ export default class ExtendedRequest {
} }
getCookie(): string { getCookie(): string {
return this.requestHeaders.find((h) => h.name == 'Cookie')?.value; return this.requestHeaders.find((h) => h.name == 'Cookie')?.value || '';
} }
getPathParams(): StolenDataEntry[] { getPathParams(): StolenDataEntry[] {
@ -257,8 +257,8 @@ export default class ExtendedRequest {
getQueryParams(): StolenDataEntry[] { getQueryParams(): StolenDataEntry[] {
const url = new URL(this.data.url); const url = new URL(this.data.url);
return flattenObjectEntries( return flattenObjectEntries(
Array.from((url.searchParams as any).entries()) (Array.from((url.searchParams as any).entries()) as [string, string][])
.map(([key, value]) => [key, value || '']) .map(([key, value]: [string, string]) => [key, value || ''])
.map(([key, value]) => { .map(([key, value]) => {
return [key, StolenDataEntry.parseValue(safeDecodeURIComponent(value))]; return [key, StolenDataEntry.parseValue(safeDecodeURIComponent(value))];
}) })
@ -281,7 +281,7 @@ export default class ExtendedRequest {
.map((header) => { .map((header) => {
return [ return [
header.name, header.name,
StolenDataEntry.parseValue(safeDecodeURIComponent(header.value)), StolenDataEntry.parseValue(safeDecodeURIComponent(header.value || '')),
]; ];
}) })
).map(([key, value]) => new StolenDataEntry(this, 'header', key, value)); ).map(([key, value]) => new StolenDataEntry(this, 'header', key, value));

View File

@ -1,5 +1,5 @@
import ExtendedRequest from './extended-request'; import ExtendedRequest from './extended-request';
import { getshorthost, makeThrottle } from './util'; import { getshorthost } from './util';
import { RequestCluster } from './request-cluster'; import { RequestCluster } from './request-cluster';
import { SaferEmitter } from './safer-emitter'; import { SaferEmitter } from './safer-emitter';
@ -67,7 +67,7 @@ export default class Memory extends SaferEmitter {
emit(eventName: string, data = 'any'): boolean { emit(eventName: string, data = 'any'): boolean {
setTimeout(() => super.emit(eventName, data), 0); setTimeout(() => super.emit(eventName, data), 0);
return; return true;
} }
getClustersForOrigin(origin: string): Record<string, RequestCluster> { getClustersForOrigin(origin: string): Record<string, RequestCluster> {

View File

@ -9,12 +9,13 @@ const source_priority: Array<Sources> = ['cookie', 'pathname', 'queryparams', 'h
export class RequestCluster extends SaferEmitter { export class RequestCluster extends SaferEmitter {
public requests: ExtendedRequest[] = []; public requests: ExtendedRequest[] = [];
public representativeStolenData: StolenDataEntry[] = []; public representativeStolenData: StolenDataEntry[] = [];
public expanded: boolean; public expanded: boolean = false;
public lastModified: number = 0; public lastModified: number = 0;
public lastFullUrl: string | null = null; public lastFullUrl: string | null = null;
constructor(public id: string) { constructor(public id: string) {
super(); super();
} }
add(request: ExtendedRequest) { add(request: ExtendedRequest) {
this.requests.push(request); this.requests.push(request);
this.emit('change'); this.emit('change');

View File

@ -1,4 +1,3 @@
import { EventEmitter } from 'events';
import ExtendedRequest, { HAREntry } from './extended-request'; import ExtendedRequest, { HAREntry } from './extended-request';
import { SaferEmitter } from './safer-emitter'; import { SaferEmitter } from './safer-emitter';
@ -59,11 +58,10 @@ export class StolenDataEntry extends SaferEmitter {
getPriority() { getPriority() {
let priority = 0; let priority = 0;
priority += Math.min(this.value.length, 50); priority += Math.min(this.value.length, 50);
const url = new URL(this.request.originalURL); if (this.value.includes(this.request.originalHost)) {
if (this.value.includes(url.host)) {
priority += 100; priority += 100;
} }
if (this.value.includes(url.pathname)) { if (this.request.originalPathname && this.value.includes(this.request.originalPathname)) {
priority += 100; priority += 100;
} }
if (this.source === 'cookie') { if (this.source === 'cookie') {
@ -133,7 +131,7 @@ export class StolenDataEntry extends SaferEmitter {
} else if (value === null) { } else if (value === null) {
return 'null'; return 'null';
} else { } else {
return value.toString(); return (value as any).toString();
} }
} }
@ -238,10 +236,14 @@ export class StolenDataEntry extends SaferEmitter {
} }
exposesPath() { exposesPath() {
const pathname = this.request.originalPathname;
if (pathname === null) {
return false;
}
return ( return (
this.request.originalPathname !== '/' && this.request.originalPathname !== '/' &&
[this.value, safeDecodeURIComponent(this.value)].some((haystack) => [this.value, safeDecodeURIComponent(this.value)].some((haystack) =>
haystack.includes(this.request.originalPathname) haystack.includes(pathname)
) )
); );
} }

View File

@ -10,6 +10,7 @@
"outDir": "lib", "outDir": "lib",
"skipLibCheck": true, "skipLibCheck": true,
"strictNullChecks": true, "strictNullChecks": true,
"strict": true "strict": true,
"alwaysStrict": true
} }
} }

14
util.ts
View File

@ -102,7 +102,7 @@ export function parseToObject(str: unknown): Record<string | symbol, unknown> {
result = str as Record<string | symbol, unknown>; result = str as Record<string | symbol, unknown>;
original_string = (result[Symbol.for('originalString')] as string) || JSON.stringify(str); original_string = (result[Symbol.for('originalString')] as string) || JSON.stringify(str);
} else { } else {
return {}; return result;
} }
result[Symbol.for('originalString')] = original_string; result[Symbol.for('originalString')] = original_string;
return result; return result;
@ -159,7 +159,10 @@ export function toBase64(file: File): Promise<string> {
const FR = new FileReader(); const FR = new FileReader();
FR.addEventListener('load', (e) => { FR.addEventListener('load', (e) => {
const target = e.target; const target = e.target;
target ? resolve(target.result as string) : reject('empty file?'); if (!target) {
return reject('File missing?');
}
resolve(e.target.result as string);
}); });
FR.readAsDataURL(file); FR.readAsDataURL(file);
}); });
@ -228,7 +231,12 @@ export function flattenObject(
flattenObject(value, parser, prefix + subkey, ret); flattenObject(value, parser, prefix + subkey, ret);
} }
} else if (!parsed) { } else if (!parsed) {
flattenObject(parser(obj as { toString: () => string }), parser, key, ret, true); try {
flattenObject(parser(obj as { toString: () => string }), parser, key, ret, true);
} catch (e) {
//emergency case, mostly for just type safety
ret.push([key, JSON.stringify(obj)]);
}
} else if (typeof obj === 'string') { } else if (typeof obj === 'string') {
ret.push([key, obj]); ret.push([key, obj]);
} else { } else {