phonsint 0.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.
phonsint-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kaif
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,189 @@
1
+ Metadata-Version: 2.4
2
+ Name: phonsint
3
+ Version: 0.1.0
4
+ Summary: High-throughput silent phone number OSINT suite for platform footprinting, profile enrichment, and masked email discovery
5
+ Keywords: osint,phone-osint,reconnaissance,security,threat-intelligence,footprinting,identity-enrichment,metadata-extraction
6
+ Author-email: Kaif <kaifcodec@gmail.com>
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Information Technology
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Security
22
+ Classifier: Topic :: Utilities
23
+ License-File: LICENSE
24
+ Requires-Dist: phonenumbers>=8.13.0
25
+ Requires-Dist: httpx[http2]>=0.27.0,<0.29.0
26
+ Requires-Dist: curl_cffi>=0.7.0,<1.0.0
27
+ Requires-Dist: rich>=13.0.0
28
+ Requires-Dist: colorama>=0.4.0,<1.0.0
29
+ Requires-Dist: pytest>=7.0.0 ; extra == "dev"
30
+ Requires-Dist: pytest-cov ; extra == "dev"
31
+ Requires-Dist: ruff ; extra == "dev"
32
+ Requires-Dist: mypy ; extra == "dev"
33
+ Project-URL: Bug Tracker, https://github.com/kaifcodec/phonsint/issues
34
+ Project-URL: Homepage, https://github.com/kaifcodec/phonsint
35
+ Project-URL: Repository, https://github.com/kaifcodec/phonsint.git
36
+ Provides-Extra: dev
37
+
38
+ # phonsint
39
+ <img width="1713" height="420" alt="phonsint-banner" src="https://github.com/user-attachments/assets/a76a73a2-8aac-4813-a265-6802b2230eb0" />
40
+
41
+ <p align="center">
42
+ <img src="https://img.shields.io/badge/Version-0.1.0-blueviolet?style=for-the-badge&logo=github" />
43
+ <img src="https://img.shields.io/badge/Tested%20on-Linux%20%7C%20macOS%20%7C%20Windows%20%7C%20Termux-black?style=for-the-badge" />
44
+ </p>
45
+
46
+ <p align="center">
47
+ <strong>High-Throughput Silent Phone Number OSINT & Identity Footprinting Suite</strong><br>
48
+ <em>Uncover account registrations, masked emails, display names, and profile photos across major platforms—without ever dispatching an SMS or alerting the target.</em>
49
+ </p>
50
+
51
+ ---
52
+
53
+ ## ⚡ What is phonsint?
54
+
55
+ Most phone number tools stop at basic format parsing or risk blowing an investigation by triggering loud 2FA/SMS verification codes to the target's mobile device.
56
+
57
+ **phonsint** is engineered for **silent, high-fidelity reconnaissance**. It interrogates pre-authentication discovery endpoints, recovery validation flows, and cryptographic challenge APIs (such as Meta LOX proof-of-work) to determine account existence and extract rich profile metadata—**completely silently**.
58
+
59
+ ---
60
+
61
+ ## ✨ Key Capabilities
62
+
63
+ * **Guaranteed Silent Operation**: Exploits unauthenticated query endpoints and pre-dispatch recovery flows that **never send SMS verification codes or push notifications** to the target.
64
+ * **Deep Identity Enrichment**:
65
+ * 📧 **Masked Email Addresses**: Surfaces linked emails (e.g. `j******e@gmail.com`) associated with the phone number.
66
+ * 👤 **Full Display Names**: Pulls account owner names from public platform recovery APIs.
67
+ * 🖼️ **High-Resolution Avatars**: Extracts direct CDN profile photo URLs without logging in.
68
+ * 🏢 **Tenant & Region Routing**: Identifies regional datacenters (e.g., Zoho US, EU, IN, CN, JP).
69
+ * **High-Throughput Concurrency**: Built with Python `asyncio` and `curl_cffi` for browser TLS fingerprint impersonation (bypasses Cloudflare, Akamai, and Shape Security without heavy headless browsers).
70
+ * **Zero False Positives**: Uses strict, dual-state validation markers (matching the design philosophy of [`user-scanner`](https://github.com/kaifcodec/user-scanner)).
71
+ * **Multi-Format Reports**: Automated structured exports to **JSON** and **CSV** for immediate ingestion into investigation pipelines.
72
+
73
+ ---
74
+
75
+ ## 🖥️ Live Terminal Preview
76
+
77
+ ```text
78
+ Checking phone: +14155552671
79
+
80
+ == SOCIAL SITES ==
81
+ [✔] Facebook (+14155552671) [Registered]
82
+ ├── name: John Doe
83
+ ├── masked_email: j******e@gmail.com
84
+ └── avatar: https://scontent.xx.fbcdn.net/v/t39.30808-1/s200x200/412...
85
+
86
+ [✔] Instagram (+14155552671) [Registered]
87
+ └── contact_point: Phone Number Verified
88
+
89
+ == AUTH SITES ==
90
+ [✔] Microsoft (+14155552671) [Registered]
91
+ └── auth_type: Live ID / Account Exists
92
+
93
+ == SHOPPING SITES ==
94
+ [✔] Amazon (+14155552671) [Registered]
95
+ └── claim_endpoint: Active Sign-in Route
96
+
97
+ == SUMMARY ==
98
+ Total Sites Scanned: 5
99
+ Registered / Found: 4
100
+ ```
101
+
102
+ ---
103
+
104
+ ## 🚀 Installation
105
+
106
+ ### 🐍 Via Virtual Environment (Recommended)
107
+
108
+ ```bash
109
+ # Clone the repository
110
+ git clone https://github.com/kaifcodec/phonsint.git
111
+ cd phonsint
112
+
113
+ # Create and activate virtual environment
114
+ python3 -m venv .venv
115
+ source .venv/bin/activate # On Windows: .venv\Scripts\Activate.ps1
116
+
117
+ # Install dependencies and package in editable mode
118
+ pip install -e .
119
+ ```
120
+
121
+ ### 📦 Direct Installation
122
+
123
+ ```bash
124
+ pip install phonsint
125
+ ```
126
+
127
+ ---
128
+
129
+ ## 💻 Usage Guide
130
+
131
+ ### 1. Standard Target Scan (E.164 Format)
132
+
133
+ ```bash
134
+ phonsint -p +14155552671
135
+ ```
136
+
137
+ ### 2. Scanning Local Format Numbers with Default Region
138
+
139
+ If a target number does not include an international `+` country prefix, specify the default ISO country code:
140
+
141
+ ```bash
142
+ phonsint -p 07911123456 -r GB
143
+ phonsint -p 9876543210 -r IN
144
+ ```
145
+
146
+ ### 3. Targeted Category & Module Filtering
147
+
148
+ ```bash
149
+ # Scan authentication providers only (e.g., Microsoft)
150
+ phonsint -p +14155552671 -c auth
151
+
152
+ # Scan specific platforms
153
+ phonsint -p +14155552671 -m facebook,instagram
154
+
155
+ # List all available modules and categories in a clean table
156
+ phonsint -l
157
+ ```
158
+
159
+ ### 4. Verbose Mode & Exporting Results
160
+
161
+ ```bash
162
+ # Show all results including unregistered platforms and diagnostic reasons
163
+ phonsint -p +14155552671 --all -v
164
+
165
+ # Export results to JSON or CSV
166
+ phonsint -p +14155552671 -o report.json
167
+ phonsint -p +14155552671 -f csv -o report.csv
168
+ ```
169
+
170
+ ---
171
+
172
+ ## 🛡️ OPSEC & Silent-by-Default Guarantee
173
+
174
+ In phone OSINT, accidental SMS verification codes sent to the target's phone are a critical operational failure. `phonsint` operates **100% silently by default**, targeting pre-dispatch query endpoints, session credential checks, and unauthenticated recovery validations that **do not dispatch SMS messages**.
175
+
176
+ Any module that could potentially trigger a notification is registered under `LOUD_MODULES` in `phonsint/core/helpers.py` and will be automatically skipped unless the user explicitly passes `--allow-loud`.
177
+
178
+ ---
179
+
180
+ ## 🤝 Contributing
181
+
182
+ We welcome contributions! Adding a new platform check takes around ~30 lines of clean Python. Check out **[CONTRIBUTING.md](CONTRIBUTING.md)** for developer conventions, validator signatures, and instructions.
183
+
184
+ ---
185
+
186
+ ## 📜 License
187
+
188
+ Distributed under the **MIT License**.
189
+
@@ -0,0 +1,151 @@
1
+ # phonsint
2
+ <img width="1713" height="420" alt="phonsint-banner" src="https://github.com/user-attachments/assets/a76a73a2-8aac-4813-a265-6802b2230eb0" />
3
+
4
+ <p align="center">
5
+ <img src="https://img.shields.io/badge/Version-0.1.0-blueviolet?style=for-the-badge&logo=github" />
6
+ <img src="https://img.shields.io/badge/Tested%20on-Linux%20%7C%20macOS%20%7C%20Windows%20%7C%20Termux-black?style=for-the-badge" />
7
+ </p>
8
+
9
+ <p align="center">
10
+ <strong>High-Throughput Silent Phone Number OSINT & Identity Footprinting Suite</strong><br>
11
+ <em>Uncover account registrations, masked emails, display names, and profile photos across major platforms—without ever dispatching an SMS or alerting the target.</em>
12
+ </p>
13
+
14
+ ---
15
+
16
+ ## ⚡ What is phonsint?
17
+
18
+ Most phone number tools stop at basic format parsing or risk blowing an investigation by triggering loud 2FA/SMS verification codes to the target's mobile device.
19
+
20
+ **phonsint** is engineered for **silent, high-fidelity reconnaissance**. It interrogates pre-authentication discovery endpoints, recovery validation flows, and cryptographic challenge APIs (such as Meta LOX proof-of-work) to determine account existence and extract rich profile metadata—**completely silently**.
21
+
22
+ ---
23
+
24
+ ## ✨ Key Capabilities
25
+
26
+ * **Guaranteed Silent Operation**: Exploits unauthenticated query endpoints and pre-dispatch recovery flows that **never send SMS verification codes or push notifications** to the target.
27
+ * **Deep Identity Enrichment**:
28
+ * 📧 **Masked Email Addresses**: Surfaces linked emails (e.g. `j******e@gmail.com`) associated with the phone number.
29
+ * 👤 **Full Display Names**: Pulls account owner names from public platform recovery APIs.
30
+ * 🖼️ **High-Resolution Avatars**: Extracts direct CDN profile photo URLs without logging in.
31
+ * 🏢 **Tenant & Region Routing**: Identifies regional datacenters (e.g., Zoho US, EU, IN, CN, JP).
32
+ * **High-Throughput Concurrency**: Built with Python `asyncio` and `curl_cffi` for browser TLS fingerprint impersonation (bypasses Cloudflare, Akamai, and Shape Security without heavy headless browsers).
33
+ * **Zero False Positives**: Uses strict, dual-state validation markers (matching the design philosophy of [`user-scanner`](https://github.com/kaifcodec/user-scanner)).
34
+ * **Multi-Format Reports**: Automated structured exports to **JSON** and **CSV** for immediate ingestion into investigation pipelines.
35
+
36
+ ---
37
+
38
+ ## 🖥️ Live Terminal Preview
39
+
40
+ ```text
41
+ Checking phone: +14155552671
42
+
43
+ == SOCIAL SITES ==
44
+ [✔] Facebook (+14155552671) [Registered]
45
+ ├── name: John Doe
46
+ ├── masked_email: j******e@gmail.com
47
+ └── avatar: https://scontent.xx.fbcdn.net/v/t39.30808-1/s200x200/412...
48
+
49
+ [✔] Instagram (+14155552671) [Registered]
50
+ └── contact_point: Phone Number Verified
51
+
52
+ == AUTH SITES ==
53
+ [✔] Microsoft (+14155552671) [Registered]
54
+ └── auth_type: Live ID / Account Exists
55
+
56
+ == SHOPPING SITES ==
57
+ [✔] Amazon (+14155552671) [Registered]
58
+ └── claim_endpoint: Active Sign-in Route
59
+
60
+ == SUMMARY ==
61
+ Total Sites Scanned: 5
62
+ Registered / Found: 4
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 🚀 Installation
68
+
69
+ ### 🐍 Via Virtual Environment (Recommended)
70
+
71
+ ```bash
72
+ # Clone the repository
73
+ git clone https://github.com/kaifcodec/phonsint.git
74
+ cd phonsint
75
+
76
+ # Create and activate virtual environment
77
+ python3 -m venv .venv
78
+ source .venv/bin/activate # On Windows: .venv\Scripts\Activate.ps1
79
+
80
+ # Install dependencies and package in editable mode
81
+ pip install -e .
82
+ ```
83
+
84
+ ### 📦 Direct Installation
85
+
86
+ ```bash
87
+ pip install phonsint
88
+ ```
89
+
90
+ ---
91
+
92
+ ## 💻 Usage Guide
93
+
94
+ ### 1. Standard Target Scan (E.164 Format)
95
+
96
+ ```bash
97
+ phonsint -p +14155552671
98
+ ```
99
+
100
+ ### 2. Scanning Local Format Numbers with Default Region
101
+
102
+ If a target number does not include an international `+` country prefix, specify the default ISO country code:
103
+
104
+ ```bash
105
+ phonsint -p 07911123456 -r GB
106
+ phonsint -p 9876543210 -r IN
107
+ ```
108
+
109
+ ### 3. Targeted Category & Module Filtering
110
+
111
+ ```bash
112
+ # Scan authentication providers only (e.g., Microsoft)
113
+ phonsint -p +14155552671 -c auth
114
+
115
+ # Scan specific platforms
116
+ phonsint -p +14155552671 -m facebook,instagram
117
+
118
+ # List all available modules and categories in a clean table
119
+ phonsint -l
120
+ ```
121
+
122
+ ### 4. Verbose Mode & Exporting Results
123
+
124
+ ```bash
125
+ # Show all results including unregistered platforms and diagnostic reasons
126
+ phonsint -p +14155552671 --all -v
127
+
128
+ # Export results to JSON or CSV
129
+ phonsint -p +14155552671 -o report.json
130
+ phonsint -p +14155552671 -f csv -o report.csv
131
+ ```
132
+
133
+ ---
134
+
135
+ ## 🛡️ OPSEC & Silent-by-Default Guarantee
136
+
137
+ In phone OSINT, accidental SMS verification codes sent to the target's phone are a critical operational failure. `phonsint` operates **100% silently by default**, targeting pre-dispatch query endpoints, session credential checks, and unauthenticated recovery validations that **do not dispatch SMS messages**.
138
+
139
+ Any module that could potentially trigger a notification is registered under `LOUD_MODULES` in `phonsint/core/helpers.py` and will be automatically skipped unless the user explicitly passes `--allow-loud`.
140
+
141
+ ---
142
+
143
+ ## 🤝 Contributing
144
+
145
+ We welcome contributions! Adding a new platform check takes around ~30 lines of clean Python. Check out **[CONTRIBUTING.md](CONTRIBUTING.md)** for developer conventions, validator signatures, and instructions.
146
+
147
+ ---
148
+
149
+ ## 📜 License
150
+
151
+ Distributed under the **MIT License**.
@@ -0,0 +1,5 @@
1
+ """
2
+ phonsint - Advanced Silent Phone Reconnaissance & Identity Footprinting Suite
3
+ """
4
+
5
+ __version__ = "0.1.0"
@@ -0,0 +1,150 @@
1
+ import argparse
2
+ import csv
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+ from colorama import Fore, Style, init
7
+
8
+ from phonsint import __version__
9
+ from phonsint.core.helpers import ScanConfig, load_categories, load_modules
10
+ from phonsint.core.orchestrator import run_scan
11
+ from phonsint.core.parser import parse_phone_number
12
+ from rich.console import Console
13
+ from rich.table import Table
14
+
15
+ init(autoreset=True)
16
+ console = Console()
17
+
18
+ BANNER = rf"""{Fore.CYAN}
19
+ ____ __ _ __
20
+ / __ \/ /_ ____ ____ _____ (_)___ / /_
21
+ / /_/ / __ \/ __ \/ __ \/ ___// / __ \/ __/
22
+ / ____/ / / / /_/ / / / (__ )/ / / / / /_
23
+ /_/ /_/ /_/\____/_/ /_/____//_/_/ /_/\__/
24
+ {Fore.LIGHTBLACK_EX} phonsint v{__version__} | Silent Phone Reconnaissance & Intelligence Suite
25
+ {Style.RESET_ALL}"""
26
+
27
+
28
+ def print_banner() -> None:
29
+ print(BANNER)
30
+
31
+
32
+
33
+
34
+ def list_modules() -> None:
35
+ print_banner()
36
+ categories = load_categories()
37
+ if not categories:
38
+ print(f"{Fore.YELLOW}[!] No modules found.{Style.RESET_ALL}")
39
+ return
40
+
41
+ table = Table(title="[bold cyan]Available Scan Modules[/bold cyan]", border_style="cyan")
42
+ table.add_column("Category", style="magenta", width=18)
43
+ table.add_column("Module", style="green", width=22)
44
+ table.add_column("Path", style="dim white")
45
+
46
+ for cat_name, cat_path in sorted(categories.items()):
47
+ mods = load_modules(cat_path)
48
+ for m in mods:
49
+ table.add_row(cat_name.capitalize(), m.__name__.split(".")[-1], str(m.__file__))
50
+
51
+ console.print(table)
52
+
53
+
54
+ def export_results(results, file_path: str, fmt: str = "json") -> None:
55
+ out = Path(file_path)
56
+ out.parent.mkdir(parents=True, exist_ok=True)
57
+
58
+ if fmt == "csv" or out.suffix.lower() == ".csv":
59
+ with open(out, "w", newline="", encoding="utf-8") as f:
60
+ writer = csv.writer(f)
61
+ writer.writerow(["site_name", "category", "phone", "status", "url", "extra", "media", "reason"])
62
+ for r in results:
63
+ writer.writerow([
64
+ r.site_name,
65
+ r.category,
66
+ r.phone,
67
+ r.status.to_label(),
68
+ r.url,
69
+ json.dumps(r.extra) if r.extra else "",
70
+ json.dumps(r.media) if r.media else "",
71
+ r.reason or "",
72
+ ])
73
+ else:
74
+ with open(out, "w", encoding="utf-8") as f:
75
+ json.dump([r.to_dict() for r in results], f, indent=2)
76
+
77
+ print(f"\n{Fore.GREEN}[+] Results successfully exported to: {out}{Style.RESET_ALL}")
78
+
79
+
80
+ def main() -> None:
81
+ parser = argparse.ArgumentParser(
82
+ prog="phonsint",
83
+ description="phonsint: High-Throughput Silent Phone Number Reconnaissance & Identity Footprinting",
84
+ )
85
+ parser.add_argument("-p", "--phone", help="Target phone number (e.g. +14155552671)")
86
+ parser.add_argument("-r", "--region", help="Default ISO country code if number lacks '+' prefix (e.g. US, GB, IN, FR)")
87
+ parser.add_argument("-c", "--category", help="Limit scan to specific comma-separated categories (e.g. auth,social)")
88
+ parser.add_argument("-m", "--module", help="Limit scan to specific comma-separated modules (e.g. microsoft,facebook)")
89
+ parser.add_argument("-l", "--list", action="store_true", help="List all available categories and modules")
90
+ parser.add_argument("--all", action="store_true", help="Show all results including not registered and errors")
91
+ parser.add_argument("-v", "--verbose", action="store_true", help="Display URLs and diagnostic reasons")
92
+ parser.add_argument("-t", "--timeout", type=float, default=15.0, help="Per-module request timeout in seconds")
93
+ parser.add_argument("-C", "--concurrency", type=int, default=15, help="Maximum concurrent requests")
94
+ parser.add_argument("--allow-loud", action="store_true", help="Allow modules that may trigger SMS/notifications")
95
+ parser.add_argument("-f", "--format", choices=["json", "csv"], default="json", help="Export format (json/csv)")
96
+ parser.add_argument("-o", "--output", help="Save scan results to file")
97
+ parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
98
+
99
+ args = parser.parse_args()
100
+
101
+ if args.list:
102
+ list_modules()
103
+ return
104
+
105
+ if not args.phone:
106
+ print_banner()
107
+ parser.print_help()
108
+ print(f"\n{Fore.RED}[!] Error: Please provide a target phone number using -p / --phone.{Style.RESET_ALL}")
109
+ sys.exit(1)
110
+
111
+ print_banner()
112
+
113
+ # Step 1: Parse and validate phone number
114
+ phone_info = parse_phone_number(args.phone, args.region)
115
+ print(f"\n{Fore.CYAN} Checking phone: {phone_info.e164}{Style.RESET_ALL}")
116
+
117
+ if not phone_info.is_valid:
118
+ print(f"{Fore.YELLOW}[!] Warning: '{args.phone}' is not a standard allocated number according to E.164.{Style.RESET_ALL}")
119
+ print(f"{Fore.YELLOW}[*] Proceeding with best-effort scan anyway...{Style.RESET_ALL}\n")
120
+
121
+ # Step 2: Build scan configuration
122
+ configs = ScanConfig(
123
+ allow_loud=args.allow_loud,
124
+ show_all=args.all,
125
+ verbose=args.verbose,
126
+ timeout=args.timeout,
127
+ concurrency=args.concurrency,
128
+ )
129
+
130
+ # Step 3: Run platform scan
131
+ results = run_scan(
132
+ phone_info,
133
+ configs,
134
+ category_filter=args.category,
135
+ module_filter=args.module,
136
+ )
137
+
138
+ # Summary count
139
+ taken_count = sum(1 for r in results if r.is_found())
140
+ print(f"\n{Fore.CYAN}== SUMMARY =={Style.RESET_ALL}")
141
+ print(f"Total Sites Scanned: {len(results)}")
142
+ print(f"Registered / Found: {Fore.GREEN}{taken_count}{Style.RESET_ALL}")
143
+
144
+ # Step 4: Export if requested
145
+ if args.output:
146
+ export_results(results, args.output, args.format)
147
+
148
+
149
+ if __name__ == "__main__":
150
+ main()
@@ -0,0 +1,3 @@
1
+ """
2
+ Core modules for phonsint
3
+ """
@@ -0,0 +1,74 @@
1
+ import importlib.util
2
+ import inspect
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+ from typing import Any, Callable, Dict, List, Optional, cast
7
+
8
+ # List of modules that could potentially alert the target via SMS or phone call
9
+ LOUD_MODULES: List[str] = []
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class ScanConfig:
14
+ allow_loud: bool = False
15
+ show_all: bool = False
16
+ verbose: bool = False
17
+ timeout: Optional[float] = None
18
+ concurrency: int = 15
19
+
20
+
21
+ def is_loud(site_name: str) -> bool:
22
+ return site_name.lower() in LOUD_MODULES
23
+
24
+
25
+ def get_site_name(module: ModuleType) -> str:
26
+ name = module.__name__.split(".")[-1].replace("_", " ").title()
27
+ return name
28
+
29
+
30
+ def find_category(module: ModuleType) -> str:
31
+ parts = module.__name__.split(".")
32
+ if len(parts) >= 3 and parts[-3] == "modules":
33
+ return parts[-2].capitalize()
34
+ return "General"
35
+
36
+
37
+ def load_categories() -> Dict[str, Path]:
38
+ root = Path(__file__).resolve().parent.parent / "modules"
39
+ categories: Dict[str, Path] = {}
40
+ if not root.exists():
41
+ return categories
42
+
43
+ for subfolder in root.iterdir():
44
+ if subfolder.is_dir() and not subfolder.name.startswith("__"):
45
+ categories[subfolder.name] = subfolder.resolve()
46
+
47
+ return categories
48
+
49
+
50
+ def load_modules(category_path: Path) -> List[ModuleType]:
51
+ modules = []
52
+ for file in category_path.glob("*.py"):
53
+ if file.name.startswith("__"):
54
+ continue
55
+ spec = importlib.util.spec_from_file_location(file.stem, str(file))
56
+ if spec is None or spec.loader is None:
57
+ continue
58
+ module = importlib.util.module_from_spec(spec)
59
+ try:
60
+ spec.loader.exec_module(module)
61
+ modules.append(module)
62
+ except Exception:
63
+ continue
64
+ return modules
65
+
66
+
67
+ def get_scan_func(module: ModuleType) -> Optional[Callable[..., Any]]:
68
+ for attr_name in dir(module):
69
+ if not attr_name.startswith("validate_"):
70
+ continue
71
+ f = getattr(module, attr_name)
72
+ if inspect.isfunction(f) or inspect.iscoroutinefunction(f):
73
+ return cast(Callable[..., Any], f)
74
+ return None
@@ -0,0 +1,97 @@
1
+ import asyncio
2
+ import threading
3
+ from typing import Literal, Optional, cast
4
+ from curl_cffi import requests as cffi
5
+
6
+ DEFAULT_IMPERSONATE = "chrome"
7
+ DEFAULT_TIMEOUT = 15.0
8
+
9
+ _sessions: dict[tuple, cffi.Session] = {}
10
+ _key_locks: dict[tuple, threading.Lock] = {}
11
+ _warmed: set[tuple] = set()
12
+ _lock = threading.Lock()
13
+ _global_timeout: Optional[float] = None
14
+ _global_proxy: Optional[str] = None
15
+
16
+
17
+ def set_global_timeout(val: Optional[float]) -> None:
18
+ global _global_timeout
19
+ _global_timeout = val
20
+
21
+
22
+ def get_global_timeout() -> float:
23
+ return _global_timeout if _global_timeout is not None else DEFAULT_TIMEOUT
24
+
25
+
26
+ def set_global_proxy(val: Optional[str]) -> None:
27
+ global _global_proxy
28
+ _global_proxy = val
29
+
30
+
31
+ def get_global_proxy() -> Optional[str]:
32
+ return _global_proxy
33
+
34
+
35
+ def _get_warm_session(
36
+ impersonate: str,
37
+ proxy: Optional[str],
38
+ warmup_url: Optional[str] = None,
39
+ ) -> cffi.Session:
40
+ key = (impersonate, proxy)
41
+ with _lock:
42
+ session = _sessions.get(key)
43
+ if session is None:
44
+ session = cffi.Session(
45
+ impersonate=impersonate, # type: ignore[arg-type]
46
+ proxies={"http": proxy, "https": proxy} if proxy else None,
47
+ )
48
+ _sessions[key] = session
49
+ _key_locks[key] = threading.Lock()
50
+
51
+ key_lock = _key_locks.get(key)
52
+ if key_lock is None:
53
+ key_lock = threading.Lock()
54
+ _key_locks[key] = key_lock
55
+
56
+ if warmup_url and key not in _warmed:
57
+ with key_lock:
58
+ if key not in _warmed:
59
+ try:
60
+ session.get(warmup_url, timeout=get_global_timeout())
61
+ except Exception:
62
+ pass
63
+ _warmed.add(key)
64
+
65
+ return session
66
+
67
+
68
+ def impersonate_request(
69
+ url: str,
70
+ method: Literal["GET", "POST"] = "GET",
71
+ warmup_url: Optional[str] = None,
72
+ impersonate: str = DEFAULT_IMPERSONATE,
73
+ **kwargs,
74
+ ) -> cffi.Response:
75
+ """Issue a request through a cookie-persistent browser-impersonating curl_cffi session."""
76
+ session = _get_warm_session(impersonate, get_global_proxy(), warmup_url)
77
+ kwargs.setdefault("timeout", get_global_timeout())
78
+ kwargs.setdefault("allow_redirects", False)
79
+ return cast(cffi.Response, session.request(method, url, **kwargs))
80
+
81
+
82
+ async def impersonate_request_async(
83
+ url: str,
84
+ method: Literal["GET", "POST"] = "GET",
85
+ warmup_url: Optional[str] = None,
86
+ impersonate: str = DEFAULT_IMPERSONATE,
87
+ **kwargs,
88
+ ) -> cffi.Response:
89
+ """Async wrapper around impersonate_request."""
90
+ return await asyncio.to_thread(
91
+ impersonate_request,
92
+ url,
93
+ method=method,
94
+ warmup_url=warmup_url,
95
+ impersonate=impersonate,
96
+ **kwargs,
97
+ )