Repository navigation
Expand file tree
/
Copy pathdashboard.html
More file actions
361 lines (350 loc) · 36.4 KB
/
Copy pathdashboard.html
File metadata and controls
361 lines (350 loc) · 36.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Interceptor</title>
<link rel="icon" href="icons/icon32.png">
<link rel="stylesheet" href="dashboard.css">
<link rel="stylesheet" href="workflow.css">
<link rel="stylesheet" href="lab.css">
<link rel="stylesheet" href="layout.css">
<link rel="stylesheet" href="consent.css">
</head>
<body>
<header class="topbar">
<div class="brand"><img src="icons/icon32.png" alt=""><div>Interceptor<span class="brand-caption">Your HTTP workbench</span></div></div>
<div class="target">
<label for="tabSelect">Target tab</label>
<select id="tabSelect"></select>
<button id="refreshTabs" title="Refresh tab list">↻</button>
<button id="attachBtn" class="primary">Attach</button>
<button id="reloadTabBtn" disabled title="Reload the target tab (bypassing cache) to capture everything from the start">Reload tab</button>
</div>
<label class="chk"><input type="checkbox" id="disableCache"> Disable cache</label>
<span id="status" class="pill off">Not attached</span>
<button id="helpBtn" title="Getting started, permissions, and privacy">Help & privacy</button>
</header>
<nav class="tabs" aria-label="Workbench tools">
<button data-view="intercept" aria-controls="view-intercept" aria-current="page" class="active"><span class="nav-icon" aria-hidden="true">⇄</span>Intercept <span id="queueBadge" class="badge hidden">0</span></button>
<button data-view="history" aria-controls="view-history"><span class="nav-icon" aria-hidden="true">≡</span>HTTP History <span id="historyCount" class="count">0</span></button>
<button data-view="repeater" aria-controls="view-repeater"><span class="nav-icon" aria-hidden="true">↻</span>Repeater</button>
<button data-view="comparer" aria-controls="view-comparer"><span class="nav-icon" aria-hidden="true">◫</span>Comparer <span id="compareBadge" class="count"></span></button>
<button data-view="decoder" aria-controls="view-decoder"><span class="nav-icon" aria-hidden="true">⇆</span>Decoder</button>
<button data-view="sitemap" aria-controls="view-sitemap"><span class="nav-icon" aria-hidden="true">⌗</span>Site Map <span id="siteMapCount" class="count">0</span></button>
<button data-view="inspector" aria-controls="view-inspector"><span class="nav-icon" aria-hidden="true">⊙</span>Inspector</button>
<button data-view="runner" aria-controls="view-runner"><span class="nav-icon" aria-hidden="true">▷</span>Runner</button>
<button data-view="collections" aria-controls="view-collections"><span class="nav-icon" aria-hidden="true">▤</span>Collections</button>
<button data-view="builder" aria-controls="view-builder"><span class="nav-icon" aria-hidden="true">+</span>API Builder</button>
<button data-view="security" aria-controls="view-security"><span class="nav-icon" aria-hidden="true">◇</span>Security Review</button>
<button data-view="websocket" aria-controls="view-websocket"><span class="nav-icon" aria-hidden="true">⇌</span>WebSockets</button>
<button data-view="workspace" aria-controls="view-workspace"><span class="nav-icon" aria-hidden="true">▣</span>Workspace</button>
<span class="local-label"><span></span> Local workspace</span>
</nav>
<main>
<!-- ============ INTERCEPT ============ -->
<section id="view-intercept" class="view active">
<div class="view-heading"><div><h1>Intercept</h1><p>Pause traffic. Make your edit. Keep the flow moving.</p></div><span class="key-hint"><span data-shortcut="Enter">Ctrl + Enter</span> <b>forward</b></span></div>
<div class="toolbar">
<button id="interceptToggle" class="toggle">Intercept is OFF</button>
<button id="fwdBtn" class="primary" disabled data-shortcut-title="Enter" title="Ctrl + Enter">Forward</button>
<button id="fwdRespBtn" disabled title="Forward this request and pause its response too">Forward + catch response</button>
<button id="dropBtn" class="danger" disabled>Drop</button>
<button id="fwdAllBtn" disabled>Forward all</button>
<button id="toRepeaterFromIntercept" disabled>Send to Repeater</button>
<span class="sep"></span>
<label class="chk"><input type="checkbox" id="optRequests" checked> Requests</label>
<label class="chk"><input type="checkbox" id="optResponses"> Responses</label>
<label class="chk"><input type="checkbox" id="optSkipStatic" checked> Skip static files</label>
<input id="optFilter" class="filter" placeholder="Only intercept URLs matching… (text or /regex/)">
</div>
<div id="autoBar" class="auto-bar hidden">
<span class="auto-dot"></span>
<span id="autoBarText"></span>
<span class="sep"></span>
<span class="hint">Change the rule in the toolbar popup (extension icon)</span>
</div>
<div class="split intercept-split">
<aside class="queue">
<div class="pane-title">Queue <span id="queueCount" class="count">0</span></div>
<ul id="queueList"></ul>
<div id="queueEmpty" class="empty">
<p><b>Catch your next request</b></p>
<ol>
<li>Pick your app's tab above and click <b>Attach</b>.</li>
<li>Turn <b>Intercept ON</b>.</li>
<li>Use your app — requests pause here so you can edit, forward or drop them.</li>
</ol>
<p class="hint">Testing the same field repeatedly? Set an Auto rule in the toolbar popup and let it handle the edits.</p>
</div>
<div class="pane-title auto-title">Auto log <span id="autoLogCount" class="count">0</span><button id="autoClearLog" class="mini" title="Clear auto log">clear</button></div>
<ul id="autoLogList" class="autolog"></ul>
<div id="autoLogEmpty" class="empty small">No auto-rewrites yet. Turn on Auto mode in the popup.</div>
</aside>
<div class="editor-pane">
<div class="pane-title"><span id="editorTitle">No intercepted item selected</span><span id="editorHint" class="hint"></span></div>
<div id="editorWarn" class="warn hidden"></div>
<textarea id="interceptEditor" spellcheck="false" disabled placeholder="Intercepted request / response appears here. Edit anything — method, path, headers, body — then Forward."></textarea>
</div>
</div>
</section>
<!-- ============ HISTORY ============ -->
<section id="view-history" class="view">
<div class="view-heading"><div><h1>HTTP History</h1><p>Find the request that matters. Inspect it, replay it, or compare it.</p></div><span class="key-hint">Live traffic + imported HAR</span></div>
<div class="toolbar">
<input id="historyFilter" class="filter wide" placeholder="Filter URL, headers or bodies…">
<select id="historyMethod" aria-label="Filter by request method">
<option value="">All methods</option>
<option>GET</option><option>POST</option><option>PUT</option><option>PATCH</option>
<option>DELETE</option><option>HEAD</option><option>OPTIONS</option>
</select>
<select id="historyStatus" aria-label="Filter by response status">
<option value="">All statuses</option>
<option value="2">2xx</option><option value="3">3xx</option><option value="4">4xx</option><option value="5">5xx</option>
<option value="pending">Pending / failed</option>
</select>
<select id="historyType">
<option value="">All types</option>
<option value="api">Fetch / XHR</option>
<option value="Document">Document</option>
<option value="Script">Script</option>
<option value="Stylesheet">CSS</option>
<option value="Image">Image</option>
<option value="Font">Font</option>
<option value="Other">Other</option>
</select>
<label class="chk"><input type="checkbox" id="historyHideStatic"> Hide static</label>
<label class="chk"><input type="checkbox" id="historySearchContent" checked> Search content</label>
<label class="chk"><input type="checkbox" id="historyAutoScroll" checked> Auto-scroll</label>
<span class="sep"></span>
<button id="importHar">Import HAR</button>
<input id="importHarFile" class="hidden" type="file" accept=".har,application/json">
<button id="exportHar" title="Exports rows matching the current filters">Export visible HAR</button>
<button id="clearHistory" class="danger">Clear</button>
</div>
<div class="history-layout">
<div class="table-wrap" id="historyWrap">
<table id="historyTable">
<thead><tr><th>#</th><th>Method</th><th>Host</th><th>Path</th><th>Status</th><th>Type</th><th>Size</th><th>Time</th></tr></thead>
<tbody></tbody>
</table>
<div id="historyEmpty" class="empty center">Attach to a tab to start recording traffic.</div>
</div>
<div class="detail">
<div class="toolbar small">
<button id="histToRepeater" disabled>Send to Repeater</button>
<button id="histCurl" disabled>Copy as cURL</button>
<button id="histCopyReq" disabled>Copy request</button>
<button id="histCopyResp" disabled>Copy response</button>
<button id="histCompare" disabled>Compare response</button>
<button id="histInspect" disabled>Inspect fields</button>
<button id="histSave" disabled>Save request</button>
<label class="chk"><input type="checkbox" id="prettyJson" checked> Pretty JSON</label>
</div>
<div class="split2">
<div class="pane"><div class="pane-title">Request</div><pre id="histReq" class="raw"></pre></div>
<div class="pane"><div class="pane-title">Response</div><pre id="histRes" class="raw"></pre></div>
</div>
</div>
</div>
</section>
<!-- ============ REPEATER ============ -->
<section id="view-repeater" class="view">
<div class="view-heading"><div><h1>Repeater</h1><p>Tweak a request and see how your server responds.</p></div><span class="key-hint"><span data-shortcut="Enter">Ctrl + Enter</span> <b>send</b></span></div>
<div class="rep-tabs"><div id="repTabList"></div><button id="repNew" title="New repeater tab">+</button></div>
<div class="toolbar">
<button id="repSend" class="primary" data-shortcut-title="Enter" title="Ctrl + Enter">Send</button>
<button id="repCancel" disabled>Cancel</button>
<button id="repImportCurl">Import cURL</button>
<button id="repDuplicate">Duplicate</button>
<button id="repSave">Save</button>
<button id="repInspect">Inspect</button>
<button id="repToRunner">Send to Runner</button>
<button id="repCompare" disabled>Compare response</button>
<label for="repTarget">Target</label>
<input id="repTarget" class="filter" placeholder="https://localhost:3000">
<label for="repTimeout">Timeout</label><input id="repTimeout" type="number" min="1" max="300" value="30" class="number-input" aria-label="Request timeout in seconds"><span class="hint">s</span>
<label class="chk"><input type="checkbox" id="repFollow"> Follow redirects</label>
<label class="chk"><input type="checkbox" id="repPretty" checked> Pretty JSON</label>
<label class="chk hidden"><input type="checkbox" id="repRemember" checked disabled> Remember tabs locally</label>
<span id="repMeta" class="meta"></span>
</div>
<details class="assertion-editor"><summary>Response assertions <span class="hint">status, headers, JSON fields, body, time & size</span></summary><textarea id="repAssertions" spellcheck="false" aria-label="Repeater response assertions">[]</textarea><div id="repTests" class="assertion-results" role="status"></div><p class="hint">Example: [{"type":"status","equals":200},{"type":"json","path":"/ok","equals":true}] · JSON Pointer paths · no scripts executed</p></details>
<div class="split2 grow">
<div class="pane">
<div class="pane-title">Request <span class="hint">edit freely · <span data-shortcut="Enter">Ctrl + Enter</span> to send</span></div>
<textarea id="repEditor" spellcheck="false"></textarea>
</div>
<div class="pane">
<div class="pane-title">
<span class="seg">
<button id="repViewRes" class="active">Response</button>
<button id="repViewSent">Actual request sent</button>
</span>
<select id="repSnapshot" aria-label="Response snapshot"><option value="">Latest response</option></select>
<button id="repComparePrevious" class="mini" disabled>Compare previous</button>
</div>
<pre id="repResponse" class="raw"></pre>
</div>
</div>
</section>
<!-- ============ COMPARER ============ -->
<section id="view-comparer" class="view">
<div class="view-heading"><div><h1>Comparer</h1><p>See what changed between two requests, responses, or documents.</p></div><span class="key-hint">A → B</span></div>
<div class="toolbar">
<button id="compareRun" class="primary">Compare</button>
<button id="compareSwap">Swap A / B</button>
<button id="compareClear" class="danger">Clear</button>
<button id="compareCopy">Copy unified diff</button>
<label class="chk"><input type="checkbox" id="comparePretty" checked> Normalize JSON bodies</label>
<span id="compareMeta" class="meta">Add two responses from HTTP History, or paste any text.</span>
</div>
<div class="compare-inputs">
<div class="pane">
<div class="pane-title"><span>Side A</span><span id="compareLabelA" class="hint">paste or send a history response</span></div>
<textarea id="compareA" spellcheck="false" placeholder="Paste the first request, response, JSON document, or text…"></textarea>
</div>
<div class="pane">
<div class="pane-title"><span>Side B</span><span id="compareLabelB" class="hint">paste or send a history response</span></div>
<textarea id="compareB" spellcheck="false" placeholder="Paste the second value…"></textarea>
</div>
</div>
<div class="compare-result">
<div class="pane-title">Line diff <span class="hint">removed / changed / added</span></div>
<div id="compareEmpty" class="empty center">Add content to both sides to see their differences.</div>
<table id="compareTable" class="diff-table"><tbody></tbody></table>
</div>
</section>
<!-- ============ DECODER ============ -->
<section id="view-decoder" class="view">
<div class="view-heading"><div><h1>Decoder</h1><p>Turn encoded values into something you can read.</p></div><span class="key-hint"><span data-shortcut="Enter">Ctrl + Enter</span> <b>transform</b></span></div>
<div class="toolbar">
<select id="decoderAction" aria-label="Transformation">
<option value="json-pretty">Pretty-print JSON</option>
<option value="json-minify">Minify JSON</option>
<option value="url-decode">URL decode</option>
<option value="url-encode">URL encode</option>
<option value="base64-decode">Base64 / base64url decode</option>
<option value="base64-encode">Base64 encode (UTF-8)</option>
<option value="html-decode">HTML entity decode</option>
<option value="html-encode">HTML entity encode</option>
<option value="jwt-inspect">Inspect JWT (no verification)</option>
<option value="hex-decode">Hex decode (UTF-8)</option>
<option value="hex-encode">Hex encode (UTF-8)</option>
<option value="sha256">SHA-256 hash</option>
<option value="sha512">SHA-512 hash</option>
</select>
<button id="decoderRun" class="primary" data-shortcut-title="Enter" title="Ctrl + Enter">Transform</button>
<button id="decoderSwap">Use output as input</button>
<button id="decoderClear" class="danger">Clear</button>
<button id="decoderCopy">Copy output</button>
<span id="decoderMeta" class="meta">Everything runs locally.</span>
</div>
<div class="split2 grow">
<div class="pane"><div class="pane-title">Input</div><textarea id="decoderInput" spellcheck="false" placeholder="Paste JSON, an encoded value, or a JWT…"></textarea></div>
<div class="pane"><div class="pane-title">Output</div><textarea id="decoderOutput" spellcheck="false" readonly placeholder="Transformed output appears here."></textarea></div>
</div>
</section>
<section id="view-sitemap" class="view">
<div class="view-heading"><div><h1>Site Map</h1><p>A map of the endpoints your app actually uses.</p></div><span class="key-hint">Built from HTTP History</span></div>
<div class="toolbar">
<input id="siteMapFilter" class="filter wide" placeholder="Search origin, method, path, or query field…" aria-label="Search captured endpoints">
<label class="chk"><input id="siteMapHideStatic" type="checkbox" checked> Hide static files</label>
<button id="siteMapRefresh">Refresh</button><button id="siteMapExport">Export endpoint list</button>
<span id="siteMapMeta" class="meta"></span>
</div>
<div class="table-wrap grow"><table id="siteMapTable"><thead><tr><th>Method</th><th>Origin</th><th>Path</th><th>Calls</th><th>Statuses</th><th>Avg. time</th><th>Query fields</th><th></th></tr></thead><tbody></tbody></table>
<div id="siteMapEmpty" class="empty center">Attach to your app or import a HAR file to discover endpoints. No requests are sent by Site Map.</div>
</div>
</section>
<section id="view-inspector" class="view">
<div class="view-heading"><div><h1>Inspector</h1><p>Read query fields, JSON values, headers, and cookie attributes at a glance.</p></div><span class="key-hint">Captured data only</span></div>
<div class="toolbar"><button id="inspectRun" class="primary">Inspect</button><button id="inspectToRepeater">Send to Repeater</button>
<label for="inspectTarget">Target</label><input id="inspectTarget" class="filter" value="http://localhost:3000" aria-label="Inspector request target">
<input id="inspectFilter" class="filter" placeholder="Filter fields…" aria-label="Filter inspected fields"><button id="inspectCopy">Copy fields as JSON</button><span id="inspectMeta" class="meta"></span>
</div>
<div class="workflow-split grow">
<div class="pane inspector-input"><div class="pane-title">Request <span id="inspectLabel" class="hint">paste a raw request or send from History</span></div><textarea id="inspectRequest" spellcheck="false" aria-label="Raw request to inspect" placeholder="GET /api/orders?status=pending HTTP/1.1 Host: localhost:3000 "></textarea>
<div class="pane-title">Response <span class="hint">optional: cookie attributes and header observations</span></div><textarea id="inspectResponse" spellcheck="false" aria-label="Raw response to inspect" placeholder="Paste a response to inspect its headers and Set-Cookie attributes."></textarea></div>
<div class="pane inspector-results"><div class="pane-title">Request fields</div><div class="inspector-scroll"><table id="inspectTable"><thead><tr><th>Location</th><th>Name</th><th>Value</th><th></th></tr></thead><tbody></tbody></table><div id="inspectEmpty" class="empty">Send a captured request here, or paste one on the left.</div>
<div class="pane-title">Response cookies</div><div id="inspectCookies" class="empty small">No response cookies.</div>
<div class="pane-title">Response headers</div><pre id="inspectResponseHeaders" class="raw compact-raw"></pre>
<div class="pane-title">Observations</div><div id="inspectNotes" class="empty small">Inspect a response to see header observations. These are context, not vulnerability findings.</div>
</div></div>
</div>
</section>
<section id="view-runner" class="view">
<div class="view-heading"><div><h1>Runner</h1><p>Try a controlled set of values and compare the responses.</p></div><span class="key-hint">One request at a time · up to 50 payloads</span></div>
<div class="toolbar"><button id="runnerStart" class="primary">Start run</button><button id="runnerStop" disabled>Stop</button><button id="runnerExport">Export results CSV</button><button id="runnerClear">Clear results</button>
<label for="runnerTarget">Target</label><input id="runnerTarget" class="filter" value="http://localhost:3000">
<label for="runnerDelay">Delay</label><input id="runnerDelay" type="number" class="number-input" min="100" max="10000" value="250"><span class="hint">ms</span>
<label for="runnerTimeout">Timeout</label><input id="runnerTimeout" type="number" class="number-input" min="1" max="120" value="15"><span class="hint">s</span>
<span id="runnerMeta" class="meta">Ready</span>
</div>
<div class="runner-plan">
<div class="pane"><div class="pane-title">Request template <span class="hint">replace one value with {{payload}}</span></div><textarea id="runnerRequest" spellcheck="false" aria-label="Runner request template" placeholder="POST /api/validate HTTP/1.1 Host: {{host}} Content-Type: application/json {"amount": {{payload}}}"></textarea></div>
<div class="pane payload-pane"><div class="pane-title">Payloads <span class="hint">one per line; inserted exactly as typed</span></div><textarea id="runnerPayloads" spellcheck="false" aria-label="Payload list" placeholder="0 1 -1 null"></textarea><div class="runner-check"><label for="runnerMatch">Expected response text</label><input id="runnerMatch" placeholder="Optional literal text for each response"><span class="hint">Use quotes in payloads when a JSON value should be a string.</span></div></div>
</div>
<div class="runner-progress"><progress id="runnerProgress" value="0" max="1" aria-label="Run progress"></progress><span id="runnerProgressText">No run yet</span></div>
<details class="assertion-editor"><summary>Assertions & payload presets</summary><textarea id="runnerAssertions" spellcheck="false" aria-label="Runner response assertions">[]</textarea><div class="toolbar small"><select id="runnerPreset" aria-label="Payload preset"><option value="numbers">Numeric boundaries</option><option value="types">JSON type checks</option><option value="strings">String boundaries</option></select><button id="runnerLoadPreset">Load payload preset</button><span class="hint">Replaces the payload list. Assertions run for every response.</span></div><div id="runnerTests" class="assertion-results"></div></details>
<div class="workflow-split runner-results grow"><div class="table-wrap"><table id="runnerTable"><thead><tr><th>#</th><th>Payload</th><th>Status</th><th>Time</th><th>Bytes</th><th>Check</th><th></th></tr></thead><tbody></tbody></table><div id="runnerEmpty" class="empty">Send a request from Repeater, replace a value with <code>{{payload}}</code>, and add the values to try. Starting a run sends every listed request to your chosen target.</div></div><div class="pane"><div class="pane-title">Selected result <button id="runnerCompare" class="mini" disabled>Compare with first</button><button id="runnerToRepeater" class="mini" disabled>Open in Repeater</button></div><pre id="runnerResponse" class="raw"></pre></div></div>
</section>
<section id="view-collections" class="view">
<div class="view-heading"><div><h1>Collections</h1><p>Keep useful requests together and switch targets with reusable variables.</p></div><span class="key-hint">{{variable}} in requests and targets</span></div>
<div class="toolbar"><button id="collectionNew">New request</button><button id="collectionImport">Import collection</button><input id="collectionImportFile" type="file" class="hidden" accept=".json,application/json"><button id="collectionExport">Export collection</button>
<input id="collectionFilter" class="filter" placeholder="Find a saved request…" aria-label="Search collection requests"><label class="chk hidden"><input id="workspaceRemember" type="checkbox" checked disabled> Remember workspace locally</label><span id="collectionMeta" class="meta">Saved on this PC</span></div>
<div class="workflow-split grow"><aside class="collection-sidebar"><div class="pane-title">Requests <span id="collectionCount" class="count">0</span></div><div id="collectionList" class="collection-list"></div><div id="collectionEmpty" class="empty">Save a request from History or Repeater to build a collection.</div>
<div class="pane-title">Variables</div><textarea id="workspaceVariables" spellcheck="false" aria-label="Workspace variables" placeholder="baseUrl=https://localhost:3000 host=localhost:3000 token=your-test-token"></textarea><p class="empty small">Use <code>{{baseUrl}}</code>, <code>{{host}}</code>, or your own names. Values are inserted literally. Missing variables block sending.</p></aside>
<div class="pane collection-editor"><div class="toolbar small"><label for="collectionName">Name</label><input id="collectionName" placeholder="Request name"><label for="collectionFolder">Folder</label><input id="collectionFolder" placeholder="General"><button id="collectionOpen" class="primary" disabled>Open in Repeater</button><button id="collectionDelete" class="danger" disabled>Delete</button></div>
<div class="toolbar small"><label for="collectionTarget">Target</label><input id="collectionTarget" class="filter wide" placeholder="{{baseUrl}}"><label class="chk"><input id="collectionFollow" type="checkbox"> Follow redirects</label></div>
<div class="pane-title">Saved request <span class="hint">edits are kept automatically</span></div><textarea id="collectionRaw" spellcheck="false" disabled aria-label="Saved raw request" placeholder="Choose a saved request to edit it."></textarea>
<div class="pane-title">Notes</div><textarea id="collectionNotes" class="collection-notes" spellcheck="false" disabled aria-label="Saved request notes" placeholder="Expected behavior, test context, or reproduction steps…"></textarea>
<details class="assertion-editor"><summary>Saved response assertions</summary><textarea id="collectionAssertions" spellcheck="false" aria-label="Saved response assertions">[]</textarea></details>
</div>
</div>
</section>
<section id="view-builder" class="view">
<div class="view-heading"><div><h1>API Builder</h1><p>Build an API request, add authentication, and check the response.</p></div><span class="key-hint">Local first · {{variables}} supported</span></div>
<div class="toolbar"><select id="builderMethod" aria-label="HTTP method"><option>GET</option><option>POST</option><option>PUT</option><option>PATCH</option><option>DELETE</option><option>HEAD</option><option>OPTIONS</option></select><input id="builderUrl" class="filter wide" placeholder="https://localhost:3000/api/health" aria-label="Request URL"><button id="builderSend" class="primary">Send request</button><button id="builderCancel" disabled>Cancel</button><button id="builderReplay">Open in Repeater</button><button id="builderSave">Save request</button><span id="builderMeta" class="meta">Ready</span></div>
<div class="lab-grid grow"><div class="pane builder-form">
<label for="builderAuth">Authentication</label><select id="builderAuth"><option value="none">None</option><option value="bearer">Bearer token</option><option value="basic">Basic auth</option><option value="apikey">API key header</option></select>
<div id="builderAuthFields" class="lab-grid"><label><span id="builderAuthNameLabel">Username / header name</span><input id="builderAuthName" autocomplete="off" spellcheck="false"></label><label>Token / password / key<input id="builderAuthValue" type="password" autocomplete="off" spellcheck="false" placeholder="{{token}}"></label></div>
<label for="builderHeaders">Headers <span class="hint">one Name: value per line</span></label><textarea id="builderHeaders" spellcheck="false" placeholder="Accept: application/json"></textarea>
<label for="builderBodyMode">Body</label><select id="builderBodyMode"><option value="none">No body</option><option value="json">JSON</option><option value="form">Form fields (name=value per line)</option><option value="raw">Raw text</option></select><textarea id="builderBody" spellcheck="false" placeholder="Request body…"></textarea>
<details class="assertion-editor"><summary>Response assertions</summary><textarea id="builderAssertions" spellcheck="false" aria-label="Builder assertions">[{"type":"status","equals":200}]</textarea><p class="hint">Types: status, header, json, bodyContains, time, size. Open Repeater for examples.</p></details>
</div><div class="pane"><div class="pane-title">Response <button id="builderInspect" class="mini">Inspect</button><button id="builderCurl" class="mini">Copy cURL</button></div><div id="builderTests" class="assertion-results" role="status"></div><pre id="builderResponse" class="raw"></pre></div></div>
</section>
<section id="view-security" class="view">
<div class="view-heading"><div><h1>Security Review</h1><p>Review captured evidence, then verify behavior with a controlled comparison.</p></div><span class="key-hint">Observations need context</span></div>
<div class="toolbar"><button id="securityScan" class="primary">Review HTTP History</button><select id="securityFilter" aria-label="Finding filter"><option value="open">Open observations</option><option value="all">All observations</option><option value="review">Needs review</option><option value="info">Hardening context</option><option value="ignored">Ignored</option></select><button id="securityExport">Export report</button><span id="securityMeta" class="meta">Review is passive; it sends no requests.</span></div>
<details class="auth-comparison"><summary>Compare a request with and without credential headers</summary><p>Select a captured request, or use the active Repeater tab. This sends <strong>two requests</strong> to the same origin with redirects disabled. The second omits Authorization, Cookie and API key headers. Credentials in the URL or body remain; inspect those separately.</p><div class="toolbar small"><select id="securitySource" aria-label="Comparison request source"><option value="repeater">Active Repeater tab</option></select><button id="securityCompare">Send both & compare</button><button id="securityStop" disabled>Stop</button><button id="securityOpenDiff" disabled>Open response diff</button></div><pre id="securityComparison" class="raw compact"></pre></details>
<div id="securityFindings" class="findings-list grow"></div>
</section>
<section id="view-websocket" class="view">
<div class="view-heading"><div><h1>WebSockets</h1><p>Inspect messages sent and received by the attached browser tab.</p></div><span class="key-hint">Passive · no message injection</span></div>
<div class="toolbar"><input id="wsFilter" class="filter" placeholder="Filter URL or message…" aria-label="Filter WebSocket frames"><button id="wsExport">Export frames</button><button id="wsClear">Clear frames</button><span id="wsMeta" class="meta">Attach a tab in Intercept to capture frames.</span></div>
<div class="workflow-split grow"><div class="table-wrap"><table id="wsTable"><thead><tr><th>#</th><th>Direction</th><th>Socket</th><th>Type</th><th>Preview</th></tr></thead><tbody></tbody></table><div id="wsEmpty" class="empty">Live WebSocket messages appear here while a tab is attached. Up to 1,000 frames; each preview is capped at 20,000 characters.</div></div><div class="pane"><div class="pane-title">Selected frame <button id="wsDecode" class="mini">Open in Decoder</button></div><pre id="wsData" class="raw"></pre></div></div>
</section>
<section id="view-workspace" class="view">
<div class="view-heading"><div><h1>Workspace</h1><p>Your requests, history, responses, and results. Saved on this PC.</p></div><span id="localSaveStatus" class="key-hint" role="status">Opening local database…</span></div>
<div class="workspace-cards"><article class="workspace-card"><h2>Local storage</h2><p>Work is saved automatically in this browser profile’s local database. No account, cloud sync, or telemetry. Stored requests and backups can contain cookies, tokens, and personal data.</p><div id="workspaceCounts" class="workspace-counts"></div><button id="workspaceSaveNow">Save now</button><button id="workspaceUndoRestore">Undo last restore</button><h2>Pane layout</h2><p>Drag a divider to resize panes. Focus it and use arrow keys, or double-click to reset that split. Sizes are saved locally for wide and stacked views.</p><button id="layoutReset">Reset all pane sizes</button><p id="layoutStatus" class="hint" role="status"></p></article>
<article class="workspace-card"><h2>Download & restore everything</h2><p>Back up history, requests, response snapshots, collections, settings, environments, tool drafts, findings, frames, and run results. Live debugger sessions are started manually.</p><label for="backupPassword">Backup password <span class="hint">optional; 8+ characters encrypts the file</span></label><input id="backupPassword" type="password" autocomplete="off" placeholder="Leave blank for an unencrypted backup"><div class="toolbar small"><button id="backupDownload" class="primary">Download full backup</button><button id="backupImport">Import backup</button><input id="backupFile" type="file" accept=".json,.jsonl,application/json,application/x-ndjson" class="hidden"></div><p class="hint">Use the same password to import an encrypted backup. Passwords are never saved. New backups use a bounded chunk format; older JSON backups still import.</p><p id="backupStatus" class="hint" role="status"></p><div id="backupPreview" class="hidden"><p id="backupSummary"></p><button id="backupRestore" class="danger">Replace workspace with this backup</button><button id="backupDismiss">Cancel import</button></div></article>
<article class="workspace-card environment-card"><h2>Environments</h2><p>Switch between local, staging, and other test accounts. Active environment values override the shared variables in Collections.</p><div class="toolbar small"><select id="environmentSelect" aria-label="Active environment"><option value="">Shared variables only</option></select><button id="environmentNew">New environment</button><button id="environmentDelete">Delete</button></div><label for="environmentName">Name</label><input id="environmentName" maxlength="100"><label for="environmentVariables">Variables <span class="hint">name=value per line</span></label><textarea id="environmentVariables" spellcheck="false" placeholder="baseUrl=http://localhost:3000 token=your-test-token"></textarea><p id="environmentMeta" class="hint"></p></article><article class="workspace-card environment-card"><h2>Recovered paused edits</h2><p>Saved text from requests or responses paused in a previous session. These drafts do not restart interception or send traffic.</p><div id="recoveredDrafts"></div></article></div>
</section>
</main>
<dialog id="curlDialog" class="workbench-dialog"><form method="dialog"><div class="dialog-title"><h2>Import cURL</h2><button aria-label="Close cURL import">×</button></div></form><p>Paste a single command copied from DevTools. It is parsed locally; importing does not send it.</p><textarea id="curlInput" spellcheck="false" aria-label="cURL command" placeholder="curl 'https://localhost:3000/api/orders' -H 'Accept: application/json'"></textarea><p id="curlError" class="error-text" role="status"></p><button id="curlImportRun" class="primary">Import into Repeater</button></dialog>
<dialog id="toolDialog" class="workbench-dialog tool-dialog"><form method="dialog"><div class="dialog-title"><h2>Go to a tool</h2><button aria-label="Close tool switcher">×</button></div></form><input id="toolSearch" placeholder="Search tools…" aria-label="Search workbench tools"><div id="toolList"></div><p class="hint"><span data-shortcut="K">Ctrl + K</span> to open · Esc to close</p></dialog>
<div id="toast" class="toast" role="status" aria-live="polite"></div>
<script src="consent.js"></script>
<script src="http.js"></script>
<script src="workbench.js"></script>
<script src="workflow.js"></script>
<script src="lab.js"></script>
<script src="backup.js"></script>
<script src="local-store.js"></script>
<script src="layout.js"></script>
<script src="dashboard.js"></script>
<script src="workflow-ui.js"></script>
<script src="lab-ui.js"></script>
</body>
</html>