Skip to content

Commit dfe05ff

Browse files
committed
feat(new tool): HAR file sanitizer
Fix CorentinTh#811
1 parent e0ec52b commit dfe05ff

8 files changed

Lines changed: 446 additions & 0 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@
277277
"@types/figlet": "^1.7.0",
278278
"@types/js-beautify": "^1.14.3",
279279
"@types/hex-array": "^1.0.2",
280+
"@types/har-format": "^1.2.16",
280281
"@types/jsdom": "^21.1.7",
281282
"@types/jsonpath": "^0.2.4",
282283
"@types/libmime": "^5.0.3",

pnpm-lock.yaml

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<script setup lang="ts">
2+
import { downloadFile } from './lib/downloadFile';
3+
import { defaultScrubItems, getHarInfo, sanitize } from './lib/har_sanitize';
4+
5+
type ScrubState = Record<ScrubType, Record<string, boolean>>;
6+
type ScrubType =
7+
| 'cookies'
8+
| 'headers'
9+
| 'queryArgs'
10+
| 'postParams'
11+
| 'mimeTypes';
12+
13+
const typeMap: Record<ScrubType, string> = {
14+
cookies: 'Cookies',
15+
mimeTypes: 'Mime Types',
16+
headers: 'Headers',
17+
postParams: 'Post Body Params',
18+
queryArgs: 'Query String Parameters',
19+
};
20+
21+
const defaulScrubState: ScrubState = {
22+
cookies: {},
23+
headers: {},
24+
queryArgs: {},
25+
postParams: {},
26+
mimeTypes: {},
27+
};
28+
const scrubItemsToClean = ref<ScrubState>(defaulScrubState);
29+
30+
function getScrubableItems(input: string): ScrubState {
31+
const rawItems = getHarInfo(input);
32+
const output = { ...defaulScrubState };
33+
Object.entries(rawItems).forEach(([key, items]: [string, string[]]) => {
34+
output[key as ScrubType] = items.reduce(
35+
(acc, curr) => {
36+
if (!curr) {
37+
return acc;
38+
}
39+
acc[curr] = defaultScrubItems.includes(curr);
40+
return acc;
41+
},
42+
{} as Record<string, boolean>,
43+
);
44+
return null;
45+
});
46+
return output;
47+
}
48+
49+
function sanitizeHar(input: string, scrubItems: ScrubState) {
50+
const words = new Set<string>();
51+
Object.entries(scrubItems.cookies).forEach(([key, val]) => {
52+
if (val) {
53+
words.add(key);
54+
}
55+
});
56+
Object.entries(scrubItems.headers).forEach(([key, val]) => {
57+
if (val) {
58+
words.add(key);
59+
}
60+
});
61+
Object.entries(scrubItems.queryArgs).forEach(([key, val]) => {
62+
if (val) {
63+
words.add(key);
64+
}
65+
});
66+
Object.entries(scrubItems.postParams).forEach(([key, val]) => {
67+
if (val) {
68+
words.add(key);
69+
}
70+
});
71+
72+
const mimeTypes = new Set<string>();
73+
Object.entries(scrubItems.mimeTypes).forEach(([key, val]) => {
74+
if (val) {
75+
mimeTypes.add(key);
76+
}
77+
});
78+
return sanitize(input, {
79+
scrubWords: [...words],
80+
scrubMimetypes: [...mimeTypes],
81+
});
82+
}
83+
84+
const file = ref<File | null>(null);
85+
86+
const error = ref('');
87+
88+
function readAsTextAsync(file: File) {
89+
return new Promise<string>((resolve, reject) => {
90+
const reader = new FileReader();
91+
reader.readAsText(file);
92+
reader.onload = () => resolve(reader.result?.toString() ?? '');
93+
reader.onerror = error => reject(error);
94+
});
95+
}
96+
97+
const harContent = ref('');
98+
async function onFileUploaded(uploadedFile: File) {
99+
file.value = uploadedFile;
100+
harContent.value = await readAsTextAsync(uploadedFile);
101+
102+
error.value = '';
103+
try {
104+
scrubItemsToClean.value = getScrubableItems(harContent.value);
105+
}
106+
catch (e: any) {
107+
error.value = e.toString();
108+
}
109+
}
110+
111+
function processHar() {
112+
downloadFile(sanitizeHar(harContent.value, scrubItemsToClean.value), `sanitized-${file.value?.name}`);
113+
}
114+
</script>
115+
116+
<template>
117+
<div>
118+
<div style="flex: 0 0 100%" mb-3>
119+
<div mx-auto max-w-600px>
120+
<c-file-upload
121+
title="Drag and drop a HAR file here, or click to select a file"
122+
accept=".har" @file-upload="onFileUploaded"
123+
/>
124+
</div>
125+
</div>
126+
127+
<c-alert v-if="error" title="Error">
128+
{{ error }}
129+
</c-alert>
130+
131+
<div v-for="(title, key) in typeMap" :key="key" mb-1>
132+
<c-card v-if="Object.keys(scrubItemsToClean[key]).length" :title="title">
133+
<n-checkbox font-size-5 @update:checked="(allChecked: boolean) => Object.keys(scrubItemsToClean[key]).forEach((name) => scrubItemsToClean[key][name] = allChecked)">
134+
All {{ title }}
135+
</n-checkbox>
136+
<n-space size="large">
137+
<n-checkbox v-for="(checked, name) in scrubItemsToClean[key]" :key="name" v-model:checked="scrubItemsToClean[key][name]" style="width: 150px">
138+
{{ name }}
139+
</n-checkbox>
140+
</n-space>
141+
</c-card>
142+
</div>
143+
144+
<div v-if="!error" mt-3 flex justify-center>
145+
<c-button @click="processHar()">
146+
Sanitize and download
147+
</c-button>
148+
</div>
149+
</div>
150+
</template>

src/tools/har-sanitizer/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { ClearFormatting } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'HAR Sanitizer',
6+
path: '/har-sanitizer',
7+
description: 'HAR Files Sanitizer',
8+
keywords: ['har', 'sanitizer'],
9+
component: () => import('./har-sanitizer.vue'),
10+
icon: ClearFormatting,
11+
createdAt: new Date('2024-06-17'),
12+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export function downloadFile(harOutput: string, name: string) {
2+
const blob = new Blob([harOutput], { type: 'application/json' });
3+
4+
// Create a URL for the Blob
5+
const url = URL.createObjectURL(blob);
6+
7+
// Create an anchor element to trigger the download
8+
const a = document.createElement('a');
9+
a.href = url;
10+
// Set file name
11+
a.download = name;
12+
a.style.display = 'none';
13+
document.body.appendChild(a);
14+
a.click();
15+
16+
// Clean up by removing the anchor and revoking the URL
17+
document.body.removeChild(a);
18+
URL.revokeObjectURL(url);
19+
}

0 commit comments

Comments
 (0)