webpeek 1.2.1__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.
webpeek-1.2.1/PKG-INFO ADDED
@@ -0,0 +1,195 @@
1
+ Metadata-Version: 2.4
2
+ Name: webpeek
3
+ Version: 1.2.1
4
+ Summary: OSINT CLI tool for web reconnaissance
5
+ Home-page: https://github.com/JorgeRosbel/webpeek
6
+ Author: JorgeRosbel
7
+ Author-email: JorgeRosbel <jorge@rosbel.dev>
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/JorgeRosbel/webpeek
10
+ Requires-Python: >=3.8
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: click>=8.0
13
+ Requires-Dist: requests>=2.28
14
+ Requires-Dist: dnspython>=2.1
15
+ Requires-Dist: whois>=0.9
16
+ Requires-Dist: beautifulsoup4>=4.9
17
+ Requires-Dist: lxml>=4.6
18
+ Requires-Dist: colorama>=0.4
19
+ Requires-Dist: tldextract>=3.1
20
+ Requires-Dist: pwntools>=4.0
21
+ Requires-Dist: playwright>=1.40
22
+ Requires-Dist: pyee>=10.0
23
+ Requires-Dist: greenlet>=3.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: twine; extra == "dev"
26
+ Requires-Dist: build; extra == "dev"
27
+ Dynamic: author
28
+ Dynamic: home-page
29
+ Dynamic: requires-python
30
+
31
+ # webpeek
32
+
33
+ OSINT CLI tool for web reconnaissance - gather passive and active information about websites.
34
+
35
+ ## Installation
36
+
37
+ ### From PyPI (recommended when published)
38
+ ```bash
39
+ pip install webpeek
40
+ ```
41
+
42
+ ### From GitHub
43
+ ```bash
44
+ pip install git+https://github.com/JorgeRosbel/webpeek.git
45
+ ```
46
+
47
+ ### Local development
48
+ ```bash
49
+ git clone https://github.com/JorgeRosbel/webpeek.git
50
+ cd webpeek
51
+ pip install -e .
52
+ ```
53
+
54
+ ## Usage
55
+
56
+ ```bash
57
+ # Basic scan
58
+ webpeek example.com
59
+
60
+ # Hybrid mode (active + passive)
61
+ webpeek example.com -H
62
+
63
+ # Passive only
64
+ webpeek example.com -P
65
+
66
+ # Active only
67
+ webpeek example.com -A
68
+
69
+ # Dynamic mode (for JavaScript sites)
70
+ webpeek example.com -H -y
71
+
72
+ # Specific modules
73
+ webpeek example.com -T -l -e -p
74
+
75
+ # Save to file
76
+ webpeek example.com -H -oN result.txt
77
+
78
+ # Without colors
79
+ webpeek example.com -C
80
+
81
+ # Help
82
+ webpeek --help
83
+ ```
84
+
85
+ ## Options
86
+
87
+ | Short | Flag | Description |
88
+ |-------|------|-------------|
89
+ | `-H` | `--hybrid` | Run both active and passive modules |
90
+ | `-A` | `--active` | Run all active modules |
91
+ | `-P` | `--passive` | Run all passive modules |
92
+ | `-y` | `--dynamic` | Use headless browser (Playwright) |
93
+ | `-oN` | `--output` | Save output to file |
94
+ | `-C` | `--no-color` | Disable colors |
95
+ | `-v` | `--verbose` | Verbose output |
96
+
97
+ ---
98
+
99
+ ## Passive Modules
100
+
101
+ Gather information from public sources without connecting directly to the target.
102
+
103
+ | Short | Flag | Description |
104
+ |-------|------|-------------|
105
+ | `-w` | `--whois` | Domain registration info |
106
+ | `-d` | `--dns` | DNS A records |
107
+ | `-m` | `--mx` | MX records |
108
+ | `-t` | `--txt` | TXT records (SPF, DKIM) |
109
+ | `-S` | `--subdomains` | Find subdomains |
110
+
111
+ ---
112
+
113
+ ## Active Modules
114
+
115
+ Gather information by connecting directly to the target.
116
+
117
+ | Short | Flag | Description |
118
+ |-------|------|-------------|
119
+ | `-h` | `--headers` | HTTP headers |
120
+ | `-c` | `--security` | Security headers |
121
+ | `-T` | `--tech` | Detect technologies |
122
+ | `-W` | `--wplugins` | WordPress plugins |
123
+ | `-s` | `--ssl` | SSL certificate info |
124
+ | `-g` | `--geo` | Geolocation |
125
+ | `-O` | `--os` | OS detection |
126
+ | `-i` | `--title` | Page title |
127
+ | `-D` | `--description` | Meta description |
128
+ | `-e` | `--emails` | Extract emails |
129
+ | `-p` | `--phones` | Extract phone numbers |
130
+ | `-M` | `--sitemap` | Sitemap URLs |
131
+ | `-r` | `--robots` | Robots.txt |
132
+ | `-l` | `--social` | Social networks |
133
+
134
+ ---
135
+
136
+ ## Dynamic Mode
137
+
138
+ Use `-y` for JavaScript-rendered sites (React, Vue, Angular, etc.):
139
+
140
+ ```bash
141
+ webpeek example.com -T -y
142
+ ```
143
+
144
+ First time using dynamic mode will download Chromium (~150MB).
145
+
146
+ ---
147
+
148
+ ## Example Output
149
+
150
+ ```
151
+ ╔════════════════════════════════════════════════════════╗
152
+ ║ 🌐 WEBPEEK v1.0.0 - example.com ║
153
+ ╚════════════════════════════════════════════════════════╝
154
+
155
+ 📡 PASSIVE MODE
156
+ ─────────────────────
157
+ ◉ WHOIS:
158
+ └─ Registrar: NameCheap, Inc.
159
+ └─ Created: 2025-12-14
160
+ └─ Expires: 2026-12-14
161
+
162
+ ◉ DNS:
163
+ └─ 93.184.216.34
164
+
165
+ ⚡ ACTIVE MODE
166
+ ─────────────────────
167
+ ◉ IP:
168
+ └─ 93.184.216.34
169
+
170
+ ◉ Geo:
171
+ └─ US, California (Cloudflare, Inc.)
172
+
173
+ ◉ Technologies:
174
+ └─ Cloudflare
175
+ └─ Nginx
176
+
177
+ ◉ SSL:
178
+ └─ example.com (expires in 90 days)
179
+
180
+ ╔════════════════════════════════════════════════════════╗
181
+ ║ Scan complete! ║
182
+ ╚════════════════════════════════════════════════════════╝
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Uninstall
188
+
189
+ ```bash
190
+ pip uninstall webpeek
191
+ ```
192
+
193
+ ## License
194
+
195
+ MIT
@@ -0,0 +1,165 @@
1
+ # webpeek
2
+
3
+ OSINT CLI tool for web reconnaissance - gather passive and active information about websites.
4
+
5
+ ## Installation
6
+
7
+ ### From PyPI (recommended when published)
8
+ ```bash
9
+ pip install webpeek
10
+ ```
11
+
12
+ ### From GitHub
13
+ ```bash
14
+ pip install git+https://github.com/JorgeRosbel/webpeek.git
15
+ ```
16
+
17
+ ### Local development
18
+ ```bash
19
+ git clone https://github.com/JorgeRosbel/webpeek.git
20
+ cd webpeek
21
+ pip install -e .
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ```bash
27
+ # Basic scan
28
+ webpeek example.com
29
+
30
+ # Hybrid mode (active + passive)
31
+ webpeek example.com -H
32
+
33
+ # Passive only
34
+ webpeek example.com -P
35
+
36
+ # Active only
37
+ webpeek example.com -A
38
+
39
+ # Dynamic mode (for JavaScript sites)
40
+ webpeek example.com -H -y
41
+
42
+ # Specific modules
43
+ webpeek example.com -T -l -e -p
44
+
45
+ # Save to file
46
+ webpeek example.com -H -oN result.txt
47
+
48
+ # Without colors
49
+ webpeek example.com -C
50
+
51
+ # Help
52
+ webpeek --help
53
+ ```
54
+
55
+ ## Options
56
+
57
+ | Short | Flag | Description |
58
+ |-------|------|-------------|
59
+ | `-H` | `--hybrid` | Run both active and passive modules |
60
+ | `-A` | `--active` | Run all active modules |
61
+ | `-P` | `--passive` | Run all passive modules |
62
+ | `-y` | `--dynamic` | Use headless browser (Playwright) |
63
+ | `-oN` | `--output` | Save output to file |
64
+ | `-C` | `--no-color` | Disable colors |
65
+ | `-v` | `--verbose` | Verbose output |
66
+
67
+ ---
68
+
69
+ ## Passive Modules
70
+
71
+ Gather information from public sources without connecting directly to the target.
72
+
73
+ | Short | Flag | Description |
74
+ |-------|------|-------------|
75
+ | `-w` | `--whois` | Domain registration info |
76
+ | `-d` | `--dns` | DNS A records |
77
+ | `-m` | `--mx` | MX records |
78
+ | `-t` | `--txt` | TXT records (SPF, DKIM) |
79
+ | `-S` | `--subdomains` | Find subdomains |
80
+
81
+ ---
82
+
83
+ ## Active Modules
84
+
85
+ Gather information by connecting directly to the target.
86
+
87
+ | Short | Flag | Description |
88
+ |-------|------|-------------|
89
+ | `-h` | `--headers` | HTTP headers |
90
+ | `-c` | `--security` | Security headers |
91
+ | `-T` | `--tech` | Detect technologies |
92
+ | `-W` | `--wplugins` | WordPress plugins |
93
+ | `-s` | `--ssl` | SSL certificate info |
94
+ | `-g` | `--geo` | Geolocation |
95
+ | `-O` | `--os` | OS detection |
96
+ | `-i` | `--title` | Page title |
97
+ | `-D` | `--description` | Meta description |
98
+ | `-e` | `--emails` | Extract emails |
99
+ | `-p` | `--phones` | Extract phone numbers |
100
+ | `-M` | `--sitemap` | Sitemap URLs |
101
+ | `-r` | `--robots` | Robots.txt |
102
+ | `-l` | `--social` | Social networks |
103
+
104
+ ---
105
+
106
+ ## Dynamic Mode
107
+
108
+ Use `-y` for JavaScript-rendered sites (React, Vue, Angular, etc.):
109
+
110
+ ```bash
111
+ webpeek example.com -T -y
112
+ ```
113
+
114
+ First time using dynamic mode will download Chromium (~150MB).
115
+
116
+ ---
117
+
118
+ ## Example Output
119
+
120
+ ```
121
+ ╔════════════════════════════════════════════════════════╗
122
+ ║ 🌐 WEBPEEK v1.0.0 - example.com ║
123
+ ╚════════════════════════════════════════════════════════╝
124
+
125
+ 📡 PASSIVE MODE
126
+ ─────────────────────
127
+ ◉ WHOIS:
128
+ └─ Registrar: NameCheap, Inc.
129
+ └─ Created: 2025-12-14
130
+ └─ Expires: 2026-12-14
131
+
132
+ ◉ DNS:
133
+ └─ 93.184.216.34
134
+
135
+ ⚡ ACTIVE MODE
136
+ ─────────────────────
137
+ ◉ IP:
138
+ └─ 93.184.216.34
139
+
140
+ ◉ Geo:
141
+ └─ US, California (Cloudflare, Inc.)
142
+
143
+ ◉ Technologies:
144
+ └─ Cloudflare
145
+ └─ Nginx
146
+
147
+ ◉ SSL:
148
+ └─ example.com (expires in 90 days)
149
+
150
+ ╔════════════════════════════════════════════════════════╗
151
+ ║ Scan complete! ║
152
+ ╚════════════════════════════════════════════════════════╝
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Uninstall
158
+
159
+ ```bash
160
+ pip uninstall webpeek
161
+ ```
162
+
163
+ ## License
164
+
165
+ MIT
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "webpeek"
7
+ version = "1.2.1"
8
+ description = "OSINT CLI tool for web reconnaissance"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ authors = [
12
+ {name = "JorgeRosbel", email = "jorge@rosbel.dev"}
13
+ ]
14
+ requires-python = ">=3.8"
15
+ dependencies = [
16
+ "click>=8.0",
17
+ "requests>=2.28",
18
+ "dnspython>=2.1",
19
+ "whois>=0.9",
20
+ "beautifulsoup4>=4.9",
21
+ "lxml>=4.6",
22
+ "colorama>=0.4",
23
+ "tldextract>=3.1",
24
+ "pwntools>=4.0",
25
+ "playwright>=1.40",
26
+ "pyee>=10.0",
27
+ "greenlet>=3.0",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/JorgeRosbel/webpeek"
32
+
33
+ [project.scripts]
34
+ webpeek = "webpeek.cli:cli"
35
+
36
+ [project.optional-dependencies]
37
+ dev = ["twine", "build"]
38
+
39
+ [tool.setuptools.packages.find]
40
+ where = ["."]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
webpeek-1.2.1/setup.py ADDED
@@ -0,0 +1,42 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name='webpeek',
5
+ version='1.2.0',
6
+ description='OSINT CLI tool for web reconnaissance',
7
+ author='JorgeRosbel',
8
+ author_email='jorge@rosbel.dev',
9
+ url='https://github.com/JorgeRosbel/webpeek',
10
+ packages=find_packages(),
11
+ install_requires=[
12
+ 'click>=8.0',
13
+ 'requests>=2.28',
14
+ 'dnspython>=2.1',
15
+ 'Whois>=0.9',
16
+ 'beautifulsoup4>=4.9',
17
+ 'lxml>=4.6',
18
+ 'colorama>=0.4',
19
+ 'tldextract>=3.1',
20
+ 'pwntools>=4.0',
21
+ 'playwright>=1.40',
22
+ 'pyee>=10.0',
23
+ 'greenlet>=3.0',
24
+ ],
25
+ entry_points={
26
+ 'console_scripts': [
27
+ 'webpeek=webpeek.cli:cli',
28
+ ],
29
+ },
30
+ python_requires='>=3.8',
31
+ classifiers=[
32
+ 'Development Status :: 4 - Beta',
33
+ 'Intended Audience :: Developers',
34
+ 'License :: OSI Approved :: MIT License',
35
+ 'Programming Language :: Python :: 3',
36
+ 'Programming Language :: Python :: 3.8',
37
+ 'Programming Language :: Python :: 3.9',
38
+ 'Programming Language :: Python :: 3.10',
39
+ 'Programming Language :: Python :: 3.11',
40
+ 'Programming Language :: Python :: 3.12',
41
+ ],
42
+ )
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0"
@@ -0,0 +1,137 @@
1
+ import click
2
+ import tldextract
3
+ import socket
4
+ import re
5
+ from pwn import log
6
+ from webpeek.core.scanner import Scanner
7
+ from webpeek.core.output import print_results, save_to_file
8
+ from webpeek.utils.colors import Colors, RESET
9
+
10
+
11
+ def is_valid_domain(domain):
12
+ pattern = r'^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$'
13
+ return bool(re.match(pattern, domain))
14
+
15
+
16
+ def validate_target(target):
17
+ ext = tldextract.extract(target)
18
+ if not ext.domain or not ext.suffix:
19
+ return None
20
+
21
+ domain = f"{ext.domain}.{ext.suffix}"
22
+
23
+ if not is_valid_domain(domain):
24
+ return None
25
+
26
+ try:
27
+ socket.gethostbyname(domain)
28
+ return domain
29
+ except socket.gaierror:
30
+ return None
31
+
32
+
33
+ ALL_ACTIVE_MODULES = ['headers', 'tech', 'ssl', 'geo', 'title', 'description', 'security', 'wplugins', 'sitemap', 'robots', 'os', 'emails', 'phones']
34
+ ALL_PASSIVE_MODULES = ['whois', 'dns', 'mx', 'txt', 'subdomains']
35
+
36
+
37
+ @click.command()
38
+ @click.argument('target')
39
+ @click.option('-H', '--hybrid', 'hybrid_mode', is_flag=True, help='Run both active and passive modules')
40
+ @click.option('-A', '--active', is_flag=True, help='Run all active modules')
41
+ @click.option('-P', '--passive', is_flag=True, help='Run all passive modules')
42
+ @click.option('-w', '--whois', is_flag=True, help='WHOIS lookup')
43
+ @click.option('-d', '--dns', is_flag=True, help='DNS lookup')
44
+ @click.option('-m', '--mx', is_flag=True, help='MX records')
45
+ @click.option('-t', '--txt', is_flag=True, help='TXT records (SPF, DKIM)')
46
+ @click.option('-h', '--headers', is_flag=True, help='HTTP headers')
47
+ @click.option('-T', '--tech', is_flag=True, help='Detect technologies')
48
+ @click.option('-s', '--ssl', is_flag=True, help='SSL certificate info')
49
+ @click.option('-g', '--geo', is_flag=True, help='Geolocation')
50
+ @click.option('-O', '--os', is_flag=True, help='OS detection via TTL')
51
+ @click.option('-i', '--title', is_flag=True, help='Page title')
52
+ @click.option('-D', '--description', 'desc', is_flag=True, help='Meta description')
53
+ @click.option('-e', '--emails', is_flag=True, help='Extract emails')
54
+ @click.option('-p', '--phones', is_flag=True, help='Extract phone numbers')
55
+ @click.option('-S', '--subdomains', is_flag=True, help='Find subdomains')
56
+ @click.option('-c', '--security', is_flag=True, help='Security headers audit')
57
+ @click.option('-W', '--wplugins', is_flag=True, help='WordPress plugins')
58
+ @click.option('-M', '--sitemap', is_flag=True, help='Extract URLs from sitemap')
59
+ @click.option('-r', '--robots', is_flag=True, help='Fetch robots.txt')
60
+ @click.option('-oN', '--output', type=click.Path(), help='Save output to file')
61
+ @click.option('-C', '--no-color', is_flag=True, help='Disable colors')
62
+ @click.option('-v', '--verbose', is_flag=True, help='Verbose output')
63
+ def cli(target, hybrid_mode, active, passive, whois, dns, mx, txt, headers, tech, ssl, geo, os, title, desc, emails, phones, subdomains, security, wplugins, sitemap, robots, output, no_color, verbose):
64
+ use_color = not no_color
65
+
66
+ domain = validate_target(target)
67
+ if not domain:
68
+ print(f"{Colors.error('[ERROR] Invalid domain: ' + target)}")
69
+ return
70
+
71
+ scanner = Scanner(domain, verbose, use_color)
72
+
73
+ active_modules = []
74
+ passive_modules = []
75
+
76
+ specific_flags = [whois, dns, mx, txt, subdomains, headers, tech, ssl, geo, os, title, desc, emails, phones, security, wplugins, sitemap, robots]
77
+ is_custom = any(specific_flags) and not hybrid_mode and not active and not passive
78
+
79
+ if hybrid_mode:
80
+ active_modules = ALL_ACTIVE_MODULES.copy()
81
+ passive_modules = ALL_PASSIVE_MODULES.copy()
82
+ elif active:
83
+ active_modules = ALL_ACTIVE_MODULES.copy()
84
+ elif passive:
85
+ passive_modules = ALL_PASSIVE_MODULES.copy()
86
+ elif any(specific_flags):
87
+ active_modules = []
88
+ passive_modules = []
89
+ else:
90
+ active_modules = ALL_ACTIVE_MODULES.copy()
91
+ passive_modules = ALL_PASSIVE_MODULES.copy()
92
+
93
+ if whois: passive_modules.append('whois')
94
+ if dns: passive_modules.append('dns')
95
+ if mx: passive_modules.append('mx')
96
+ if txt: passive_modules.append('txt')
97
+ if subdomains: passive_modules.append('subdomains')
98
+ if emails: active_modules.append('emails')
99
+ if phones: active_modules.append('phones')
100
+ if headers: active_modules.append('headers')
101
+ if tech: active_modules.append('tech')
102
+ if ssl: active_modules.append('ssl')
103
+ if geo: active_modules.append('geo')
104
+ if os: active_modules.append('os')
105
+ if title: active_modules.append('title')
106
+ if desc: active_modules.append('description')
107
+ if security: active_modules.append('security')
108
+ if wplugins: active_modules.append('wplugins')
109
+ if sitemap: active_modules.append('sitemap')
110
+ if robots: active_modules.append('robots')
111
+
112
+ active_modules = list(set(active_modules))
113
+ passive_modules = list(set(passive_modules))
114
+
115
+ results = {}
116
+
117
+ if passive_modules:
118
+ results.update(scanner.scan_passive(passive_modules))
119
+
120
+ if active_modules:
121
+ results.update(scanner.scan_active(active_modules))
122
+
123
+ print_results(domain, results, use_color, is_custom)
124
+
125
+ if output:
126
+ from webpeek.utils.colors import format_plain
127
+ plain_output = format_plain(domain, results)
128
+
129
+ if save_to_file(output, plain_output):
130
+ if use_color:
131
+ click.echo(f"{Colors.pass_('Results saved to ' + output)}")
132
+ else:
133
+ click.echo(f"Results saved to {output}")
134
+
135
+
136
+ if __name__ == '__main__':
137
+ cli()
File without changes
@@ -0,0 +1,51 @@
1
+ from webpeek.utils.colors import format_output, Colors
2
+
3
+
4
+ def format_list_value(value):
5
+ if isinstance(value, dict):
6
+ return value
7
+
8
+ if not isinstance(value, list):
9
+ return str(value)
10
+
11
+ if not value:
12
+ return ""
13
+
14
+ if isinstance(value[0], dict):
15
+ if 'name' in value[0] and 'version' in value[0]:
16
+ return "\n".join(f" • {v['name']} ({v['version']})" for v in value)
17
+ return "\n".join(f" • {v}" for v in value)
18
+
19
+ return "\n".join(f" • {v}" for v in value)
20
+
21
+
22
+ def print_results(target, results, use_color=True, is_custom=False):
23
+ clean_results = {}
24
+ for key, value in results.items():
25
+ if key == 'IP' and is_custom:
26
+ continue
27
+ if value and (isinstance(value, dict) or str(value).strip()):
28
+ if isinstance(value, dict):
29
+ clean_results[key] = value
30
+ else:
31
+ formatted = format_list_value(value)
32
+ clean_results[key] = formatted[:200]
33
+
34
+ if not clean_results:
35
+ if use_color:
36
+ print(f"{Colors.warning('No results found')}")
37
+ else:
38
+ print("No results found")
39
+ return
40
+
41
+ output = format_output(target, clean_results, use_color, is_custom)
42
+ print(output)
43
+
44
+
45
+ def save_to_file(filename, content):
46
+ try:
47
+ with open(filename, 'w') as f:
48
+ f.write(content)
49
+ return True
50
+ except Exception as e:
51
+ return False