Add possibility to generate a trimmed HAR file
This commit is contained in:
parent
399b5eca9d
commit
a859d0239f
|
@ -1,25 +1,42 @@
|
||||||
import { StolenDataEntry } from "./stolen-data-entry";
|
import { StolenDataEntry } from "./stolen-data-entry";
|
||||||
import { getshorthost, parseCookie, Request } from "./util";
|
import { getshorthost, parseCookie, Request } from "./util";
|
||||||
|
|
||||||
|
type NameValue = { name: string; value: string };
|
||||||
|
|
||||||
export type HAREntry = {
|
export type HAREntry = {
|
||||||
pageref: string;
|
pageref: string;
|
||||||
startedDateTime: string;
|
startedDateTime: string;
|
||||||
request: {
|
request: {
|
||||||
bodySize: number;
|
bodySize: number;
|
||||||
cookies: {}[];
|
cookies: NameValue[];
|
||||||
headers: {}[];
|
headers: NameValue[];
|
||||||
headersSize: number;
|
headersSize: number;
|
||||||
httpVersion: string;
|
httpVersion: string;
|
||||||
method: string;
|
method: string;
|
||||||
postData: {
|
postData?: {
|
||||||
mimeType: string;
|
mimeType: string;
|
||||||
params: { name: string; value: string }[];
|
params: NameValue[];
|
||||||
text: string;
|
text: string;
|
||||||
};
|
};
|
||||||
queryString: { name: string; value: string }[];
|
queryString: NameValue[];
|
||||||
url: string;
|
url: string;
|
||||||
};
|
};
|
||||||
response: {}; // not relevant
|
response: {
|
||||||
|
status: number;
|
||||||
|
statusText: string;
|
||||||
|
httpVersion: string;
|
||||||
|
headers: NameValue[];
|
||||||
|
cookies: NameValue[];
|
||||||
|
content: {
|
||||||
|
mimeType: string;
|
||||||
|
size: number;
|
||||||
|
encoding: "base64";
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
redirectURL: "";
|
||||||
|
headersSize: number;
|
||||||
|
bodySize: number;
|
||||||
|
}; // not relevant
|
||||||
cache: {};
|
cache: {};
|
||||||
timings: {};
|
timings: {};
|
||||||
time: number;
|
time: number;
|
||||||
|
@ -48,6 +65,13 @@ export default class ExtendedRequest {
|
||||||
public initialized = false;
|
public initialized = false;
|
||||||
public stolenData: StolenDataEntry[];
|
public stolenData: StolenDataEntry[];
|
||||||
|
|
||||||
|
constructor(public data: Request) {
|
||||||
|
this.tabId = data.tabId;
|
||||||
|
this.url = data.url;
|
||||||
|
this.requestHeaders = data.requestHeaders;
|
||||||
|
this.shorthost = getshorthost(data.url);
|
||||||
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
await this.cacheOrigin();
|
await this.cacheOrigin();
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
|
@ -67,6 +91,8 @@ export default class ExtendedRequest {
|
||||||
);
|
);
|
||||||
if (headers.Referer) {
|
if (headers.Referer) {
|
||||||
url = headers.Referer;
|
url = headers.Referer;
|
||||||
|
} else {
|
||||||
|
url = this.data.url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,8 +116,10 @@ export default class ExtendedRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
getReferer() {
|
getReferer() {
|
||||||
return this.data.requestHeaders.filter((h) => h.name === "Referer")[0]
|
return (
|
||||||
.value;
|
this.data.requestHeaders.filter((h) => h.name === "Referer")?.[0].value ||
|
||||||
|
"missing-referrer"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
exposesOrigin() {
|
exposesOrigin() {
|
||||||
|
@ -180,13 +208,6 @@ export default class ExtendedRequest {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(public data: Request) {
|
|
||||||
this.tabId = data.tabId;
|
|
||||||
this.url = data.url;
|
|
||||||
this.requestHeaders = data.requestHeaders;
|
|
||||||
this.shorthost = getshorthost(data.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasMark() {
|
hasMark() {
|
||||||
return this.stolenData.some((data) => data.hasMark());
|
return this.stolenData.some((data) => data.hasMark());
|
||||||
}
|
}
|
||||||
|
@ -204,4 +225,57 @@ export default class ExtendedRequest {
|
||||||
const hrq = har.request;
|
const hrq = har.request;
|
||||||
return rq.url == hrq.url;
|
return rq.url == hrq.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toHAR(): HAREntry {
|
||||||
|
return {
|
||||||
|
pageref: "page_1",
|
||||||
|
startedDateTime: `${new Date().toJSON().replace("Z", "+01:00")}`,
|
||||||
|
request: {
|
||||||
|
bodySize: 0,
|
||||||
|
method: this.data.method,
|
||||||
|
url: this.data.url,
|
||||||
|
headersSize: 100,
|
||||||
|
httpVersion: "HTTP/2",
|
||||||
|
headers: this.data.requestHeaders as NameValue[],
|
||||||
|
cookies: this.getCookieData().map((cookie) => ({
|
||||||
|
name: cookie.name,
|
||||||
|
value: cookie.value,
|
||||||
|
})),
|
||||||
|
queryString: this.getQueryParams().map((param) => ({
|
||||||
|
name: param.name,
|
||||||
|
value: param.value,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
httpVersion: "HTTP/2",
|
||||||
|
headers: [],
|
||||||
|
cookies: [],
|
||||||
|
content: {
|
||||||
|
mimeType: "text/plain",
|
||||||
|
size: 15,
|
||||||
|
encoding: "base64",
|
||||||
|
text: "ZG9lc24ndCBtYXR0ZXIK",
|
||||||
|
},
|
||||||
|
redirectURL: "",
|
||||||
|
headersSize: 15,
|
||||||
|
bodySize: 15,
|
||||||
|
},
|
||||||
|
cache: {},
|
||||||
|
timings: {
|
||||||
|
blocked: -1,
|
||||||
|
dns: 0,
|
||||||
|
connect: 0,
|
||||||
|
ssl: 0,
|
||||||
|
send: 0,
|
||||||
|
wait: 79,
|
||||||
|
receive: 0,
|
||||||
|
},
|
||||||
|
time: 79,
|
||||||
|
_securityState: "secure",
|
||||||
|
serverIPAddress: "31.13.92.36",
|
||||||
|
connection: "443",
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ export default function DomainSummary({
|
||||||
<li>
|
<li>
|
||||||
Właściciel domeny <strong>{cluster.id}</strong> otrzymał:{" "}
|
Właściciel domeny <strong>{cluster.id}</strong> otrzymał:{" "}
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>Mój adres IP</li>
|
||||||
{cluster
|
{cluster
|
||||||
.getMarkedEntries()
|
.getMarkedEntries()
|
||||||
.sort((entryA, entryB) => (entryA.value > entryB.value ? -1 : 1))
|
.sort((entryA, entryB) => (entryA.value > entryB.value ? -1 : 1))
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { HAREntry } from "../extended-request";
|
import { HAREntry } from "../extended-request";
|
||||||
import { StolenDataEntry } from "../stolen-data-entry";
|
import { StolenDataEntry } from "../stolen-data-entry";
|
||||||
|
import { getshorthost, unique } from "../util";
|
||||||
|
|
||||||
function handleNewFile(
|
function handleNewFile(
|
||||||
element: HTMLInputElement,
|
element: HTMLInputElement,
|
||||||
marked_entries: StolenDataEntry[],
|
marked_entries: StolenDataEntry[],
|
||||||
setFiltered: (Blob) => void
|
setFiltered: (Blob) => void
|
||||||
) {
|
): void {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.addEventListener("load", () => {
|
reader.addEventListener("load", () => {
|
||||||
const content = JSON.parse(reader.result as string);
|
const content = JSON.parse(reader.result as string);
|
||||||
|
@ -22,6 +23,35 @@ function handleNewFile(
|
||||||
reader.readAsText(element.files[0]);
|
reader.readAsText(element.files[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateFakeHAR(marked_entries: StolenDataEntry[]) {
|
||||||
|
const requests = marked_entries.map((entry) => entry.request);
|
||||||
|
return {
|
||||||
|
log: {
|
||||||
|
version: "1.2",
|
||||||
|
creator: {
|
||||||
|
name: "Firefox",
|
||||||
|
version: "94.0",
|
||||||
|
},
|
||||||
|
browser: {
|
||||||
|
name: "Firefox",
|
||||||
|
version: "94.0",
|
||||||
|
},
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
startedDateTime: "2021-11-08T20:27:23.195+01:00",
|
||||||
|
id: "page_1",
|
||||||
|
title: "HAR DUmp",
|
||||||
|
pageTimings: {
|
||||||
|
onContentLoad: 467,
|
||||||
|
onLoad: 4226,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
entries: unique(requests).map((r) => r.toHAR()),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default function HARConverter({
|
export default function HARConverter({
|
||||||
marked_entries,
|
marked_entries,
|
||||||
}: {
|
}: {
|
||||||
|
@ -29,6 +59,7 @@ export default function HARConverter({
|
||||||
}) {
|
}) {
|
||||||
const [filtered, setFiltered] = useState<Blob | null>(null);
|
const [filtered, setFiltered] = useState<Blob | null>(null);
|
||||||
const [filename, setFilename] = useState("");
|
const [filename, setFilename] = useState("");
|
||||||
|
const fakeHAR = generateFakeHAR(marked_entries);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
|
@ -48,6 +79,16 @@ export default function HARConverter({
|
||||||
</a>
|
</a>
|
||||||
)) ||
|
)) ||
|
||||||
null}
|
null}
|
||||||
|
<a
|
||||||
|
href={URL.createObjectURL(
|
||||||
|
new Blob([JSON.stringify(fakeHAR)], { type: "application/json" })
|
||||||
|
)}
|
||||||
|
download={`${getshorthost(
|
||||||
|
marked_entries[0].request.originalURL
|
||||||
|
)}-${new Date().toJSON()}-faked.har`}
|
||||||
|
>
|
||||||
|
Pobierz "zfałszowany" HAR
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
32
util.ts
32
util.ts
|
@ -6,9 +6,31 @@ 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;
|
||||||
|
|
||||||
export type Tab = Unarray<Unpromisify<ReturnType<typeof browser.tabs.query>>>;
|
export type Tab = Unarray<Unpromisify<ReturnType<typeof browser.tabs.query>>>;
|
||||||
export type Request = Parameters<
|
export type Request = {
|
||||||
Parameters<typeof browser.webRequest.onBeforeSendHeaders.addListener>[0]
|
cookieStoreId?: string;
|
||||||
>[0];
|
documentUrl?: string; // RL of the document in which the resource will be loaded. For example, if the web page at "https://example.com" contains an image or an iframe, then the documentUrl for the image or iframe will be "https://example.com". For a top-level document, documentUrl is undefined.
|
||||||
|
frameId: number;
|
||||||
|
incognito?: boolean;
|
||||||
|
method: string;
|
||||||
|
originUrl: string;
|
||||||
|
parentFrameId: number;
|
||||||
|
proxyInfo?: {
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
type: string;
|
||||||
|
username: string;
|
||||||
|
proxyDNS: boolean;
|
||||||
|
failoverTimeout: number;
|
||||||
|
};
|
||||||
|
requestHeaders?: { name: string; value?: string; binaryValue?: number[] }[];
|
||||||
|
requestId: string;
|
||||||
|
tabId: number;
|
||||||
|
thirdParty?: boolean;
|
||||||
|
timeStamp: number;
|
||||||
|
type: string;
|
||||||
|
url: string; // the target of the request;
|
||||||
|
urlClassification?: { firstParty: string[]; thirdParty: string[] };
|
||||||
|
};
|
||||||
|
|
||||||
export function getshorthost(host: string) {
|
export function getshorthost(host: string) {
|
||||||
return host
|
return host
|
||||||
|
@ -83,8 +105,8 @@ export function hyphenate(str: string): string {
|
||||||
return str.replace(/[_\[A-Z]/g, `${String.fromCharCode(173)}$&`);
|
return str.replace(/[_\[A-Z]/g, `${String.fromCharCode(173)}$&`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unique(array: string[]) {
|
export function unique<T>(array: T[]): Array<T> {
|
||||||
return Array.from(new Set(array));
|
return Array.from(new Set<T>(array));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function allSubhosts(host: string) {
|
export function allSubhosts(host: string) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user