riciplay-miniproxy 0.4.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.
- riciplay_miniproxy-0.4.0/.gitignore +3 -0
- riciplay_miniproxy-0.4.0/LICENSE +21 -0
- riciplay_miniproxy-0.4.0/PKG-INFO +101 -0
- riciplay_miniproxy-0.4.0/README.md +78 -0
- riciplay_miniproxy-0.4.0/bin/miniproxy +24 -0
- riciplay_miniproxy-0.4.0/package.json +49 -0
- riciplay_miniproxy-0.4.0/pyproject.toml +50 -0
- riciplay_miniproxy-0.4.0/scripts/postinstall.js +44 -0
- riciplay_miniproxy-0.4.0/src/miniproxy/__init__.py +9 -0
- riciplay_miniproxy-0.4.0/src/miniproxy/__main__.py +150 -0
- riciplay_miniproxy-0.4.0/src/miniproxy/addon/__init__.py +7 -0
- riciplay_miniproxy-0.4.0/src/miniproxy/addon/db.py +351 -0
- riciplay_miniproxy-0.4.0/src/miniproxy/addon/proxy.py +233 -0
- riciplay_miniproxy-0.4.0/src/miniproxy/app.py +161 -0
- riciplay_miniproxy-0.4.0/src/miniproxy/intruder.py +126 -0
- riciplay_miniproxy-0.4.0/src/miniproxy/server.py +205 -0
- riciplay_miniproxy-0.4.0/src/miniproxy/templates/index.html +419 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zaidux
|
|
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.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: riciplay-miniproxy
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: A lightweight HTTP/S interception proxy for security research — always-on capture, timing, scope guard, Repeater + Intruder dashboard
|
|
5
|
+
Project-URL: Homepage, https://github.com/Zaidux/miniproxy
|
|
6
|
+
Project-URL: Repository, https://github.com/Zaidux/miniproxy
|
|
7
|
+
Author: Zaidux
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: bugbounty,interception,intruder,mitmproxy,proxy,repeater,security
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Internet :: Proxy Servers
|
|
17
|
+
Classifier: Topic :: Security
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: flask>=3.0.0
|
|
20
|
+
Requires-Dist: mitmproxy>=10.0.0
|
|
21
|
+
Requires-Dist: requests>=2.31.0
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# MiniProxy
|
|
25
|
+
|
|
26
|
+
A lightweight HTTP/S interception proxy for security research — the standalone Burp-style toolkit from the [Riciplay](https://github.com/Zaidux/Riciplay) ecosystem. Built on **mitmproxy**, **Flask**, and **SQLite**.
|
|
27
|
+
|
|
28
|
+
Ships the **v4 capture engine** (the same addon the Riciplay CLI embeds):
|
|
29
|
+
|
|
30
|
+
- **Always-on capture** — every request/response is logged with full headers, bodies, and status codes. (The old intercept toggle silently produced empty captures on fresh DBs; it's gone.)
|
|
31
|
+
- **Static-asset streaming** — JS/CSS/images stream through without buffering, so heavy SPAs don't pay a per-flow decode tax.
|
|
32
|
+
- **Timing** — `ttfb_ms` / `total_ms` on every flow. Oracles and timing side-channels need numbers.
|
|
33
|
+
- **Scope guard** — `--scope '*.target.com'` keeps out-of-scope traffic out of your capture DB (or 403-blocks it with `--block-out-of-scope`).
|
|
34
|
+
- **Repeater** — pick a logged request, edit method/headers/body, resend, inspect.
|
|
35
|
+
- **Intruder** — mark parameters with `§param§` placeholders, supply a wordlist, fuzz every position; results highlighted by status code and stored in the DB.
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Python (PyPI)
|
|
41
|
+
pip install riciplay-miniproxy
|
|
42
|
+
|
|
43
|
+
# or Node (npm — installs the Python package on postinstall)
|
|
44
|
+
npm install -g riciplay-miniproxy
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Requires Python 3.10+ and [mitmproxy](https://mitmproxy.org/).
|
|
48
|
+
|
|
49
|
+
## Quick start
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
miniproxy start # proxy on :8080
|
|
53
|
+
miniproxy start --scope '*.target.com' # capture only in-scope traffic
|
|
54
|
+
miniproxy dashboard # web UI on :5000 (Log/Repeater/Intruder)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Then point your browser/system at `http://127.0.0.1:8080` (HTTPS interception uses mitmproxy's CA — run `mitmdump` once and install `~/.mitmproxy/mitmproxy-ca-cert.pem` if you haven't).
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
miniproxy log # recent captured requests (with timing; reads the DB directly)
|
|
61
|
+
miniproxy log --id 5 # full detail for request #5
|
|
62
|
+
miniproxy send --url https://example.com/api --method POST \
|
|
63
|
+
--headers '{"Content-Type":"application/json"}' \
|
|
64
|
+
--body '{"key":"value"}'
|
|
65
|
+
miniproxy status
|
|
66
|
+
miniproxy stop
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
State (pid, port, default capture DB) lives under `~/.miniproxy/`.
|
|
70
|
+
|
|
71
|
+
## The dashboard
|
|
72
|
+
|
|
73
|
+
`miniproxy dashboard` serves a dark-theme SPA:
|
|
74
|
+
|
|
75
|
+
- **Proxy** — capture status and scope
|
|
76
|
+
- **Log** — live request feed; click any row for full headers/bodies/timing
|
|
77
|
+
- **Repeater** — edit and resend any captured request
|
|
78
|
+
- **Intruder** — `§param§` placeholders + wordlist fuzzing, results by status code
|
|
79
|
+
|
|
80
|
+
## Relation to the Riciplay CLI
|
|
81
|
+
|
|
82
|
+
The Riciplay CLI (`pip install riciplay-cli`) embeds this same engine with agent-first extras: per-session capture DBs, RULES.md-derived scope enforcement, headless replay with response diffing, and bounded payload sweeps driven by the AI agent. Use MiniProxy standalone when you want the manual Burp-like workflow; use Riciplay when you want the agent to drive.
|
|
83
|
+
|
|
84
|
+
## Project structure
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
src/miniproxy/
|
|
88
|
+
├── addon/
|
|
89
|
+
│ ├── proxy.py # mitmproxy addon — v4 capture engine
|
|
90
|
+
│ └── db.py # SQLite handler (timing columns, intruder results, pruning)
|
|
91
|
+
├── app.py # Flask dashboard (API + UI)
|
|
92
|
+
├── intruder.py # §placeholder§ fuzzing engine
|
|
93
|
+
├── server.py # start/stop/status process manager
|
|
94
|
+
└── __main__.py # the `miniproxy` entry point (start/stop/status/
|
|
95
|
+
# dashboard/send/log — log and send work without
|
|
96
|
+
# any dashboard running)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# MiniProxy
|
|
2
|
+
|
|
3
|
+
A lightweight HTTP/S interception proxy for security research — the standalone Burp-style toolkit from the [Riciplay](https://github.com/Zaidux/Riciplay) ecosystem. Built on **mitmproxy**, **Flask**, and **SQLite**.
|
|
4
|
+
|
|
5
|
+
Ships the **v4 capture engine** (the same addon the Riciplay CLI embeds):
|
|
6
|
+
|
|
7
|
+
- **Always-on capture** — every request/response is logged with full headers, bodies, and status codes. (The old intercept toggle silently produced empty captures on fresh DBs; it's gone.)
|
|
8
|
+
- **Static-asset streaming** — JS/CSS/images stream through without buffering, so heavy SPAs don't pay a per-flow decode tax.
|
|
9
|
+
- **Timing** — `ttfb_ms` / `total_ms` on every flow. Oracles and timing side-channels need numbers.
|
|
10
|
+
- **Scope guard** — `--scope '*.target.com'` keeps out-of-scope traffic out of your capture DB (or 403-blocks it with `--block-out-of-scope`).
|
|
11
|
+
- **Repeater** — pick a logged request, edit method/headers/body, resend, inspect.
|
|
12
|
+
- **Intruder** — mark parameters with `§param§` placeholders, supply a wordlist, fuzz every position; results highlighted by status code and stored in the DB.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Python (PyPI)
|
|
18
|
+
pip install riciplay-miniproxy
|
|
19
|
+
|
|
20
|
+
# or Node (npm — installs the Python package on postinstall)
|
|
21
|
+
npm install -g riciplay-miniproxy
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Requires Python 3.10+ and [mitmproxy](https://mitmproxy.org/).
|
|
25
|
+
|
|
26
|
+
## Quick start
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
miniproxy start # proxy on :8080
|
|
30
|
+
miniproxy start --scope '*.target.com' # capture only in-scope traffic
|
|
31
|
+
miniproxy dashboard # web UI on :5000 (Log/Repeater/Intruder)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Then point your browser/system at `http://127.0.0.1:8080` (HTTPS interception uses mitmproxy's CA — run `mitmdump` once and install `~/.mitmproxy/mitmproxy-ca-cert.pem` if you haven't).
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
miniproxy log # recent captured requests (with timing; reads the DB directly)
|
|
38
|
+
miniproxy log --id 5 # full detail for request #5
|
|
39
|
+
miniproxy send --url https://example.com/api --method POST \
|
|
40
|
+
--headers '{"Content-Type":"application/json"}' \
|
|
41
|
+
--body '{"key":"value"}'
|
|
42
|
+
miniproxy status
|
|
43
|
+
miniproxy stop
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
State (pid, port, default capture DB) lives under `~/.miniproxy/`.
|
|
47
|
+
|
|
48
|
+
## The dashboard
|
|
49
|
+
|
|
50
|
+
`miniproxy dashboard` serves a dark-theme SPA:
|
|
51
|
+
|
|
52
|
+
- **Proxy** — capture status and scope
|
|
53
|
+
- **Log** — live request feed; click any row for full headers/bodies/timing
|
|
54
|
+
- **Repeater** — edit and resend any captured request
|
|
55
|
+
- **Intruder** — `§param§` placeholders + wordlist fuzzing, results by status code
|
|
56
|
+
|
|
57
|
+
## Relation to the Riciplay CLI
|
|
58
|
+
|
|
59
|
+
The Riciplay CLI (`pip install riciplay-cli`) embeds this same engine with agent-first extras: per-session capture DBs, RULES.md-derived scope enforcement, headless replay with response diffing, and bounded payload sweeps driven by the AI agent. Use MiniProxy standalone when you want the manual Burp-like workflow; use Riciplay when you want the agent to drive.
|
|
60
|
+
|
|
61
|
+
## Project structure
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
src/miniproxy/
|
|
65
|
+
├── addon/
|
|
66
|
+
│ ├── proxy.py # mitmproxy addon — v4 capture engine
|
|
67
|
+
│ └── db.py # SQLite handler (timing columns, intruder results, pruning)
|
|
68
|
+
├── app.py # Flask dashboard (API + UI)
|
|
69
|
+
├── intruder.py # §placeholder§ fuzzing engine
|
|
70
|
+
├── server.py # start/stop/status process manager
|
|
71
|
+
└── __main__.py # the `miniproxy` entry point (start/stop/status/
|
|
72
|
+
# dashboard/send/log — log and send work without
|
|
73
|
+
# any dashboard running)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// MiniProxy launcher — delegates to the Python package (riciplay-miniproxy).
|
|
3
|
+
//
|
|
4
|
+
// miniproxy <args> → python3 -m miniproxy <args>
|
|
5
|
+
//
|
|
6
|
+
// The Python package is installed by scripts/postinstall.js (pip install
|
|
7
|
+
// riciplay-miniproxy). If python3 is missing the user gets a clear error
|
|
8
|
+
// rather than a silent failure.
|
|
9
|
+
|
|
10
|
+
const { spawn } = require('child_process');
|
|
11
|
+
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
const proc = spawn('python3', ['-m', 'miniproxy', ...args], { stdio: 'inherit' });
|
|
14
|
+
|
|
15
|
+
proc.on('error', (err) => {
|
|
16
|
+
if (err.code === 'ENOENT') {
|
|
17
|
+
console.error('Error: python3 not found. MiniProxy is a Python tool — install Python 3.10+ first.');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
console.error('Error:', err.message);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
proc.on('close', (code) => process.exit(code ?? 1));
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "riciplay-miniproxy",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "A lightweight HTTP/S interception proxy for security research — always-on capture, timing, scope guard, Repeater + Intruder dashboard",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"miniproxy": "./bin/miniproxy"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
+
"prepublishOnly": "echo \"Ensure the Python package riciplay-miniproxy is published to PyPI first\"",
|
|
12
|
+
"postinstall": "node scripts/postinstall.js"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/Zaidux/miniproxy.git"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"proxy",
|
|
20
|
+
"mitmproxy",
|
|
21
|
+
"interception",
|
|
22
|
+
"security",
|
|
23
|
+
"bugbounty",
|
|
24
|
+
"repeater",
|
|
25
|
+
"intruder",
|
|
26
|
+
"pentesting"
|
|
27
|
+
],
|
|
28
|
+
"author": "Zaidux <zaiduabubakar777@gmail.com>",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/Zaidux/miniproxy/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/Zaidux/miniproxy#readme",
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=14.0.0"
|
|
36
|
+
},
|
|
37
|
+
"os": [
|
|
38
|
+
"darwin",
|
|
39
|
+
"linux"
|
|
40
|
+
],
|
|
41
|
+
"files": [
|
|
42
|
+
"bin/",
|
|
43
|
+
"scripts/postinstall.js",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE"
|
|
46
|
+
],
|
|
47
|
+
"dependencies": {},
|
|
48
|
+
"peerDependencies": {}
|
|
49
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "riciplay-miniproxy"
|
|
7
|
+
version = "0.4.0"
|
|
8
|
+
description = "A lightweight HTTP/S interception proxy for security research — always-on capture, timing, scope guard, Repeater + Intruder dashboard"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Zaidux"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["proxy", "mitmproxy", "interception", "security", "bugbounty", "repeater", "intruder"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Operating System :: POSIX :: Linux",
|
|
21
|
+
"Operating System :: MacOS",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Topic :: Security",
|
|
24
|
+
"Topic :: Internet :: Proxy Servers",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"mitmproxy>=10.0.0",
|
|
28
|
+
"Flask>=3.0.0",
|
|
29
|
+
"requests>=2.31.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
miniproxy = "miniproxy.__main__:main"
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/Zaidux/miniproxy"
|
|
37
|
+
Repository = "https://github.com/Zaidux/miniproxy"
|
|
38
|
+
|
|
39
|
+
[tool.hatch.build.targets.wheel]
|
|
40
|
+
packages = ["src/miniproxy"]
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.sdist]
|
|
43
|
+
include = [
|
|
44
|
+
"src/miniproxy",
|
|
45
|
+
"README.md",
|
|
46
|
+
"LICENSE",
|
|
47
|
+
"package.json",
|
|
48
|
+
"bin",
|
|
49
|
+
"scripts",
|
|
50
|
+
]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// MiniProxy npm postinstall — installs the Python package from PyPI.
|
|
3
|
+
//
|
|
4
|
+
// This npm package is a thin launcher; the real implementation is the
|
|
5
|
+
// Python package `riciplay-miniproxy`. If it is not importable, pip
|
|
6
|
+
// install it. Failures are warnings, not errors: the user may be
|
|
7
|
+
// installing without network access or managing Python themselves.
|
|
8
|
+
|
|
9
|
+
const { execSync, spawnSync } = require('child_process');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
|
|
12
|
+
console.log('\n🔌 Installing MiniProxy (HTTP/S interception proxy)...\n');
|
|
13
|
+
|
|
14
|
+
const isWin = os.platform() === 'win32';
|
|
15
|
+
const python = isWin ? 'python' : 'python3';
|
|
16
|
+
const pip = isWin ? 'pip' : 'pip3';
|
|
17
|
+
|
|
18
|
+
function hasCommand(cmd) {
|
|
19
|
+
try {
|
|
20
|
+
execSync(`${cmd} --version`, { stdio: 'ignore' });
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const probe = spawnSync(python, ['-c', 'import miniproxy'], { stdio: 'ignore' });
|
|
29
|
+
if (probe.status === 0) {
|
|
30
|
+
console.log('✓ Python package `miniproxy` already importable.');
|
|
31
|
+
} else {
|
|
32
|
+
console.log('Installing riciplay-miniproxy from PyPI...');
|
|
33
|
+
execSync(`${pip} install --upgrade riciplay-miniproxy`, { stdio: 'inherit' });
|
|
34
|
+
console.log('✓ Installed riciplay-miniproxy.');
|
|
35
|
+
}
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.warn(
|
|
38
|
+
`\n⚠ Could not install the Python package automatically (${err.message}).\n` +
|
|
39
|
+
' MiniProxy needs Python 3.10+ and mitmproxy. Install manually with:\n' +
|
|
40
|
+
' pip install riciplay-miniproxy\n'
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log('\nUsage: miniproxy start --port 8080 # then miniproxy dashboard\n');
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""MiniProxy — a lightweight HTTP/S interception proxy for security research.
|
|
2
|
+
|
|
3
|
+
Ships the canonical v4 capture engine (the same addon the Riciplay CLI
|
|
4
|
+
embeds): always-on capture, static-asset streaming, per-flow TTFB/total
|
|
5
|
+
timing, and a RULES-style scope guard with skip/block modes — plus a Flask
|
|
6
|
+
dashboard (Log / Repeater / Intruder) and a small manager CLI.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__version__ = "0.4.0"
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""MiniProxy entry point — ``miniproxy`` / ``python -m miniproxy``.
|
|
2
|
+
|
|
3
|
+
Subcommands:
|
|
4
|
+
start Start the interception proxy (mitmdump + v4 addon)
|
|
5
|
+
stop Stop the proxy
|
|
6
|
+
status Proxy status
|
|
7
|
+
dashboard Run the Flask dashboard (Log / Repeater / Intruder UI)
|
|
8
|
+
send Send a request through the dashboard's Repeater
|
|
9
|
+
log Show captured requests
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def main(argv: list[str] | None = None) -> int:
|
|
19
|
+
parser = argparse.ArgumentParser(
|
|
20
|
+
prog="miniproxy",
|
|
21
|
+
description="HTTP/S interception proxy for security research.",
|
|
22
|
+
)
|
|
23
|
+
sub = parser.add_subparsers(dest="command")
|
|
24
|
+
|
|
25
|
+
p_start = sub.add_parser("start", help="Start the interception proxy")
|
|
26
|
+
p_start.add_argument("--port", type=int, default=8080)
|
|
27
|
+
p_start.add_argument("--db", default=None, help="SQLite capture DB path")
|
|
28
|
+
p_start.add_argument("--scope", nargs="*", default=None,
|
|
29
|
+
help="Capture-scope hosts (*.domain wildcards); "
|
|
30
|
+
"out-of-scope traffic is not captured")
|
|
31
|
+
p_start.add_argument("--block-out-of-scope", action="store_true",
|
|
32
|
+
help="403-block out-of-scope traffic instead of passing it through")
|
|
33
|
+
p_start.add_argument("--mitmdump", default="mitmdump")
|
|
34
|
+
|
|
35
|
+
sub.add_parser("stop", help="Stop the proxy")
|
|
36
|
+
sub.add_parser("status", help="Proxy status")
|
|
37
|
+
|
|
38
|
+
p_dash = sub.add_parser("dashboard", help="Run the Flask dashboard")
|
|
39
|
+
p_dash.add_argument("--port", type=int, default=5000)
|
|
40
|
+
p_dash.add_argument("--host", default="127.0.0.1")
|
|
41
|
+
|
|
42
|
+
p_send = sub.add_parser("send", help="Send a request via the dashboard Repeater")
|
|
43
|
+
p_send.add_argument("--url", required=True)
|
|
44
|
+
p_send.add_argument("--method", default="GET")
|
|
45
|
+
p_send.add_argument("--headers", default="{}", help="JSON object of headers")
|
|
46
|
+
p_send.add_argument("--body", default="")
|
|
47
|
+
|
|
48
|
+
p_log = sub.add_parser("log", help="Show captured requests")
|
|
49
|
+
p_log.add_argument("--id", type=int, default=None)
|
|
50
|
+
p_log.add_argument("--limit", type=int, default=30)
|
|
51
|
+
|
|
52
|
+
args = parser.parse_args(argv)
|
|
53
|
+
|
|
54
|
+
if args.command == "start":
|
|
55
|
+
from miniproxy import server
|
|
56
|
+
result = server.start(
|
|
57
|
+
port=args.port, db_path=args.db, scope_hosts=args.scope,
|
|
58
|
+
out_of_scope="block" if args.block_out_of_scope else "skip",
|
|
59
|
+
mitmdump_path=args.mitmdump,
|
|
60
|
+
)
|
|
61
|
+
print(result["message"])
|
|
62
|
+
return 0 if result["status"] in ("started", "already_running") else 1
|
|
63
|
+
|
|
64
|
+
if args.command == "stop":
|
|
65
|
+
from miniproxy import server
|
|
66
|
+
result = server.stop()
|
|
67
|
+
print(result["message"])
|
|
68
|
+
return 0 if result["status"] in ("stopped", "not_running") else 1
|
|
69
|
+
|
|
70
|
+
if args.command == "status":
|
|
71
|
+
from miniproxy import server
|
|
72
|
+
result = server.status()
|
|
73
|
+
print(result["message"])
|
|
74
|
+
return 0 if result["status"] == "running" else 1
|
|
75
|
+
|
|
76
|
+
if args.command == "dashboard":
|
|
77
|
+
from miniproxy.app import app
|
|
78
|
+
app.run(host=args.host, port=args.port, debug=False)
|
|
79
|
+
return 0
|
|
80
|
+
|
|
81
|
+
if args.command == "send":
|
|
82
|
+
# Direct request — no dashboard needed (mirrors the Repeater API's
|
|
83
|
+
# semantics: no redirects, self-signed certs allowed, 15s timeout).
|
|
84
|
+
import json as _json
|
|
85
|
+
|
|
86
|
+
import requests
|
|
87
|
+
import urllib3
|
|
88
|
+
|
|
89
|
+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
90
|
+
try:
|
|
91
|
+
headers = _json.loads(args.headers)
|
|
92
|
+
if not isinstance(headers, dict):
|
|
93
|
+
headers = {}
|
|
94
|
+
except ValueError:
|
|
95
|
+
print("Error: --headers must be a JSON object", file=sys.stderr)
|
|
96
|
+
return 1
|
|
97
|
+
try:
|
|
98
|
+
resp = requests.request(
|
|
99
|
+
method=args.method.upper(), url=args.url, headers=headers,
|
|
100
|
+
data=args.body, timeout=15, verify=False, allow_redirects=False,
|
|
101
|
+
)
|
|
102
|
+
except requests.RequestException as exc:
|
|
103
|
+
print(f"Error: {exc}", file=sys.stderr)
|
|
104
|
+
return 1
|
|
105
|
+
print(f"Status: {resp.status_code}")
|
|
106
|
+
print(f"Headers: {_json.dumps(dict(resp.headers), indent=2)}")
|
|
107
|
+
body_text = resp.text or ""
|
|
108
|
+
print(f"Body ({len(body_text)} chars):")
|
|
109
|
+
print(body_text[:2000] + ("..." if len(body_text) > 2000 else ""))
|
|
110
|
+
return 0
|
|
111
|
+
|
|
112
|
+
if args.command == "log":
|
|
113
|
+
# Reads the capture DB directly — the dashboard does not need to
|
|
114
|
+
# be running to inspect what the proxy captured.
|
|
115
|
+
from miniproxy.addon.db import Database
|
|
116
|
+
from miniproxy.server import STATE_DIR
|
|
117
|
+
|
|
118
|
+
db = Database(os.environ.get("MINIPROXY_DB_PATH") or (STATE_DIR / "proxy.db"))
|
|
119
|
+
if args.id is not None:
|
|
120
|
+
row = db.get_request(args.id)
|
|
121
|
+
if row is None:
|
|
122
|
+
print(f"No captured request with id={args.id}.", file=sys.stderr)
|
|
123
|
+
return 1
|
|
124
|
+
print(f"ID: {row.get('id')}")
|
|
125
|
+
print(f"Method: {row.get('method')}")
|
|
126
|
+
print(f"URL: {row.get('url')}")
|
|
127
|
+
print(f"Status: {row.get('response_code')}")
|
|
128
|
+
print(f"TTFB: {row.get('ttfb_ms')} ms | Total: {row.get('total_ms')} ms")
|
|
129
|
+
print(f"Headers: {str(row.get('headers') or '')[:500]}")
|
|
130
|
+
print(f"Body: {str(row.get('body') or '')[:1000]}")
|
|
131
|
+
return 0
|
|
132
|
+
logs = db.get_logs(limit=max(args.limit, 1))
|
|
133
|
+
print(f"{'ID':>4} {'METHOD':<7} {'STATUS':<5} {'MS':>7} URL")
|
|
134
|
+
print("-" * 72)
|
|
135
|
+
for r in logs:
|
|
136
|
+
ms = r.get("total_ms")
|
|
137
|
+
ms_s = f"{ms:.0f}" if isinstance(ms, (int, float)) else "?"
|
|
138
|
+
print(f"{r.get('id', '?'):>4} {r.get('method', '?'):<7} "
|
|
139
|
+
f"{r.get('response_code', '?'):<5} {ms_s:>7} "
|
|
140
|
+
f"{str(r.get('url', ''))[:60]}")
|
|
141
|
+
if not logs:
|
|
142
|
+
print("(no captured requests yet)")
|
|
143
|
+
return 0
|
|
144
|
+
|
|
145
|
+
parser.print_help()
|
|
146
|
+
return 1
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
if __name__ == "__main__":
|
|
150
|
+
sys.exit(main())
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""MiniProxy addon package — the canonical v4 mitmproxy addon.
|
|
2
|
+
|
|
3
|
+
``proxy.py`` and ``db.py`` are loaded by mitmdump BY PATH (``mitmdump -s
|
|
4
|
+
.../addon/proxy.py``); the ``__init__.py`` exists so the same files are
|
|
5
|
+
importable as a regular package (tests, tooling) via the try/except import
|
|
6
|
+
at the top of ``proxy.py``.
|
|
7
|
+
"""
|