From 416a6aa340055c2a7a542bc51a15cf9ad2e3c748 Mon Sep 17 00:00:00 2001 From: Kuba Orlik Date: Sat, 9 Jul 2022 15:51:34 +0200 Subject: [PATCH] Fix typechecks. Closes #54 --- components/report-window/email-content.tsx | 1 + components/report-window/har-converter.tsx | 2 +- components/report-window/report-window.tsx | 5 +- components/sidebar/sidebar.tsx | 1 + components/toolbar/toolbar.tsx | 7 +- extended-request.ts | 92 +++++++++++----------- memory.ts | 4 +- request-cluster.ts | 3 +- stolen-data-entry.ts | 14 ++-- tsconfig.json | 3 +- util.ts | 14 +++- 11 files changed, 82 insertions(+), 64 deletions(-) diff --git a/components/report-window/email-content.tsx b/components/report-window/email-content.tsx index eb99062..803ffa2 100644 --- a/components/report-window/email-content.tsx +++ b/components/report-window/email-content.tsx @@ -25,6 +25,7 @@ export default function EmailContent({ downloadFiles: Function; user_role: string; }) { + console.log('rendering email!', answers); const _ = (key: string) => v(key, answers.zaimek); const problems = deduceProblems(answers, clusters); const explainers = Array.from( diff --git a/components/report-window/har-converter.tsx b/components/report-window/har-converter.tsx index ed0e873..0384c72 100644 --- a/components/report-window/har-converter.tsx +++ b/components/report-window/har-converter.tsx @@ -105,7 +105,7 @@ export default function HARConverter({ entries }: { entries: StolenDataEntry[] } }) )} download={`${getshorthost( - entries[0].request.originalURL + entries[0].request.origin )}-${new Date().toJSON()}-trimmed.har`} > Pobierz "zredukowany" HAR diff --git a/components/report-window/report-window.tsx b/components/report-window/report-window.tsx index a9b1db3..c51a7ff 100644 --- a/components/report-window/report-window.tsx +++ b/components/report-window/report-window.tsx @@ -21,6 +21,9 @@ function Report() { try { const url = new URL(document.location.toString()); const origin = url.searchParams.get('origin'); + if (!origin) { + return
Błąd: brak parametru "origin"
; + } const [counter] = useEmitter(getMemory()); const rawAnswers = url.searchParams.get('answers'); const [answers, setAnswers] = React.useState( @@ -40,7 +43,7 @@ function Report() { history.pushState({}, 'Rentgen', url.toString()); }, [mode, answers, origin]); 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; if (!visited_url) { diff --git a/components/sidebar/sidebar.tsx b/components/sidebar/sidebar.tsx index 36744a9..6fdd0de 100644 --- a/components/sidebar/sidebar.tsx +++ b/components/sidebar/sidebar.tsx @@ -1,5 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; + import { getMemory } from '../../memory'; import Options from '../../options'; import { useEmitter } from '../../util'; diff --git a/components/toolbar/toolbar.tsx b/components/toolbar/toolbar.tsx index 73342fd..922a031 100644 --- a/components/toolbar/toolbar.tsx +++ b/components/toolbar/toolbar.tsx @@ -31,8 +31,7 @@ const Toolbar = () => { const listener = async () => { const tab = await getCurrentTab(); - if (tab !== undefined) { - if (!tab.url) return; + if (tab !== undefined && tab.url) { const url = new URL(tab.url); if (url.origin.startsWith('moz-extension')) { return; @@ -51,6 +50,7 @@ const Toolbar = () => { }); React.useEffect(() => { + if (!origin) return; const exposedOriginDomains = Object.values(getMemory().getClustersForOrigin(origin)) .filter((cluster) => cluster.exposesOrigin()) .map((cluster) => cluster.id); @@ -83,6 +83,7 @@ const Toolbar = () => { }, [eventCounts['*'], origin]); React.useEffect(() => { + if (!origin) return; const cookieDomains = Object.values(getMemory().getClustersForOrigin(origin)) .filter((cluster) => cluster.hasCookies()) .map((cluster) => cluster.id); @@ -90,7 +91,7 @@ const Toolbar = () => { switch (cookieDomains.length) { case 0: - null; + break; case 1: setCookieDomainCopy(`${cookieDomains[0]}.`); break; diff --git a/extended-request.ts b/extended-request.ts index ac9efa9..3d4320d 100644 --- a/extended-request.ts +++ b/extended-request.ts @@ -1,3 +1,4 @@ +'use strict'; import { StolenDataEntry } from './stolen-data-entry'; import { flattenObjectEntries, @@ -76,11 +77,12 @@ export default class ExtendedRequest { public url: string; public shorthost: string; public requestHeaders: { name: string; value?: string; binaryValue?: number[] }[] = []; - public originalURL: string | null = null; public origin: string; public initialized = false; 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; static by_id = {} as Record; @@ -95,9 +97,35 @@ export default class ExtendedRequest { this.data = Object.assign({}, data); (this.data as any).frameAncestors = [ - ...(data as any).frameAncestors.map((e: any) => ({ url: e.url })), - ]; - this.origin = this.cacheOrigin(); + ...((data as any)?.frameAncestors?.map((e: any) => ({ url: e.url })) || []), + ]; // making a copy? + + // 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']) { @@ -106,50 +134,20 @@ export default class ExtendedRequest { } init() { - this.cacheOrigin(); this.initialized = true; 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() { const request_url = new URL(this.data.url); - const origin_url = new URL(this.originalURL); - if (request_url.host.includes(origin_url.host)) { + if (request_url.host.includes(this.originalHost)) { return false; } - if (getshorthost(request_url.host) == getshorthost(origin_url.host)) { + if (getshorthost(request_url.host) == getshorthost(this.originalHost)) { return false; } return ( - request_url.origin != origin_url.origin || + request_url.origin != this.origin || (this.data as any).urlClassification.thirdParty.length > 0 ); } @@ -161,9 +159,8 @@ export default class ExtendedRequest { } exposesOrigin() { - const url = new URL(this.originalURL); - const host = url.host; - const path = url.pathname; + const host = this.originalHost; + const path = this.originalPathname || '/'; const shorthost = getshorthost(host); if (this.getReferer().includes(shorthost)) { return true; @@ -215,7 +212,10 @@ export default class ExtendedRequest { if ((Array.isArray(value) && value.length === 1 && !value[0]) || !value) { return ['requestBody', key]; } 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 { return [key, value || '']; } @@ -234,7 +234,7 @@ export default class ExtendedRequest { } getCookie(): string { - return this.requestHeaders.find((h) => h.name == 'Cookie')?.value; + return this.requestHeaders.find((h) => h.name == 'Cookie')?.value || ''; } getPathParams(): StolenDataEntry[] { @@ -257,8 +257,8 @@ export default class ExtendedRequest { getQueryParams(): StolenDataEntry[] { const url = new URL(this.data.url); return flattenObjectEntries( - Array.from((url.searchParams as any).entries()) - .map(([key, value]) => [key, value || '']) + (Array.from((url.searchParams as any).entries()) as [string, string][]) + .map(([key, value]: [string, string]) => [key, value || '']) .map(([key, value]) => { return [key, StolenDataEntry.parseValue(safeDecodeURIComponent(value))]; }) @@ -281,7 +281,7 @@ export default class ExtendedRequest { .map((header) => { return [ header.name, - StolenDataEntry.parseValue(safeDecodeURIComponent(header.value)), + StolenDataEntry.parseValue(safeDecodeURIComponent(header.value || '')), ]; }) ).map(([key, value]) => new StolenDataEntry(this, 'header', key, value)); diff --git a/memory.ts b/memory.ts index 2c6cb6f..7b678aa 100644 --- a/memory.ts +++ b/memory.ts @@ -1,5 +1,5 @@ import ExtendedRequest from './extended-request'; -import { getshorthost, makeThrottle } from './util'; +import { getshorthost } from './util'; import { RequestCluster } from './request-cluster'; import { SaferEmitter } from './safer-emitter'; @@ -67,7 +67,7 @@ export default class Memory extends SaferEmitter { emit(eventName: string, data = 'any'): boolean { setTimeout(() => super.emit(eventName, data), 0); - return; + return true; } getClustersForOrigin(origin: string): Record { diff --git a/request-cluster.ts b/request-cluster.ts index ca4e4c6..0e4b9a8 100644 --- a/request-cluster.ts +++ b/request-cluster.ts @@ -9,12 +9,13 @@ const source_priority: Array = ['cookie', 'pathname', 'queryparams', 'h export class RequestCluster extends SaferEmitter { public requests: ExtendedRequest[] = []; public representativeStolenData: StolenDataEntry[] = []; - public expanded: boolean; + public expanded: boolean = false; public lastModified: number = 0; public lastFullUrl: string | null = null; constructor(public id: string) { super(); } + add(request: ExtendedRequest) { this.requests.push(request); this.emit('change'); diff --git a/stolen-data-entry.ts b/stolen-data-entry.ts index 0e03af7..6c110a2 100644 --- a/stolen-data-entry.ts +++ b/stolen-data-entry.ts @@ -1,4 +1,3 @@ -import { EventEmitter } from 'events'; import ExtendedRequest, { HAREntry } from './extended-request'; import { SaferEmitter } from './safer-emitter'; @@ -59,11 +58,10 @@ export class StolenDataEntry extends SaferEmitter { getPriority() { let priority = 0; priority += Math.min(this.value.length, 50); - const url = new URL(this.request.originalURL); - if (this.value.includes(url.host)) { + if (this.value.includes(this.request.originalHost)) { priority += 100; } - if (this.value.includes(url.pathname)) { + if (this.request.originalPathname && this.value.includes(this.request.originalPathname)) { priority += 100; } if (this.source === 'cookie') { @@ -133,7 +131,7 @@ export class StolenDataEntry extends SaferEmitter { } else if (value === null) { return 'null'; } else { - return value.toString(); + return (value as any).toString(); } } @@ -238,10 +236,14 @@ export class StolenDataEntry extends SaferEmitter { } exposesPath() { + const pathname = this.request.originalPathname; + if (pathname === null) { + return false; + } return ( this.request.originalPathname !== '/' && [this.value, safeDecodeURIComponent(this.value)].some((haystack) => - haystack.includes(this.request.originalPathname) + haystack.includes(pathname) ) ); } diff --git a/tsconfig.json b/tsconfig.json index f2b501f..01706cb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "outDir": "lib", "skipLibCheck": true, "strictNullChecks": true, - "strict": true + "strict": true, + "alwaysStrict": true } } diff --git a/util.ts b/util.ts index ffb976b..516c11d 100644 --- a/util.ts +++ b/util.ts @@ -102,7 +102,7 @@ export function parseToObject(str: unknown): Record { result = str as Record; original_string = (result[Symbol.for('originalString')] as string) || JSON.stringify(str); } else { - return {}; + return result; } result[Symbol.for('originalString')] = original_string; return result; @@ -159,7 +159,10 @@ export function toBase64(file: File): Promise { const FR = new FileReader(); FR.addEventListener('load', (e) => { 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); }); @@ -228,7 +231,12 @@ export function flattenObject( flattenObject(value, parser, prefix + subkey, ret); } } 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') { ret.push([key, obj]); } else {