hackable 1.0.0__py3-none-any.whl
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.
- hackable/__init__.py +3 -0
- hackable/__main__.py +4 -0
- hackable/checks/__init__.py +22 -0
- hackable/checks/cookies.py +65 -0
- hackable/checks/cors.py +68 -0
- hackable/checks/debug.py +43 -0
- hackable/checks/disclosure.py +36 -0
- hackable/checks/exposed_files.py +122 -0
- hackable/checks/headers.py +70 -0
- hackable/checks/methods.py +28 -0
- hackable/checks/open_redirect.py +57 -0
- hackable/checks/rate_limit.py +53 -0
- hackable/checks/robots.py +31 -0
- hackable/checks/securitytxt.py +21 -0
- hackable/checks/sqli.py +152 -0
- hackable/checks/tls.py +89 -0
- hackable/checks/xss.py +83 -0
- hackable/cli.py +117 -0
- hackable/crawler.py +98 -0
- hackable/findings.py +43 -0
- hackable/polite.py +47 -0
- hackable/report.py +244 -0
- hackable/scanner.py +60 -0
- hackable-1.0.0.dist-info/METADATA +104 -0
- hackable-1.0.0.dist-info/RECORD +29 -0
- hackable-1.0.0.dist-info/WHEEL +5 -0
- hackable-1.0.0.dist-info/entry_points.txt +2 -0
- hackable-1.0.0.dist-info/licenses/LICENSE +21 -0
- hackable-1.0.0.dist-info/top_level.txt +1 -0
hackable/report.py
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""Reports: terminal, JSON, and self-contained HTML."""
|
|
2
|
+
|
|
3
|
+
import html
|
|
4
|
+
import json
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
|
|
7
|
+
from . import __version__
|
|
8
|
+
from .findings import SEVERITIES, score_findings
|
|
9
|
+
|
|
10
|
+
RESET = "\033[0m"
|
|
11
|
+
BOLD = "\033[1m"
|
|
12
|
+
DIM = "\033[2m"
|
|
13
|
+
|
|
14
|
+
SEV_COLOR = {
|
|
15
|
+
"critical": "\033[1;31m",
|
|
16
|
+
"high": "\033[31m",
|
|
17
|
+
"medium": "\033[33m",
|
|
18
|
+
"low": "\033[34m",
|
|
19
|
+
"info": "\033[90m",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
SEV_ICON = {
|
|
23
|
+
"critical": "\u2716", # ✖
|
|
24
|
+
"high": "\u2716",
|
|
25
|
+
"medium": "!",
|
|
26
|
+
"low": "i",
|
|
27
|
+
"info": "\u2139",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
VERDICTS = {
|
|
31
|
+
"A": "Solid. Scan again after every deploy to stay here.",
|
|
32
|
+
"B": "Good shape. Fix the mediums when you get an afternoon.",
|
|
33
|
+
"C": "Shaky. The highs above are worth fixing this week.",
|
|
34
|
+
"D": "Risky. An attacker would enjoy this site.",
|
|
35
|
+
"F": "A beginner hacker could break in within minutes. Fix the criticals today.",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
SEV_CSS = {
|
|
39
|
+
"critical": "#ff5555",
|
|
40
|
+
"high": "#ff8c42",
|
|
41
|
+
"medium": "#f1c40f",
|
|
42
|
+
"low": "#5aa9ff",
|
|
43
|
+
"info": "#8a8f98",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _c(text, code, use_color):
|
|
48
|
+
return "%s%s%s" % (code, text, RESET) if use_color else text
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def render_terminal(findings, base, elapsed, req_count, use_color=True):
|
|
52
|
+
score, grade = score_findings(findings)
|
|
53
|
+
L = []
|
|
54
|
+
c = lambda t, code: _c(t, code, use_color) # noqa: E731
|
|
55
|
+
|
|
56
|
+
L.append("")
|
|
57
|
+
L.append(c("hackable v" + __version__, BOLD) + c(" - hack yourself before they do.", DIM))
|
|
58
|
+
L.append("")
|
|
59
|
+
L.append("Target: %s (%d requests in %.1fs)" % (base, req_count, elapsed))
|
|
60
|
+
L.append("")
|
|
61
|
+
|
|
62
|
+
grouped = {s: [f for f in findings if f.severity == s] for s in SEVERITIES}
|
|
63
|
+
if not findings:
|
|
64
|
+
L.append(c("No issues found. Either your site is solid, or it hid well.", BOLD))
|
|
65
|
+
for sev in SEVERITIES:
|
|
66
|
+
items = grouped[sev]
|
|
67
|
+
if not items:
|
|
68
|
+
continue
|
|
69
|
+
L.append(c("%s (%d)" % (sev.upper(), len(items)), SEV_COLOR[sev] + BOLD))
|
|
70
|
+
for f in items:
|
|
71
|
+
L.append(
|
|
72
|
+
" %s %s"
|
|
73
|
+
% (c(SEV_ICON[sev], SEV_COLOR[sev]), c(f.title, BOLD))
|
|
74
|
+
)
|
|
75
|
+
L.append(" What this means: %s" % f.meaning)
|
|
76
|
+
L.append(" Fix: %s" % f.fix)
|
|
77
|
+
if f.url:
|
|
78
|
+
L.append(c(" Found at: %s" % f.url, DIM))
|
|
79
|
+
if f.evidence:
|
|
80
|
+
ev = f.evidence.replace("\n", " ")
|
|
81
|
+
L.append(c(" Evidence: %s%s" % (ev[:140], "..." if len(ev) > 140 else ""), DIM))
|
|
82
|
+
L.append("")
|
|
83
|
+
|
|
84
|
+
L.append(c("-" * 60, DIM))
|
|
85
|
+
L.append(
|
|
86
|
+
"SCORE %s GRADE %s"
|
|
87
|
+
% (c("%d/100" % score, BOLD), c(grade, SEV_COLOR["critical"] if grade in "DF" else BOLD))
|
|
88
|
+
)
|
|
89
|
+
L.append(VERDICTS[grade])
|
|
90
|
+
L.append("")
|
|
91
|
+
return "\n".join(L)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def render_json(findings, base, elapsed, req_count):
|
|
95
|
+
score, grade = score_findings(findings)
|
|
96
|
+
return json.dumps(
|
|
97
|
+
{
|
|
98
|
+
"tool": "hackable " + __version__,
|
|
99
|
+
"target": base,
|
|
100
|
+
"scanned_at": datetime.now(timezone.utc).isoformat(),
|
|
101
|
+
"requests": req_count,
|
|
102
|
+
"elapsed_s": round(elapsed, 1),
|
|
103
|
+
"score": score,
|
|
104
|
+
"grade": grade,
|
|
105
|
+
"findings": [
|
|
106
|
+
{
|
|
107
|
+
"check": f.check,
|
|
108
|
+
"severity": f.severity,
|
|
109
|
+
"title": f.title,
|
|
110
|
+
"meaning": f.meaning,
|
|
111
|
+
"fix": f.fix,
|
|
112
|
+
"evidence": f.evidence,
|
|
113
|
+
"url": f.url,
|
|
114
|
+
}
|
|
115
|
+
for f in findings
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
indent=2,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
SARIF_LEVELS = {
|
|
123
|
+
"critical": "error",
|
|
124
|
+
"high": "error",
|
|
125
|
+
"medium": "warning",
|
|
126
|
+
"low": "note",
|
|
127
|
+
"info": "note",
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def render_sarif(findings, base, elapsed, req_count):
|
|
132
|
+
"""SARIF 2.1.0 output, consumable by GitHub code scanning."""
|
|
133
|
+
rules, seen = [], set()
|
|
134
|
+
for f in findings:
|
|
135
|
+
if f.check not in seen:
|
|
136
|
+
seen.add(f.check)
|
|
137
|
+
rules.append({
|
|
138
|
+
"id": "hackable/" + f.check,
|
|
139
|
+
"name": f.check.replace("_", " ").title(),
|
|
140
|
+
"shortDescription": {"text": f.title},
|
|
141
|
+
"helpUri": "https://github.com/Shifu34/hackable",
|
|
142
|
+
})
|
|
143
|
+
results = []
|
|
144
|
+
for f in findings:
|
|
145
|
+
results.append({
|
|
146
|
+
"ruleId": "hackable/" + f.check,
|
|
147
|
+
"level": SARIF_LEVELS.get(f.severity, "note"),
|
|
148
|
+
"message": {"text": "%s. %s" % (f.title, f.fix or "")},
|
|
149
|
+
"locations": [{
|
|
150
|
+
"physicalLocation": {
|
|
151
|
+
"artifactLocation": {"uri": f.url or base},
|
|
152
|
+
}
|
|
153
|
+
}],
|
|
154
|
+
})
|
|
155
|
+
return json.dumps(
|
|
156
|
+
{
|
|
157
|
+
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
|
|
158
|
+
"version": "2.1.0",
|
|
159
|
+
"runs": [{
|
|
160
|
+
"tool": {"driver": {
|
|
161
|
+
"name": "hackable",
|
|
162
|
+
"version": __version__,
|
|
163
|
+
"informationUri": "https://github.com/Shifu34/hackable",
|
|
164
|
+
"rules": rules,
|
|
165
|
+
}},
|
|
166
|
+
"results": results,
|
|
167
|
+
}],
|
|
168
|
+
},
|
|
169
|
+
indent=2,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def render_html(findings, base, elapsed, req_count):
|
|
173
|
+
score, grade = score_findings(findings)
|
|
174
|
+
esc = html.escape
|
|
175
|
+
cards = []
|
|
176
|
+
for f in findings:
|
|
177
|
+
color = SEV_CSS[f.severity]
|
|
178
|
+
cards.append(
|
|
179
|
+
"""
|
|
180
|
+
<div class="card">
|
|
181
|
+
<div class="card-head" style="border-left-color: {color}">
|
|
182
|
+
<span class="sev" style="background: {color}">{sev}</span>
|
|
183
|
+
<span class="title">{title}</span>
|
|
184
|
+
</div>
|
|
185
|
+
<p><b>What this means:</b> {meaning}</p>
|
|
186
|
+
<p><b>How to fix it:</b> {fix}</p>
|
|
187
|
+
{url}
|
|
188
|
+
{evidence}
|
|
189
|
+
</div>""".format(
|
|
190
|
+
color=color,
|
|
191
|
+
sev=f.severity.upper(),
|
|
192
|
+
title=esc(f.title),
|
|
193
|
+
meaning=esc(f.meaning),
|
|
194
|
+
fix=esc(f.fix),
|
|
195
|
+
url='<p class="meta">Found at: <code>%s</code></p>' % esc(f.url) if f.url else "",
|
|
196
|
+
evidence='<p class="meta">Evidence: <code>%s</code></p>' % esc(f.evidence[:200])
|
|
197
|
+
if f.evidence
|
|
198
|
+
else "",
|
|
199
|
+
)
|
|
200
|
+
)
|
|
201
|
+
if not cards:
|
|
202
|
+
cards.append('<div class="card"><p><b>No issues found.</b></p></div>')
|
|
203
|
+
return """<!DOCTYPE html>
|
|
204
|
+
<html lang="en"><head><meta charset="utf-8">
|
|
205
|
+
<title>hackable report: {base}</title>
|
|
206
|
+
<style>
|
|
207
|
+
body {{ font-family: -apple-system, Segoe UI, Roboto, sans-serif; background: #0d1117;
|
|
208
|
+
color: #e6edf3; max-width: 860px; margin: 0 auto; padding: 32px 20px; }}
|
|
209
|
+
h1 {{ font-size: 22px; }} .dim {{ color: #8a8f98; }}
|
|
210
|
+
.scorebox {{ display: flex; align-items: center; gap: 24px; background: #161b22;
|
|
211
|
+
border: 1px solid #30363d; border-radius: 12px; padding: 20px 24px; margin: 20px 0; }}
|
|
212
|
+
.score {{ font-size: 56px; font-weight: 800; }}
|
|
213
|
+
.grade {{ font-size: 40px; font-weight: 800; width: 72px; height: 72px; border-radius: 50%;
|
|
214
|
+
display: flex; align-items: center; justify-content: center; border: 3px solid; }}
|
|
215
|
+
.card {{ background: #161b22; border: 1px solid #30363d; border-radius: 12px;
|
|
216
|
+
padding: 16px 20px; margin: 14px 0; }}
|
|
217
|
+
.card-head {{ border-left: 5px solid; padding-left: 12px; margin-bottom: 8px;
|
|
218
|
+
display: flex; align-items: center; gap: 10px; }}
|
|
219
|
+
.sev {{ color: #0d1117; font-weight: 700; font-size: 12px; padding: 3px 8px; border-radius: 6px; }}
|
|
220
|
+
.title {{ font-weight: 700; font-size: 16px; }}
|
|
221
|
+
.meta {{ color: #8a8f98; font-size: 13px; }} code {{ font-size: 12.5px; }}
|
|
222
|
+
.footer {{ margin-top: 28px; color: #8a8f98; font-size: 13px; }}
|
|
223
|
+
</style></head><body>
|
|
224
|
+
<h1>hackable report</h1>
|
|
225
|
+
<p class="dim">Target: {base} · {nreq} requests in {elapsed:.1f}s · {date}</p>
|
|
226
|
+
<div class="scorebox">
|
|
227
|
+
<div class="score">{score}<span style="font-size:22px;color:#8a8f98">/100</span></div>
|
|
228
|
+
<div class="grade" style="border-color:{gcolor};color:{gcolor}">{grade}</div>
|
|
229
|
+
<div><b>{verdict}</b></div>
|
|
230
|
+
</div>
|
|
231
|
+
{cards}
|
|
232
|
+
<p class="footer">Generated by hackable " + __version__ + " — hack yourself before they do.
|
|
233
|
+
Only scan apps you own or have permission to test.</p>
|
|
234
|
+
</body></html>""".format(
|
|
235
|
+
base=esc(base),
|
|
236
|
+
nreq=req_count,
|
|
237
|
+
elapsed=elapsed,
|
|
238
|
+
date=datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC"),
|
|
239
|
+
score=score,
|
|
240
|
+
grade=grade,
|
|
241
|
+
gcolor="#ff5555" if grade in "DF" else "#f1c40f" if grade == "C" else "#3fb950",
|
|
242
|
+
verdict=esc(VERDICTS[grade]),
|
|
243
|
+
cards="\n".join(cards),
|
|
244
|
+
)
|
hackable/scanner.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Scan orchestration."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
from .checks import CHECKS
|
|
6
|
+
from .crawler import discover
|
|
7
|
+
from .polite import PoliteClient
|
|
8
|
+
|
|
9
|
+
# checks that probe the crawler's discovered inputs
|
|
10
|
+
INJECTION_CHECKS = ("sqli", "xss", "open_redirect")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def normalize_target(target, http):
|
|
14
|
+
target = target.strip().rstrip("/")
|
|
15
|
+
if "://" not in target:
|
|
16
|
+
for scheme in ("https://", "http://"):
|
|
17
|
+
r = http.get(scheme + target + "/")
|
|
18
|
+
if r is not None and r.status_code < 500:
|
|
19
|
+
return scheme + target
|
|
20
|
+
return "http://" + target
|
|
21
|
+
return target
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def scan(target, http=None, on_check=None, include=None, exclude=None):
|
|
25
|
+
"""Run checks. Returns (base_url, findings, elapsed_s, requests_made).
|
|
26
|
+
|
|
27
|
+
include/exclude are optional sets of check ids (--only / --skip).
|
|
28
|
+
"""
|
|
29
|
+
http = http or PoliteClient()
|
|
30
|
+
base = normalize_target(target, http)
|
|
31
|
+
findings = []
|
|
32
|
+
started = time.time()
|
|
33
|
+
|
|
34
|
+
wanted = [c for c in CHECKS
|
|
35
|
+
if (include is None or c[0] in include)
|
|
36
|
+
and (exclude is None or c[0] not in exclude)]
|
|
37
|
+
|
|
38
|
+
discovered = []
|
|
39
|
+
if any(cid in INJECTION_CHECKS for cid, _l, _f in wanted):
|
|
40
|
+
if on_check:
|
|
41
|
+
on_check("mapping the app")
|
|
42
|
+
try:
|
|
43
|
+
discovered = discover(base, http)
|
|
44
|
+
except Exception:
|
|
45
|
+
discovered = []
|
|
46
|
+
|
|
47
|
+
for cid, label, fn in wanted:
|
|
48
|
+
if on_check:
|
|
49
|
+
on_check(label)
|
|
50
|
+
try:
|
|
51
|
+
if cid in INJECTION_CHECKS:
|
|
52
|
+
findings.extend(fn(base, http, discovered) or [])
|
|
53
|
+
else:
|
|
54
|
+
findings.extend(fn(base, http) or [])
|
|
55
|
+
except RuntimeError:
|
|
56
|
+
break # request budget exceeded: stop gracefully
|
|
57
|
+
except Exception:
|
|
58
|
+
continue # one broken check must never kill the scan
|
|
59
|
+
elapsed = time.time() - started
|
|
60
|
+
return base, findings, elapsed, http.count
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hackable
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Hack yourself before they do. One-command website security check for people who have never run a security tool.
|
|
5
|
+
Author: Shafqat Mehmood
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Shifu34/hackable
|
|
8
|
+
Keywords: security,pentest,scanner,web,vulnerability,dast
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Security
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: requests
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# hackable
|
|
22
|
+
|
|
23
|
+
**Hack yourself before they do.**
|
|
24
|
+
|
|
25
|
+
One-command website security check for people who have never run a security tool. Point it at your app, and in about ten seconds it tells you, in plain English, how a hacker would break in and exactly how to stop them.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
$ pip install hackable
|
|
29
|
+
$ hackable https://myapp.com
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
## The problem
|
|
35
|
+
|
|
36
|
+
Millions of apps are being built with AI right now by people who have never heard of OWASP. The apps work. They also leak database keys, take SQL injection, and let anyone log in as anyone. The existing scanners (ZAP, Nuclei, Burp) are expert tools with expert UX. They might as well be in another language.
|
|
37
|
+
|
|
38
|
+
hackable is the Let's Encrypt of pentesting: free, one command, plain English.
|
|
39
|
+
|
|
40
|
+
## What it checks
|
|
41
|
+
|
|
42
|
+
14 checks. Before probing, hackable crawls your app like a visitor would,
|
|
43
|
+
collecting real links and forms, so injection tests hit your actual inputs
|
|
44
|
+
instead of guessed parameter names.
|
|
45
|
+
|
|
46
|
+
| Check | What it finds |
|
|
47
|
+
|---|---|
|
|
48
|
+
| SQL injection | Error-based probes plus boolean differential checks (safe, GET-only) |
|
|
49
|
+
| Exposed files | Public `/.env`, `/.git/`, config backups, with content verification (no false alarms from SPA fallbacks) |
|
|
50
|
+
| XSS | Your input reflected unescaped into the page |
|
|
51
|
+
| Open redirects | Your site redirecting visitors to attacker domains |
|
|
52
|
+
| CORS | Arbitrary origins trusted with credentials |
|
|
53
|
+
| Debug mode | Stack traces leaking from error pages |
|
|
54
|
+
| Login rate limiting | 10 rapid wrong passwords, checking for pushback |
|
|
55
|
+
| Security headers | HSTS, CSP, X-Frame-Options, and friends |
|
|
56
|
+
| Cookie flags | Session cookies missing Secure, HttpOnly, or SameSite |
|
|
57
|
+
| TLS | Missing HTTPS, expired certs, ancient TLS versions |
|
|
58
|
+
| Version disclosure | Server headers announcing exact versions |
|
|
59
|
+
| HTTP methods | Risky methods like TRACE |
|
|
60
|
+
| robots.txt | Hidden paths handed to attackers on a plate |
|
|
61
|
+
| security.txt | No standard contact for researchers to report issues |
|
|
62
|
+
|
|
63
|
+
Every finding comes with **what this means** (one sentence, no jargon) and **how to fix it** (one sentence, actionable). You get a 0-100 score and an A-F grade.
|
|
64
|
+
|
|
65
|
+
## Output
|
|
66
|
+
|
|
67
|
+
Terminal report, machine-readable JSON (`--json`), SARIF 2.1.0 for GitHub code
|
|
68
|
+
scanning (`--sarif`), and a self-contained shareable HTML report card
|
|
69
|
+
(`--html report.html`).
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
hackable https://myapp.com --html report.html
|
|
73
|
+
hackable https://myapp.com --fail-under 70 # exit 1 in CI if score < 70
|
|
74
|
+
hackable https://myapp.com --only sqli,xss # run a subset of checks
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## GitHub Action
|
|
78
|
+
|
|
79
|
+
Drop it into your workflow and every push gets scanned:
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
- uses: Shifu34/hackable@v1
|
|
83
|
+
with:
|
|
84
|
+
target: https://myapp.com
|
|
85
|
+
fail-under: 70
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Results upload to the Security tab automatically.
|
|
89
|
+
|
|
90
|
+
## Safety
|
|
91
|
+
|
|
92
|
+
hackable only sends harmless probes: no destructive payloads, no data deletion, no login attempts with real credentials, ~50-150 requests with delays and an identifying User-Agent. It will ask for confirmation before scanning unless you pass `--yes`.
|
|
93
|
+
|
|
94
|
+
**Only scan apps you own or have explicit permission to test.** Scanning without permission may be illegal.
|
|
95
|
+
|
|
96
|
+
## Roadmap
|
|
97
|
+
|
|
98
|
+
- Authenticated scans (test behind a login)
|
|
99
|
+
- JavaScript-rendered app support
|
|
100
|
+
- Scheduled scans with diff alerts
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
MIT
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
hackable/__init__.py,sha256=dZjbcpwg8kXe8j8-QOzByBECRIGLst5KKhUNAjDLgno,69
|
|
2
|
+
hackable/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
|
|
3
|
+
hackable/cli.py,sha256=DJMID9N2jVYWF7BgdlxZ0PLuWgnD6C9gREKe8n6enR4,4368
|
|
4
|
+
hackable/crawler.py,sha256=Y-SnvIr_GAeVK36_WL_UjicTMtIjxrb0wxo8BNRBoGg,3174
|
|
5
|
+
hackable/findings.py,sha256=GgIwafofuTzezOgwNVOAlHuD98d0E882kmRFLvmjWtk,975
|
|
6
|
+
hackable/polite.py,sha256=AqRK5-46HlN0ZgfH2inShs7BdWrmxem_5EtqJ8oGwGo,1483
|
|
7
|
+
hackable/report.py,sha256=5mv-mT0SWi2vkTtjdf-g0ktvuQamttUhS20FwX0XuMc,8414
|
|
8
|
+
hackable/scanner.py,sha256=RpV50JkV1WEgJta6psbs8NjvTga4VTrcxXR9-tmOLlE,1875
|
|
9
|
+
hackable/checks/__init__.py,sha256=7KeJ_28kasDva6d6EVYrbcjp9qFl5_j5VuJj4ojID98,1095
|
|
10
|
+
hackable/checks/cookies.py,sha256=23aRpp7uLQxP51OgqdUG7NST82K_PPAA7cvS149SsjM,2464
|
|
11
|
+
hackable/checks/cors.py,sha256=XKLTC14_YuicsSDDsaJJ6BSmTNfTG1G0afyG4PH0D5g,2648
|
|
12
|
+
hackable/checks/debug.py,sha256=uUiVnmVp0gEJzeF8NwcWA7jnSrDY_YDMTwM-rHezFIE,1266
|
|
13
|
+
hackable/checks/disclosure.py,sha256=PwvrEsQDGw5a1-glZxG-BYgSfmKTifHHL6GuG8JGBGI,1129
|
|
14
|
+
hackable/checks/exposed_files.py,sha256=Xd5szLa9_TEA3tjw1s_P_0srhXBjw7y5L-YKiqzIUMY,3933
|
|
15
|
+
hackable/checks/headers.py,sha256=xtqFUHQlYQqSURNyRThd2dwCQ75cIG2sJ_VZGwDgg9I,2452
|
|
16
|
+
hackable/checks/methods.py,sha256=gJcKZN_rGBDq5BD_StpdPBhcSYszaDndsNlgONjxHSs,970
|
|
17
|
+
hackable/checks/open_redirect.py,sha256=DN0GhbPSw8mrnuB6VDrIX_sES87CDDKad-bJu7CK4xI,1908
|
|
18
|
+
hackable/checks/rate_limit.py,sha256=HmESBgfPNi0f1mdH_YBbxneKEVF_ccBM-1ySACjr2oE,1864
|
|
19
|
+
hackable/checks/robots.py,sha256=ZgJTS3hR6DTQPfyOP5oC8aik8fry6kOe7UyfywhWpwg,1068
|
|
20
|
+
hackable/checks/securitytxt.py,sha256=_1MmH7giAx7LqieXnKjMgjDK9gdoQqja4IiOI5ycuV0,903
|
|
21
|
+
hackable/checks/sqli.py,sha256=1yggTDA_o00QtKKYERumDE_KAr7VSd7kCEorwBF6vZ4,5839
|
|
22
|
+
hackable/checks/tls.py,sha256=97zjljxwA7ejj-oZb7JLJwi8VmwVMEeuldnU8wIUCyY,3492
|
|
23
|
+
hackable/checks/xss.py,sha256=V3I1o_HhRPC3zCuVdqzkJKYSS8KhwyVZcL-qBi5qLgA,2868
|
|
24
|
+
hackable-1.0.0.dist-info/licenses/LICENSE,sha256=hPiqvko3E-3DQj4evYl0HLpJAgaXnwIsTSDOB0_PQz8,1072
|
|
25
|
+
hackable-1.0.0.dist-info/METADATA,sha256=y286pySg-C_oTAwojPRjRAJlNV9fDAc2FBmSV0Ot95Y,4009
|
|
26
|
+
hackable-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
27
|
+
hackable-1.0.0.dist-info/entry_points.txt,sha256=3PoYi5B1CfebEKG9oGn0Wpd5493b8ewDsMy6brfM3r4,47
|
|
28
|
+
hackable-1.0.0.dist-info/top_level.txt,sha256=xl05JWEasdJ0My8hhPg-WnE_Kwn0BnczLA5LhHbBl24,9
|
|
29
|
+
hackable-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shafqat Mehmood
|
|
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 @@
|
|
|
1
|
+
hackable
|