bidoytu 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bidoytu-1.1.0/LICENSE +21 -0
- bidoytu-1.1.0/PKG-INFO +198 -0
- bidoytu-1.1.0/README.md +160 -0
- bidoytu-1.1.0/pyproject.toml +62 -0
- bidoytu-1.1.0/setup.cfg +4 -0
- bidoytu-1.1.0/src/bidoytu/__init__.py +11 -0
- bidoytu-1.1.0/src/bidoytu/__main__.py +5 -0
- bidoytu-1.1.0/src/bidoytu/app.py +65 -0
- bidoytu-1.1.0/src/bidoytu/assets/logo.ico +0 -0
- bidoytu-1.1.0/src/bidoytu/assets/logo.png +0 -0
- bidoytu-1.1.0/src/bidoytu/config.py +132 -0
- bidoytu-1.1.0/src/bidoytu/http_utils.py +218 -0
- bidoytu-1.1.0/src/bidoytu/net/__init__.py +5 -0
- bidoytu-1.1.0/src/bidoytu/net/async_sender.py +190 -0
- bidoytu-1.1.0/src/bidoytu/net/attack.py +228 -0
- bidoytu-1.1.0/src/bidoytu/net/interactsh.py +498 -0
- bidoytu-1.1.0/src/bidoytu/net/payloads.py +288 -0
- bidoytu-1.1.0/src/bidoytu/proxy/__init__.py +5 -0
- bidoytu-1.1.0/src/bidoytu/proxy/capture_addon.py +245 -0
- bidoytu-1.1.0/src/bidoytu/proxy/engine.py +213 -0
- bidoytu-1.1.0/src/bidoytu/resources.py +28 -0
- bidoytu-1.1.0/src/bidoytu/storage/__init__.py +7 -0
- bidoytu-1.1.0/src/bidoytu/storage/body_store.py +46 -0
- bidoytu-1.1.0/src/bidoytu/storage/models.py +119 -0
- bidoytu-1.1.0/src/bidoytu/storage/repository.py +176 -0
- bidoytu-1.1.0/src/bidoytu/ui/__init__.py +1 -0
- bidoytu-1.1.0/src/bidoytu/ui/attack_runner.py +233 -0
- bidoytu-1.1.0/src/bidoytu/ui/body_format.py +429 -0
- bidoytu-1.1.0/src/bidoytu/ui/ca_dialog.py +130 -0
- bidoytu-1.1.0/src/bidoytu/ui/collaborator_model.py +201 -0
- bidoytu-1.1.0/src/bidoytu/ui/collaborator_tab.py +722 -0
- bidoytu-1.1.0/src/bidoytu/ui/detail_view.py +120 -0
- bidoytu-1.1.0/src/bidoytu/ui/find_bar.py +121 -0
- bidoytu-1.1.0/src/bidoytu/ui/flow_layout.py +96 -0
- bidoytu-1.1.0/src/bidoytu/ui/flow_table_model.py +119 -0
- bidoytu-1.1.0/src/bidoytu/ui/group_dialog.py +150 -0
- bidoytu-1.1.0/src/bidoytu/ui/highlighter.py +73 -0
- bidoytu-1.1.0/src/bidoytu/ui/history_delegate.py +82 -0
- bidoytu-1.1.0/src/bidoytu/ui/history_view.py +278 -0
- bidoytu-1.1.0/src/bidoytu/ui/intercept_activity_model.py +204 -0
- bidoytu-1.1.0/src/bidoytu/ui/intercept_view.py +716 -0
- bidoytu-1.1.0/src/bidoytu/ui/intruder_results_model.py +183 -0
- bidoytu-1.1.0/src/bidoytu/ui/intruder_session.py +834 -0
- bidoytu-1.1.0/src/bidoytu/ui/intruder_tab.py +185 -0
- bidoytu-1.1.0/src/bidoytu/ui/main_window.py +403 -0
- bidoytu-1.1.0/src/bidoytu/ui/marker_highlighter.py +47 -0
- bidoytu-1.1.0/src/bidoytu/ui/message_view.py +245 -0
- bidoytu-1.1.0/src/bidoytu/ui/payload_editor.py +363 -0
- bidoytu-1.1.0/src/bidoytu/ui/proxy_tab.py +136 -0
- bidoytu-1.1.0/src/bidoytu/ui/repeater_session.py +707 -0
- bidoytu-1.1.0/src/bidoytu/ui/repeater_tab.py +573 -0
- bidoytu-1.1.0/src/bidoytu/ui/theme.py +843 -0
- bidoytu-1.1.0/src/bidoytu/ui/wrapping_tab_bar.py +361 -0
- bidoytu-1.1.0/src/bidoytu.egg-info/PKG-INFO +198 -0
- bidoytu-1.1.0/src/bidoytu.egg-info/SOURCES.txt +57 -0
- bidoytu-1.1.0/src/bidoytu.egg-info/dependency_links.txt +1 -0
- bidoytu-1.1.0/src/bidoytu.egg-info/entry_points.txt +2 -0
- bidoytu-1.1.0/src/bidoytu.egg-info/requires.txt +10 -0
- bidoytu-1.1.0/src/bidoytu.egg-info/top_level.txt +1 -0
bidoytu-1.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bidoytu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
bidoytu-1.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bidoytu
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: An intercepting HTTP proxy tool built with PySide6 and mitmproxy.
|
|
5
|
+
Author-email: Bidoytu <hello@swarnimbandekar.in>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Bidoytu/Bidoytu
|
|
8
|
+
Project-URL: Repository, https://github.com/Bidoytu/Bidoytu
|
|
9
|
+
Project-URL: Issues, https://github.com/Bidoytu/Bidoytu/issues
|
|
10
|
+
Keywords: proxy,mitm,http,security,pentest,pyside6,mitmproxy
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: X11 Applications :: Qt
|
|
13
|
+
Classifier: Environment :: Win32 (MS Windows)
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Information Technology
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Internet :: Proxy Servers
|
|
23
|
+
Classifier: Topic :: Security
|
|
24
|
+
Classifier: Topic :: Software Development :: Testing
|
|
25
|
+
Requires-Python: >=3.11
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: PySide6>=6.6
|
|
29
|
+
Requires-Dist: mitmproxy>=11.0
|
|
30
|
+
Requires-Dist: httpx>=0.27
|
|
31
|
+
Requires-Dist: cryptography>=42.0
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pyinstaller>=6.0; extra == "dev"
|
|
34
|
+
Requires-Dist: nuitka>=2.0; extra == "dev"
|
|
35
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
36
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# Bidoytu
|
|
40
|
+
|
|
41
|
+
An intercepting HTTP proxy tool for inspecting, modifying, and replaying web
|
|
42
|
+
traffic, built in Python. This repository currently contains the **base
|
|
43
|
+
architecture** only - a working skeleton you can run and build on.
|
|
44
|
+
|
|
45
|
+
## Architecture
|
|
46
|
+
|
|
47
|
+
| Concern | Choice |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| Language | Python 3.11+ |
|
|
50
|
+
| Proxy / MITM engine | [mitmproxy](https://mitmproxy.org) used as a library (`DumpMaster`) |
|
|
51
|
+
| Desktop UI | [PySide6](https://doc.qt.io/qtforpython/) (Qt for Python) |
|
|
52
|
+
| Request/response editor | `QPlainTextEdit` + custom `QSyntaxHighlighter` |
|
|
53
|
+
| Database | SQLite via stdlib `sqlite3`, WAL mode, custom `QAbstractTableModel` |
|
|
54
|
+
| Large bodies | Stored as content-addressed files, referenced by path (not BLOBs) |
|
|
55
|
+
| Concurrency | `QThread` hosts mitmproxy on its own asyncio loop; UI updated via signals |
|
|
56
|
+
| Packaging | PyInstaller (dev), Nuitka (release) |
|
|
57
|
+
|
|
58
|
+
### A note on the editor widget
|
|
59
|
+
|
|
60
|
+
QScintilla is a Riverbank project bound to PyQt and has **no official PySide6
|
|
61
|
+
binding** (licensing/ABI incompatibility). To keep the whole app on the
|
|
62
|
+
official PySide6 binding, the base uses `QPlainTextEdit` + a custom
|
|
63
|
+
`QSyntaxHighlighter`. This is isolated in `ui/message_view.py` and
|
|
64
|
+
`ui/highlighter.py`, so it can be swapped for a QScintilla or Monaco-in-
|
|
65
|
+
QWebEngineView approach later without touching the rest of the app.
|
|
66
|
+
|
|
67
|
+
## Features (base)
|
|
68
|
+
|
|
69
|
+
- **Top-level tabs:** Proxy, Repeater, Intruder.
|
|
70
|
+
- **Proxy tab** with two sub-tabs:
|
|
71
|
+
- *HTTP History* - live traffic table + request/response detail.
|
|
72
|
+
- *Intercept* - pause in-flight requests; edit the raw request; **Forward**
|
|
73
|
+
or **Drop**; a toggle turns interception on/off. Requests queue while one
|
|
74
|
+
is held.
|
|
75
|
+
- **Repeater** - load a request (from history), edit it, resend via an async
|
|
76
|
+
`httpx` client, and view the response.
|
|
77
|
+
- **Intruder** - skeleton: request template with `§payload§` markers + payload
|
|
78
|
+
list. The attack runner is the documented next step.
|
|
79
|
+
- **Send to Repeater (Ctrl+R) / Send to Intruder (Ctrl+I)** via right-click on a
|
|
80
|
+
history row (or the keyboard shortcut when the table is focused). The Intercept
|
|
81
|
+
request editor has the same right-click actions.
|
|
82
|
+
- **Soft wrap** on all request/response views, toggleable per detail view.
|
|
83
|
+
- **Pretty-printed bodies** - JSON, XML/HTML, and URL-encoded forms are
|
|
84
|
+
formatted for display (raw request text stays verbatim for editing).
|
|
85
|
+
- **Configurable listen host/port** from the Proxy tab.
|
|
86
|
+
- **CA certificate export** for trusting HTTPS interception (Proxy tab →
|
|
87
|
+
*CA Certificate*).
|
|
88
|
+
|
|
89
|
+
## Project layout
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
src/bidoytu/
|
|
93
|
+
app.py # entry point (QApplication + MainWindow)
|
|
94
|
+
__main__.py # python -m bidoytu
|
|
95
|
+
config.py # paths + proxy/app configuration
|
|
96
|
+
http_utils.py # parse/build raw HTTP text <-> parts
|
|
97
|
+
proxy/
|
|
98
|
+
engine.py # ProxyEngine: QThread hosting mitmproxy DumpMaster
|
|
99
|
+
capture_addon.py # flow -> FlowRecord mapping + interception (pause/forward/drop)
|
|
100
|
+
net/
|
|
101
|
+
async_sender.py # AsyncHttpSender: httpx.AsyncClient on its own loop/thread
|
|
102
|
+
storage/
|
|
103
|
+
models.py # FlowRecord dataclass (framework-free)
|
|
104
|
+
repository.py # SQLite (WAL) CRUD
|
|
105
|
+
body_store.py # content-addressed large-body file store
|
|
106
|
+
ui/
|
|
107
|
+
main_window.py # top-level QTabWidget + wiring
|
|
108
|
+
proxy_tab.py # proxy controls + HTTP History / Intercept sub-tabs
|
|
109
|
+
history_view.py # traffic table + detail + send-to context menu/shortcuts
|
|
110
|
+
intercept_view.py # intercept queue, editor, forward/drop
|
|
111
|
+
repeater_tab.py # editable request + send + response
|
|
112
|
+
intruder_tab.py # skeleton (template + payloads)
|
|
113
|
+
detail_view.py # reusable request|response viewer + soft-wrap toggle
|
|
114
|
+
flow_table_model.py# custom QAbstractTableModel for history
|
|
115
|
+
message_view.py # raw HTTP view/editor (soft wrap + pretty print)
|
|
116
|
+
highlighter.py # HTTP QSyntaxHighlighter
|
|
117
|
+
body_format.py # content-type-aware body pretty-printer
|
|
118
|
+
ca_dialog.py # export/install the proxy CA certificate
|
|
119
|
+
packaging/
|
|
120
|
+
bidoytu.spec # PyInstaller (dev)
|
|
121
|
+
build_release.md # Nuitka (release) instructions
|
|
122
|
+
scripts/ # headless smoke + live verification tests
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Data flow
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
Browser ─▶ mitmproxy (proxy thread, asyncio loop)
|
|
129
|
+
│ CaptureAddon maps flow -> FlowRecord
|
|
130
|
+
▼
|
|
131
|
+
ProxyEngine.flow_captured (Qt signal, queued to UI thread)
|
|
132
|
+
▼
|
|
133
|
+
MainWindow._on_flow_captured
|
|
134
|
+
├─▶ FlowRepository.upsert (SQLite, big bodies -> BodyStore)
|
|
135
|
+
└─▶ FlowTableModel.upsert_record (history table updates live)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Getting started
|
|
139
|
+
|
|
140
|
+
```powershell
|
|
141
|
+
# 1. Create a virtual environment
|
|
142
|
+
python -m venv .venv
|
|
143
|
+
.venv\Scripts\Activate.ps1
|
|
144
|
+
|
|
145
|
+
# 2. Install
|
|
146
|
+
pip install -e .
|
|
147
|
+
|
|
148
|
+
# 3. Run
|
|
149
|
+
bidoytu
|
|
150
|
+
# or: python -m bidoytu
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Click **Start Proxy** on the Proxy tab. It listens on `127.0.0.1:8080` by
|
|
154
|
+
default (host and port are editable). Point a browser or tool at that proxy and
|
|
155
|
+
captured traffic appears in the history table; select a row to view the raw
|
|
156
|
+
request/response.
|
|
157
|
+
|
|
158
|
+
> **Intercepting HTTPS.** Browsers will reject the proxy's on-the-fly
|
|
159
|
+
> certificates (e.g. Firefox's `MOZILLA_PKIX_ERROR_MITM_DETECTED`) until you
|
|
160
|
+
> trust its CA. Start the proxy once (the CA is generated on first run under
|
|
161
|
+
> `<data_dir>/ca/`), then click **CA Certificate** on the Proxy tab to export
|
|
162
|
+
> the cert and follow the install instructions. Only trust this CA on machines
|
|
163
|
+
> you control, and remove it when you're done. Bidoytu only ever exports the
|
|
164
|
+
> public certificate, never the CA private key.
|
|
165
|
+
|
|
166
|
+
## Building
|
|
167
|
+
|
|
168
|
+
- **Development:** `pyinstaller packaging/bidoytu.spec`
|
|
169
|
+
- **Release:** see `packaging/build_release.md` (Nuitka)
|
|
170
|
+
|
|
171
|
+
## Status / next steps
|
|
172
|
+
|
|
173
|
+
Base architecture with a working Proxy (history + interception) and Repeater.
|
|
174
|
+
Natural next features:
|
|
175
|
+
|
|
176
|
+
- Intruder attack runner - iterate payloads through `AsyncHttpSender` and
|
|
177
|
+
collect results in a table (positions parsing for `§...§` markers).
|
|
178
|
+
- Response interception (currently only requests are paused).
|
|
179
|
+
- Scope filtering, search, and column sorting/filtering on the history view.
|
|
180
|
+
- Match/replace rules and a saved project format.
|
|
181
|
+
|
|
182
|
+
## Contributing
|
|
183
|
+
|
|
184
|
+
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, the
|
|
185
|
+
testing workflow, and coding conventions, and [CONTRIBUTORS.md](CONTRIBUTORS.md)
|
|
186
|
+
for the list of people who have helped. To report a vulnerability, see
|
|
187
|
+
[SECURITY.md](SECURITY.md).
|
|
188
|
+
|
|
189
|
+
## Legal / responsible use
|
|
190
|
+
|
|
191
|
+
Bidoytu is an intercepting proxy intended for testing systems **you own or are
|
|
192
|
+
explicitly authorized to test**. Intercepting traffic or installing a MITM CA on
|
|
193
|
+
machines or networks without permission may be illegal. You are responsible for
|
|
194
|
+
how you use it.
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT - see [LICENSE](LICENSE).
|
bidoytu-1.1.0/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Bidoytu
|
|
2
|
+
|
|
3
|
+
An intercepting HTTP proxy tool for inspecting, modifying, and replaying web
|
|
4
|
+
traffic, built in Python. This repository currently contains the **base
|
|
5
|
+
architecture** only - a working skeleton you can run and build on.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
| Concern | Choice |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| Language | Python 3.11+ |
|
|
12
|
+
| Proxy / MITM engine | [mitmproxy](https://mitmproxy.org) used as a library (`DumpMaster`) |
|
|
13
|
+
| Desktop UI | [PySide6](https://doc.qt.io/qtforpython/) (Qt for Python) |
|
|
14
|
+
| Request/response editor | `QPlainTextEdit` + custom `QSyntaxHighlighter` |
|
|
15
|
+
| Database | SQLite via stdlib `sqlite3`, WAL mode, custom `QAbstractTableModel` |
|
|
16
|
+
| Large bodies | Stored as content-addressed files, referenced by path (not BLOBs) |
|
|
17
|
+
| Concurrency | `QThread` hosts mitmproxy on its own asyncio loop; UI updated via signals |
|
|
18
|
+
| Packaging | PyInstaller (dev), Nuitka (release) |
|
|
19
|
+
|
|
20
|
+
### A note on the editor widget
|
|
21
|
+
|
|
22
|
+
QScintilla is a Riverbank project bound to PyQt and has **no official PySide6
|
|
23
|
+
binding** (licensing/ABI incompatibility). To keep the whole app on the
|
|
24
|
+
official PySide6 binding, the base uses `QPlainTextEdit` + a custom
|
|
25
|
+
`QSyntaxHighlighter`. This is isolated in `ui/message_view.py` and
|
|
26
|
+
`ui/highlighter.py`, so it can be swapped for a QScintilla or Monaco-in-
|
|
27
|
+
QWebEngineView approach later without touching the rest of the app.
|
|
28
|
+
|
|
29
|
+
## Features (base)
|
|
30
|
+
|
|
31
|
+
- **Top-level tabs:** Proxy, Repeater, Intruder.
|
|
32
|
+
- **Proxy tab** with two sub-tabs:
|
|
33
|
+
- *HTTP History* - live traffic table + request/response detail.
|
|
34
|
+
- *Intercept* - pause in-flight requests; edit the raw request; **Forward**
|
|
35
|
+
or **Drop**; a toggle turns interception on/off. Requests queue while one
|
|
36
|
+
is held.
|
|
37
|
+
- **Repeater** - load a request (from history), edit it, resend via an async
|
|
38
|
+
`httpx` client, and view the response.
|
|
39
|
+
- **Intruder** - skeleton: request template with `§payload§` markers + payload
|
|
40
|
+
list. The attack runner is the documented next step.
|
|
41
|
+
- **Send to Repeater (Ctrl+R) / Send to Intruder (Ctrl+I)** via right-click on a
|
|
42
|
+
history row (or the keyboard shortcut when the table is focused). The Intercept
|
|
43
|
+
request editor has the same right-click actions.
|
|
44
|
+
- **Soft wrap** on all request/response views, toggleable per detail view.
|
|
45
|
+
- **Pretty-printed bodies** - JSON, XML/HTML, and URL-encoded forms are
|
|
46
|
+
formatted for display (raw request text stays verbatim for editing).
|
|
47
|
+
- **Configurable listen host/port** from the Proxy tab.
|
|
48
|
+
- **CA certificate export** for trusting HTTPS interception (Proxy tab →
|
|
49
|
+
*CA Certificate*).
|
|
50
|
+
|
|
51
|
+
## Project layout
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
src/bidoytu/
|
|
55
|
+
app.py # entry point (QApplication + MainWindow)
|
|
56
|
+
__main__.py # python -m bidoytu
|
|
57
|
+
config.py # paths + proxy/app configuration
|
|
58
|
+
http_utils.py # parse/build raw HTTP text <-> parts
|
|
59
|
+
proxy/
|
|
60
|
+
engine.py # ProxyEngine: QThread hosting mitmproxy DumpMaster
|
|
61
|
+
capture_addon.py # flow -> FlowRecord mapping + interception (pause/forward/drop)
|
|
62
|
+
net/
|
|
63
|
+
async_sender.py # AsyncHttpSender: httpx.AsyncClient on its own loop/thread
|
|
64
|
+
storage/
|
|
65
|
+
models.py # FlowRecord dataclass (framework-free)
|
|
66
|
+
repository.py # SQLite (WAL) CRUD
|
|
67
|
+
body_store.py # content-addressed large-body file store
|
|
68
|
+
ui/
|
|
69
|
+
main_window.py # top-level QTabWidget + wiring
|
|
70
|
+
proxy_tab.py # proxy controls + HTTP History / Intercept sub-tabs
|
|
71
|
+
history_view.py # traffic table + detail + send-to context menu/shortcuts
|
|
72
|
+
intercept_view.py # intercept queue, editor, forward/drop
|
|
73
|
+
repeater_tab.py # editable request + send + response
|
|
74
|
+
intruder_tab.py # skeleton (template + payloads)
|
|
75
|
+
detail_view.py # reusable request|response viewer + soft-wrap toggle
|
|
76
|
+
flow_table_model.py# custom QAbstractTableModel for history
|
|
77
|
+
message_view.py # raw HTTP view/editor (soft wrap + pretty print)
|
|
78
|
+
highlighter.py # HTTP QSyntaxHighlighter
|
|
79
|
+
body_format.py # content-type-aware body pretty-printer
|
|
80
|
+
ca_dialog.py # export/install the proxy CA certificate
|
|
81
|
+
packaging/
|
|
82
|
+
bidoytu.spec # PyInstaller (dev)
|
|
83
|
+
build_release.md # Nuitka (release) instructions
|
|
84
|
+
scripts/ # headless smoke + live verification tests
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Data flow
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
Browser ─▶ mitmproxy (proxy thread, asyncio loop)
|
|
91
|
+
│ CaptureAddon maps flow -> FlowRecord
|
|
92
|
+
▼
|
|
93
|
+
ProxyEngine.flow_captured (Qt signal, queued to UI thread)
|
|
94
|
+
▼
|
|
95
|
+
MainWindow._on_flow_captured
|
|
96
|
+
├─▶ FlowRepository.upsert (SQLite, big bodies -> BodyStore)
|
|
97
|
+
└─▶ FlowTableModel.upsert_record (history table updates live)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Getting started
|
|
101
|
+
|
|
102
|
+
```powershell
|
|
103
|
+
# 1. Create a virtual environment
|
|
104
|
+
python -m venv .venv
|
|
105
|
+
.venv\Scripts\Activate.ps1
|
|
106
|
+
|
|
107
|
+
# 2. Install
|
|
108
|
+
pip install -e .
|
|
109
|
+
|
|
110
|
+
# 3. Run
|
|
111
|
+
bidoytu
|
|
112
|
+
# or: python -m bidoytu
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Click **Start Proxy** on the Proxy tab. It listens on `127.0.0.1:8080` by
|
|
116
|
+
default (host and port are editable). Point a browser or tool at that proxy and
|
|
117
|
+
captured traffic appears in the history table; select a row to view the raw
|
|
118
|
+
request/response.
|
|
119
|
+
|
|
120
|
+
> **Intercepting HTTPS.** Browsers will reject the proxy's on-the-fly
|
|
121
|
+
> certificates (e.g. Firefox's `MOZILLA_PKIX_ERROR_MITM_DETECTED`) until you
|
|
122
|
+
> trust its CA. Start the proxy once (the CA is generated on first run under
|
|
123
|
+
> `<data_dir>/ca/`), then click **CA Certificate** on the Proxy tab to export
|
|
124
|
+
> the cert and follow the install instructions. Only trust this CA on machines
|
|
125
|
+
> you control, and remove it when you're done. Bidoytu only ever exports the
|
|
126
|
+
> public certificate, never the CA private key.
|
|
127
|
+
|
|
128
|
+
## Building
|
|
129
|
+
|
|
130
|
+
- **Development:** `pyinstaller packaging/bidoytu.spec`
|
|
131
|
+
- **Release:** see `packaging/build_release.md` (Nuitka)
|
|
132
|
+
|
|
133
|
+
## Status / next steps
|
|
134
|
+
|
|
135
|
+
Base architecture with a working Proxy (history + interception) and Repeater.
|
|
136
|
+
Natural next features:
|
|
137
|
+
|
|
138
|
+
- Intruder attack runner - iterate payloads through `AsyncHttpSender` and
|
|
139
|
+
collect results in a table (positions parsing for `§...§` markers).
|
|
140
|
+
- Response interception (currently only requests are paused).
|
|
141
|
+
- Scope filtering, search, and column sorting/filtering on the history view.
|
|
142
|
+
- Match/replace rules and a saved project format.
|
|
143
|
+
|
|
144
|
+
## Contributing
|
|
145
|
+
|
|
146
|
+
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, the
|
|
147
|
+
testing workflow, and coding conventions, and [CONTRIBUTORS.md](CONTRIBUTORS.md)
|
|
148
|
+
for the list of people who have helped. To report a vulnerability, see
|
|
149
|
+
[SECURITY.md](SECURITY.md).
|
|
150
|
+
|
|
151
|
+
## Legal / responsible use
|
|
152
|
+
|
|
153
|
+
Bidoytu is an intercepting proxy intended for testing systems **you own or are
|
|
154
|
+
explicitly authorized to test**. Intercepting traffic or installing a MITM CA on
|
|
155
|
+
machines or networks without permission may be illegal. You are responsible for
|
|
156
|
+
how you use it.
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
MIT - see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "bidoytu"
|
|
3
|
+
version = "1.1.0"
|
|
4
|
+
description = "An intercepting HTTP proxy tool built with PySide6 and mitmproxy."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Bidoytu", email = "hello@swarnimbandekar.in" },
|
|
10
|
+
]
|
|
11
|
+
keywords = ["proxy", "mitm", "http", "security", "pentest", "pyside6", "mitmproxy"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 5 - Production/Stable",
|
|
14
|
+
"Environment :: X11 Applications :: Qt",
|
|
15
|
+
"Environment :: Win32 (MS Windows)",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Intended Audience :: Information Technology",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Internet :: Proxy Servers",
|
|
25
|
+
"Topic :: Security",
|
|
26
|
+
"Topic :: Software Development :: Testing",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"PySide6>=6.6",
|
|
30
|
+
"mitmproxy>=11.0",
|
|
31
|
+
"httpx>=0.27",
|
|
32
|
+
"cryptography>=42.0",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/Bidoytu/Bidoytu"
|
|
37
|
+
Repository = "https://github.com/Bidoytu/Bidoytu"
|
|
38
|
+
Issues = "https://github.com/Bidoytu/Bidoytu/issues"
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
dev = [
|
|
42
|
+
"pyinstaller>=6.0",
|
|
43
|
+
"nuitka>=2.0",
|
|
44
|
+
"build>=1.0",
|
|
45
|
+
"twine>=5.0",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[project.scripts]
|
|
49
|
+
bidoytu = "bidoytu.app:main"
|
|
50
|
+
|
|
51
|
+
[build-system]
|
|
52
|
+
requires = ["setuptools>=68", "wheel"]
|
|
53
|
+
build-backend = "setuptools.build_meta"
|
|
54
|
+
|
|
55
|
+
[tool.setuptools.packages.find]
|
|
56
|
+
where = ["src"]
|
|
57
|
+
|
|
58
|
+
[tool.setuptools.package-dir]
|
|
59
|
+
"" = "src"
|
|
60
|
+
|
|
61
|
+
[tool.setuptools.package-data]
|
|
62
|
+
bidoytu = ["assets/*.png", "assets/*.ico"]
|
bidoytu-1.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Bidoytu - an intercepting HTTP proxy tool.
|
|
2
|
+
|
|
3
|
+
Base architecture:
|
|
4
|
+
- Proxy/MITM engine: mitmproxy (DumpMaster) running in a QThread.
|
|
5
|
+
- Desktop UI: PySide6 (Qt for Python).
|
|
6
|
+
- Storage: SQLite (WAL) + on-disk body file store, exposed to the UI via
|
|
7
|
+
a custom QAbstractTableModel.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
__version__ = "1.1.0"
|
|
11
|
+
__app_name__ = "Bidoytu"
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Application entry point.
|
|
2
|
+
|
|
3
|
+
Creates the Qt application, builds the main window, and starts the event loop.
|
|
4
|
+
The proxy starts automatically once the window is shown; if the listen port is
|
|
5
|
+
already in use, the window prompts the user to change the port or retry.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
from PySide6.QtGui import QIcon
|
|
13
|
+
from PySide6.QtWidgets import QApplication
|
|
14
|
+
|
|
15
|
+
from bidoytu import __app_name__
|
|
16
|
+
from bidoytu.config import AppConfig
|
|
17
|
+
from bidoytu.resources import logo_path
|
|
18
|
+
from bidoytu.ui.main_window import MainWindow
|
|
19
|
+
from bidoytu.ui.theme import apply_theme, load_theme
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _set_windows_app_id() -> None:
|
|
23
|
+
"""Give Windows an explicit AppUserModelID so the taskbar uses our icon.
|
|
24
|
+
|
|
25
|
+
Without this, a Python-hosted app is grouped under python.exe and shows the
|
|
26
|
+
interpreter's icon on the taskbar instead of the window icon.
|
|
27
|
+
"""
|
|
28
|
+
if os.name != "nt":
|
|
29
|
+
return
|
|
30
|
+
try:
|
|
31
|
+
import ctypes
|
|
32
|
+
|
|
33
|
+
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
|
|
34
|
+
f"{__app_name__}.{__app_name__}"
|
|
35
|
+
)
|
|
36
|
+
except Exception:
|
|
37
|
+
# Non-fatal: the app still runs, just without the custom taskbar group.
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main() -> int:
|
|
42
|
+
_set_windows_app_id()
|
|
43
|
+
|
|
44
|
+
app = QApplication(sys.argv)
|
|
45
|
+
app.setApplicationName(__app_name__)
|
|
46
|
+
app.setOrganizationName(__app_name__)
|
|
47
|
+
|
|
48
|
+
# Application/taskbar icon.
|
|
49
|
+
icon = QIcon(str(logo_path()))
|
|
50
|
+
if not icon.isNull():
|
|
51
|
+
app.setWindowIcon(icon)
|
|
52
|
+
|
|
53
|
+
# Apply the persisted theme (defaults to dark) before building widgets so
|
|
54
|
+
# highlighters pick up matching colors from the start.
|
|
55
|
+
apply_theme(app, load_theme())
|
|
56
|
+
|
|
57
|
+
config = AppConfig()
|
|
58
|
+
window = MainWindow(config)
|
|
59
|
+
window.show()
|
|
60
|
+
|
|
61
|
+
return app.exec()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
raise SystemExit(main())
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""Application configuration and filesystem paths.
|
|
2
|
+
|
|
3
|
+
Everything the app writes (SQLite DB, large request/response bodies) lives under
|
|
4
|
+
a single data directory so it is easy to locate, back up, or clear.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _default_data_dir() -> Path:
|
|
14
|
+
"""Return a per-user, per-OS data directory for Bidoytu."""
|
|
15
|
+
if os.name == "nt": # Windows
|
|
16
|
+
base = os.environ.get("LOCALAPPDATA") or os.path.expanduser("~")
|
|
17
|
+
return Path(base) / "Bidoytu"
|
|
18
|
+
# macOS / Linux
|
|
19
|
+
xdg = os.environ.get("XDG_DATA_HOME")
|
|
20
|
+
if xdg:
|
|
21
|
+
return Path(xdg) / "bidoytu"
|
|
22
|
+
return Path.home() / ".local" / "share" / "bidoytu"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(slots=True)
|
|
26
|
+
class ProxyConfig:
|
|
27
|
+
"""Listen settings for the mitmproxy engine."""
|
|
28
|
+
|
|
29
|
+
listen_host: str = "127.0.0.1"
|
|
30
|
+
listen_port: int = 8080
|
|
31
|
+
http2: bool = True
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Free, public Interactsh servers operated by ProjectDiscovery. They are tried
|
|
35
|
+
# in order at registration time; the first that accepts the registration wins.
|
|
36
|
+
# Users can add their own (self-hosted) server in the Collaborator tab.
|
|
37
|
+
DEFAULT_OAST_SERVERS = (
|
|
38
|
+
"oast.pro",
|
|
39
|
+
"oast.live",
|
|
40
|
+
"oast.site",
|
|
41
|
+
"oast.online",
|
|
42
|
+
"oast.fun",
|
|
43
|
+
"oast.me",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(slots=True)
|
|
48
|
+
class CollaboratorConfig:
|
|
49
|
+
"""Settings for the Collaborator (out-of-band interaction) feature.
|
|
50
|
+
|
|
51
|
+
Attributes:
|
|
52
|
+
servers: Ordered list of Interactsh server hostnames to try when
|
|
53
|
+
registering. The first entry is treated as the preferred server.
|
|
54
|
+
token: Optional ``Authorization`` token for protected / self-hosted
|
|
55
|
+
servers (leave empty for the public ones).
|
|
56
|
+
poll_interval_secs: How often to poll the server for new interactions.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
servers: list[str] = field(default_factory=lambda: list(DEFAULT_OAST_SERVERS))
|
|
60
|
+
token: str = ""
|
|
61
|
+
poll_interval_secs: int = 10
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass(slots=True)
|
|
65
|
+
class AppConfig:
|
|
66
|
+
"""Top-level runtime configuration.
|
|
67
|
+
|
|
68
|
+
Attributes:
|
|
69
|
+
data_dir: Root directory for all persisted state.
|
|
70
|
+
proxy: Proxy listen configuration.
|
|
71
|
+
body_inline_limit: Bodies at or below this size (bytes) are stored
|
|
72
|
+
inline in SQLite; larger bodies are written to the file store and
|
|
73
|
+
referenced by path.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
data_dir: Path = field(default_factory=_default_data_dir)
|
|
77
|
+
proxy: ProxyConfig = field(default_factory=ProxyConfig)
|
|
78
|
+
collaborator: CollaboratorConfig = field(default_factory=CollaboratorConfig)
|
|
79
|
+
body_inline_limit: int = 64 * 1024 # 64 KiB
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def db_path(self) -> Path:
|
|
83
|
+
return self.data_dir / "bidoytu.db"
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def bodies_dir(self) -> Path:
|
|
87
|
+
return self.data_dir / "bodies"
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def repeater_sessions_path(self) -> Path:
|
|
91
|
+
"""JSON file holding persisted Repeater sessions across restarts."""
|
|
92
|
+
return self.data_dir / "repeater_sessions.json"
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def intruder_attack_path(self) -> Path:
|
|
96
|
+
"""JSON file holding the last Intruder attack config across restarts."""
|
|
97
|
+
return self.data_dir / "intruder_attack.json"
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def collaborator_state_path(self) -> Path:
|
|
101
|
+
"""JSON file holding Collaborator session state across restarts.
|
|
102
|
+
|
|
103
|
+
Stores the active Interactsh session (server, correlation id, keys),
|
|
104
|
+
generated payloads, and captured interactions so a running OAST session
|
|
105
|
+
can be resumed after a restart.
|
|
106
|
+
"""
|
|
107
|
+
return self.data_dir / "collaborator.json"
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def confdir(self) -> Path:
|
|
111
|
+
"""Directory where mitmproxy stores its generated CA and certs.
|
|
112
|
+
|
|
113
|
+
We keep it under Bidoytu's data dir (instead of the default
|
|
114
|
+
``~/.mitmproxy``) so the CA is predictable and easy to export.
|
|
115
|
+
"""
|
|
116
|
+
return self.data_dir / "ca"
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def ca_cert_pem(self) -> Path:
|
|
120
|
+
"""PEM-encoded CA certificate (for Firefox, curl, most Linux tools)."""
|
|
121
|
+
return self.confdir / "mitmproxy-ca-cert.pem"
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def ca_cert_cer(self) -> Path:
|
|
125
|
+
"""DER/.cer CA certificate (convenient for the Windows cert store)."""
|
|
126
|
+
return self.confdir / "mitmproxy-ca-cert.cer"
|
|
127
|
+
|
|
128
|
+
def ensure_dirs(self) -> None:
|
|
129
|
+
"""Create the data, body, and CA directories if they do not exist."""
|
|
130
|
+
self.data_dir.mkdir(parents=True, exist_ok=True)
|
|
131
|
+
self.bodies_dir.mkdir(parents=True, exist_ok=True)
|
|
132
|
+
self.confdir.mkdir(parents=True, exist_ok=True)
|