open-sploit 1.2.0
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.
- package/LICENSE +21 -0
- package/README.md +75 -0
- package/bin/install.mjs +158 -0
- package/package.json +27 -0
- package/template/agent/offsec.md +53 -0
- package/template/command/target.md +45 -0
- package/template/config/opencode.example.json +46 -0
- package/template/skills/api-security-testing/SKILL.md +61 -0
- package/template/skills/conducting-api-security-testing/LICENSE +201 -0
- package/template/skills/conducting-api-security-testing/SKILL.md +195 -0
- package/template/skills/conducting-api-security-testing/references/api-reference.md +62 -0
- package/template/skills/conducting-api-security-testing/scripts/agent.py +228 -0
- package/template/skills/exploiting-api-injection-vulnerabilities/LICENSE +201 -0
- package/template/skills/exploiting-api-injection-vulnerabilities/SKILL.md +481 -0
- package/template/skills/exploiting-api-injection-vulnerabilities/references/api-reference.md +103 -0
- package/template/skills/exploiting-api-injection-vulnerabilities/scripts/agent.py +142 -0
- package/template/skills/exploiting-http-request-smuggling/LICENSE +201 -0
- package/template/skills/exploiting-http-request-smuggling/SKILL.md +343 -0
- package/template/skills/exploiting-http-request-smuggling/references/api-reference.md +53 -0
- package/template/skills/exploiting-http-request-smuggling/scripts/agent.py +196 -0
- package/template/skills/exploiting-idor-vulnerabilities/LICENSE +201 -0
- package/template/skills/exploiting-idor-vulnerabilities/SKILL.md +309 -0
- package/template/skills/exploiting-idor-vulnerabilities/references/api-reference.md +52 -0
- package/template/skills/exploiting-idor-vulnerabilities/scripts/agent.py +177 -0
- package/template/skills/exploiting-jwt-algorithm-confusion-attack/LICENSE +201 -0
- package/template/skills/exploiting-jwt-algorithm-confusion-attack/SKILL.md +472 -0
- package/template/skills/exploiting-jwt-algorithm-confusion-attack/references/api-reference.md +99 -0
- package/template/skills/exploiting-jwt-algorithm-confusion-attack/scripts/agent.py +132 -0
- package/template/skills/exploiting-nosql-injection-vulnerabilities/LICENSE +201 -0
- package/template/skills/exploiting-nosql-injection-vulnerabilities/SKILL.md +219 -0
- package/template/skills/exploiting-nosql-injection-vulnerabilities/assets/template.md +30 -0
- package/template/skills/exploiting-nosql-injection-vulnerabilities/references/api-reference.md +85 -0
- package/template/skills/exploiting-nosql-injection-vulnerabilities/references/standards.md +19 -0
- package/template/skills/exploiting-nosql-injection-vulnerabilities/references/workflows.md +23 -0
- package/template/skills/exploiting-nosql-injection-vulnerabilities/scripts/agent.py +136 -0
- package/template/skills/exploiting-nosql-injection-vulnerabilities/scripts/process.py +222 -0
- package/template/skills/exploiting-oauth-misconfiguration/LICENSE +201 -0
- package/template/skills/exploiting-oauth-misconfiguration/SKILL.md +316 -0
- package/template/skills/exploiting-oauth-misconfiguration/references/api-reference.md +59 -0
- package/template/skills/exploiting-oauth-misconfiguration/scripts/agent.py +232 -0
- package/template/skills/exploiting-prototype-pollution-in-javascript/LICENSE +201 -0
- package/template/skills/exploiting-prototype-pollution-in-javascript/SKILL.md +239 -0
- package/template/skills/exploiting-prototype-pollution-in-javascript/references/api-reference.md +102 -0
- package/template/skills/exploiting-prototype-pollution-in-javascript/scripts/agent.py +156 -0
- package/template/skills/exploiting-race-condition-vulnerabilities/LICENSE +201 -0
- package/template/skills/exploiting-race-condition-vulnerabilities/SKILL.md +246 -0
- package/template/skills/exploiting-race-condition-vulnerabilities/references/api-reference.md +84 -0
- package/template/skills/exploiting-race-condition-vulnerabilities/scripts/agent.py +119 -0
- package/template/skills/exploiting-server-side-request-forgery/LICENSE +201 -0
- package/template/skills/exploiting-server-side-request-forgery/SKILL.md +354 -0
- package/template/skills/exploiting-server-side-request-forgery/references/api-reference.md +57 -0
- package/template/skills/exploiting-server-side-request-forgery/scripts/agent.py +183 -0
- package/template/skills/exploiting-sql-injection-vulnerabilities/LICENSE +201 -0
- package/template/skills/exploiting-sql-injection-vulnerabilities/SKILL.md +205 -0
- package/template/skills/exploiting-sql-injection-vulnerabilities/references/api-reference.md +60 -0
- package/template/skills/exploiting-sql-injection-vulnerabilities/scripts/agent.py +196 -0
- package/template/skills/exploiting-sql-injection-with-sqlmap/LICENSE +201 -0
- package/template/skills/exploiting-sql-injection-with-sqlmap/SKILL.md +257 -0
- package/template/skills/exploiting-sql-injection-with-sqlmap/references/api-reference.md +74 -0
- package/template/skills/exploiting-sql-injection-with-sqlmap/scripts/agent.py +217 -0
- package/template/skills/exploiting-template-injection-vulnerabilities/LICENSE +201 -0
- package/template/skills/exploiting-template-injection-vulnerabilities/SKILL.md +322 -0
- package/template/skills/exploiting-template-injection-vulnerabilities/references/api-reference.md +55 -0
- package/template/skills/exploiting-template-injection-vulnerabilities/scripts/agent.py +205 -0
- package/template/skills/exploiting-type-juggling-vulnerabilities/LICENSE +201 -0
- package/template/skills/exploiting-type-juggling-vulnerabilities/SKILL.md +272 -0
- package/template/skills/exploiting-type-juggling-vulnerabilities/references/api-reference.md +87 -0
- package/template/skills/exploiting-type-juggling-vulnerabilities/scripts/agent.py +141 -0
- package/template/skills/exploiting-websocket-vulnerabilities/LICENSE +201 -0
- package/template/skills/exploiting-websocket-vulnerabilities/SKILL.md +424 -0
- package/template/skills/exploiting-websocket-vulnerabilities/references/api-reference.md +62 -0
- package/template/skills/exploiting-websocket-vulnerabilities/scripts/agent.py +211 -0
- package/template/skills/hackerone/SKILL.md +137 -0
- package/template/skills/hackerone/reference/INTEGRATION_GUIDE.md +199 -0
- package/template/skills/hackerone/reference/sensitive-data-tracking.md +134 -0
- package/template/skills/hackerone/tools/__init__.py +0 -0
- package/template/skills/hackerone/tools/csv_parser.py +185 -0
- package/template/skills/hackerone/tools/report_validator.py +285 -0
- package/template/skills/hackerone/tools/sensitive_data_tracker.py +495 -0
- package/template/skills/owasp-top-10-testing/SKILL.md +64 -0
- package/template/skills/web-app-penetration-testing/SKILL.md +54 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HackerOne Report Validator
|
|
3
|
+
|
|
4
|
+
Validates vulnerability reports meet HackerOne submission requirements.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Tuple, List, Dict
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ReportValidator:
|
|
13
|
+
"""Validates HackerOne vulnerability reports."""
|
|
14
|
+
|
|
15
|
+
REQUIRED_SECTIONS = [
|
|
16
|
+
"## Summary",
|
|
17
|
+
"## Severity",
|
|
18
|
+
"## Steps to Reproduce",
|
|
19
|
+
"## Impact",
|
|
20
|
+
"## Remediation"
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
# CVSS v4.0 is the primary/canonical version; v3.x vectors are still accepted
|
|
24
|
+
# (fallback ladder). Either shape satisfies the vector-format check.
|
|
25
|
+
_CVSS_V4 = (r"CVSS:4\.0/AV:[NALP]/AC:[LH]/AT:[NP]/PR:[NLH]/UI:[NPA]/"
|
|
26
|
+
r"VC:[HLN]/VI:[HLN]/VA:[HLN]/SC:[HLN]/SI:[HLN]/SA:[HLN]")
|
|
27
|
+
_CVSS_V3 = r"CVSS:3\.\d+/AV:[NALP]/AC:[LH]/PR:[NLH]/UI:[NR]/S:[UC]/C:[NLH]/I:[NLH]/A:[NLH]"
|
|
28
|
+
CVSS_PATTERN = rf"(?:{_CVSS_V4}|{_CVSS_V3})"
|
|
29
|
+
|
|
30
|
+
def __init__(self, report_path: str):
|
|
31
|
+
"""Initialize validator with report path."""
|
|
32
|
+
self.report_path = Path(report_path)
|
|
33
|
+
self.content = ""
|
|
34
|
+
self.errors = []
|
|
35
|
+
self.warnings = []
|
|
36
|
+
|
|
37
|
+
def validate(self) -> Tuple[bool, str]:
|
|
38
|
+
"""
|
|
39
|
+
Validate the report.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Tuple of (is_valid, message)
|
|
43
|
+
"""
|
|
44
|
+
if not self.report_path.exists():
|
|
45
|
+
return False, f"Report file not found: {self.report_path}"
|
|
46
|
+
|
|
47
|
+
with open(self.report_path, 'r', encoding='utf-8') as f:
|
|
48
|
+
self.content = f.read()
|
|
49
|
+
|
|
50
|
+
# Run all validation checks
|
|
51
|
+
self._check_required_sections()
|
|
52
|
+
self._check_cvss_score()
|
|
53
|
+
self._check_steps_to_reproduce()
|
|
54
|
+
self._check_poc_evidence()
|
|
55
|
+
self._check_sensitive_data()
|
|
56
|
+
self._check_report_length()
|
|
57
|
+
self._check_title()
|
|
58
|
+
|
|
59
|
+
# Build result message
|
|
60
|
+
if self.errors:
|
|
61
|
+
message = "Validation FAILED:\n\n"
|
|
62
|
+
message += "Errors:\n"
|
|
63
|
+
for error in self.errors:
|
|
64
|
+
message += f" ❌ {error}\n"
|
|
65
|
+
else:
|
|
66
|
+
message = "Validation PASSED:\n"
|
|
67
|
+
message += " ✅ All required sections present\n"
|
|
68
|
+
message += " ✅ CVSS score valid\n"
|
|
69
|
+
message += " ✅ Steps to reproduce included\n"
|
|
70
|
+
message += " ✅ PoC evidence present\n"
|
|
71
|
+
|
|
72
|
+
if self.warnings:
|
|
73
|
+
message += "\nWarnings:\n"
|
|
74
|
+
for warning in self.warnings:
|
|
75
|
+
message += f" ⚠️ {warning}\n"
|
|
76
|
+
|
|
77
|
+
return len(self.errors) == 0, message
|
|
78
|
+
|
|
79
|
+
def _check_required_sections(self):
|
|
80
|
+
"""Check all required sections are present."""
|
|
81
|
+
missing = []
|
|
82
|
+
for section in self.REQUIRED_SECTIONS:
|
|
83
|
+
if section not in self.content:
|
|
84
|
+
missing.append(section)
|
|
85
|
+
|
|
86
|
+
if missing:
|
|
87
|
+
self.errors.append(f"Missing required sections: {', '.join(missing)}")
|
|
88
|
+
|
|
89
|
+
def _check_cvss_score(self):
|
|
90
|
+
"""Check CVSS score is present and valid."""
|
|
91
|
+
if "CVSS" not in self.content:
|
|
92
|
+
self.errors.append("Missing CVSS score")
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
cvss_match = re.search(self.CVSS_PATTERN, self.content)
|
|
96
|
+
if not cvss_match:
|
|
97
|
+
self.warnings.append("CVSS vector string format may be invalid")
|
|
98
|
+
|
|
99
|
+
def _check_steps_to_reproduce(self):
|
|
100
|
+
"""Check steps to reproduce are clear and numbered."""
|
|
101
|
+
if "## Steps to Reproduce" not in self.content:
|
|
102
|
+
return # Already caught by required sections
|
|
103
|
+
|
|
104
|
+
# Extract steps section
|
|
105
|
+
steps_start = self.content.find("## Steps to Reproduce")
|
|
106
|
+
next_section = self.content.find("##", steps_start + 1)
|
|
107
|
+
steps_section = self.content[steps_start:next_section] if next_section != -1 else self.content[steps_start:]
|
|
108
|
+
|
|
109
|
+
# Check for numbered steps
|
|
110
|
+
if not re.search(r'^\d+\.', steps_section, re.MULTILINE):
|
|
111
|
+
self.warnings.append("Steps to reproduce should be numbered (1. 2. 3.)")
|
|
112
|
+
|
|
113
|
+
# Check section length
|
|
114
|
+
if len(steps_section.strip()) < 100:
|
|
115
|
+
self.warnings.append("Steps to reproduce section seems very short")
|
|
116
|
+
|
|
117
|
+
def _check_poc_evidence(self):
|
|
118
|
+
"""Check for PoC evidence (code blocks, HTTP requests, screenshots)."""
|
|
119
|
+
# Check for code blocks
|
|
120
|
+
if "```" not in self.content:
|
|
121
|
+
self.warnings.append("No code blocks found - include HTTP requests or PoC code")
|
|
122
|
+
|
|
123
|
+
# Check for evidence references
|
|
124
|
+
evidence_keywords = ["screenshot", "image", "video", "evidence", "proof"]
|
|
125
|
+
has_evidence = any(keyword.lower() in self.content.lower() for keyword in evidence_keywords)
|
|
126
|
+
|
|
127
|
+
if not has_evidence:
|
|
128
|
+
self.warnings.append("No evidence references found - include screenshots or videos")
|
|
129
|
+
|
|
130
|
+
def _check_sensitive_data(self):
|
|
131
|
+
"""Check for potential sensitive data leaks."""
|
|
132
|
+
# Common patterns that might indicate sensitive data
|
|
133
|
+
sensitive_patterns = [
|
|
134
|
+
(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', "email addresses"),
|
|
135
|
+
(r'\b\d{16}\b', "potential credit card numbers"),
|
|
136
|
+
(r'password["\s:=]+\w+', "passwords in plain text"),
|
|
137
|
+
(r'api[_-]?key["\s:=]+\w+', "API keys"),
|
|
138
|
+
(r'bearer\s+[A-Za-z0-9._-]+', "bearer tokens"),
|
|
139
|
+
(r'-----BEGIN [A-Z]+ KEY-----', "private keys"),
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
found_sensitive = []
|
|
143
|
+
for pattern, description in sensitive_patterns:
|
|
144
|
+
if re.search(pattern, self.content, re.IGNORECASE):
|
|
145
|
+
found_sensitive.append(description)
|
|
146
|
+
|
|
147
|
+
if found_sensitive:
|
|
148
|
+
self.errors.append(f"Potential sensitive data found: {', '.join(found_sensitive)} - MUST sanitize before submission")
|
|
149
|
+
|
|
150
|
+
def _check_report_length(self):
|
|
151
|
+
"""Check report is not too short or too long."""
|
|
152
|
+
word_count = len(self.content.split())
|
|
153
|
+
|
|
154
|
+
if word_count < 200:
|
|
155
|
+
self.warnings.append(f"Report is very short ({word_count} words) - add more detail")
|
|
156
|
+
elif word_count > 3000:
|
|
157
|
+
self.warnings.append(f"Report is very long ({word_count} words) - consider being more concise")
|
|
158
|
+
|
|
159
|
+
def _check_title(self):
|
|
160
|
+
"""Check for a clear title (first H1 heading)."""
|
|
161
|
+
title_match = re.search(r'^# (.+)$', self.content, re.MULTILINE)
|
|
162
|
+
|
|
163
|
+
if not title_match:
|
|
164
|
+
self.warnings.append("No title found - add # Title at top of report")
|
|
165
|
+
else:
|
|
166
|
+
title = title_match.group(1).strip()
|
|
167
|
+
if len(title) > 100:
|
|
168
|
+
self.warnings.append(f"Title is too long ({len(title)} chars) - keep under 100 characters")
|
|
169
|
+
elif len(title) < 10:
|
|
170
|
+
self.warnings.append("Title is too short - be more descriptive")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def validate_report(report_path: str) -> Tuple[bool, str]:
|
|
174
|
+
"""
|
|
175
|
+
Validate a HackerOne report.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
report_path: Path to the report markdown file
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Tuple of (is_valid, message)
|
|
182
|
+
|
|
183
|
+
Example:
|
|
184
|
+
>>> valid, message = validate_report("finding-001/report.md")
|
|
185
|
+
>>> print(message)
|
|
186
|
+
Validation PASSED:
|
|
187
|
+
✅ All required sections present
|
|
188
|
+
✅ CVSS score valid
|
|
189
|
+
...
|
|
190
|
+
"""
|
|
191
|
+
validator = ReportValidator(report_path)
|
|
192
|
+
return validator.validate()
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def validate_finding_directory(finding_dir: str) -> Tuple[bool, str]:
|
|
196
|
+
"""
|
|
197
|
+
Validate a complete finding directory.
|
|
198
|
+
|
|
199
|
+
Checks for:
|
|
200
|
+
- report.md exists and is valid
|
|
201
|
+
- poc.py or poc.sh exists
|
|
202
|
+
- poc_output.txt exists with recent timestamp
|
|
203
|
+
- workflow.md exists
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
finding_dir: Path to finding directory
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Tuple of (is_valid, message)
|
|
210
|
+
"""
|
|
211
|
+
finding_path = Path(finding_dir)
|
|
212
|
+
errors = []
|
|
213
|
+
warnings = []
|
|
214
|
+
|
|
215
|
+
# Check report.md
|
|
216
|
+
report_path = finding_path / "report.md"
|
|
217
|
+
if not report_path.exists():
|
|
218
|
+
errors.append("report.md not found")
|
|
219
|
+
else:
|
|
220
|
+
valid, msg = validate_report(str(report_path))
|
|
221
|
+
if not valid:
|
|
222
|
+
errors.append(f"report.md validation failed:\n{msg}")
|
|
223
|
+
|
|
224
|
+
# Check PoC script
|
|
225
|
+
poc_py = finding_path / "poc.py"
|
|
226
|
+
poc_sh = finding_path / "poc.sh"
|
|
227
|
+
|
|
228
|
+
if not poc_py.exists() and not poc_sh.exists():
|
|
229
|
+
errors.append("No PoC script found (poc.py or poc.sh)")
|
|
230
|
+
|
|
231
|
+
# Check PoC output
|
|
232
|
+
poc_output = finding_path / "poc_output.txt"
|
|
233
|
+
if not poc_output.exists():
|
|
234
|
+
errors.append("poc_output.txt not found - PoC must be executed and validated")
|
|
235
|
+
else:
|
|
236
|
+
# Check file is not empty
|
|
237
|
+
if poc_output.stat().st_size == 0:
|
|
238
|
+
errors.append("poc_output.txt is empty - run PoC to generate output")
|
|
239
|
+
|
|
240
|
+
# Check workflow
|
|
241
|
+
workflow = finding_path / "workflow.md"
|
|
242
|
+
if not workflow.exists():
|
|
243
|
+
warnings.append("workflow.md not found - include manual exploitation steps")
|
|
244
|
+
|
|
245
|
+
# Build message
|
|
246
|
+
if errors:
|
|
247
|
+
message = f"Finding directory validation FAILED:\n\n"
|
|
248
|
+
message += "Errors:\n"
|
|
249
|
+
for error in errors:
|
|
250
|
+
message += f" ❌ {error}\n"
|
|
251
|
+
else:
|
|
252
|
+
message = "Finding directory validation PASSED:\n"
|
|
253
|
+
message += " ✅ report.md valid\n"
|
|
254
|
+
message += " ✅ PoC script present\n"
|
|
255
|
+
message += " ✅ PoC output validated\n"
|
|
256
|
+
|
|
257
|
+
if warnings:
|
|
258
|
+
message += "\nWarnings:\n"
|
|
259
|
+
for warning in warnings:
|
|
260
|
+
message += f" ⚠️ {warning}\n"
|
|
261
|
+
|
|
262
|
+
return len(errors) == 0, message
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
if __name__ == "__main__":
|
|
266
|
+
import sys
|
|
267
|
+
|
|
268
|
+
if len(sys.argv) < 2:
|
|
269
|
+
print("Usage:")
|
|
270
|
+
print(" python report_validator.py <report.md>")
|
|
271
|
+
print(" python report_validator.py <finding_directory>")
|
|
272
|
+
sys.exit(1)
|
|
273
|
+
|
|
274
|
+
path = Path(sys.argv[1])
|
|
275
|
+
|
|
276
|
+
if path.is_dir():
|
|
277
|
+
valid, message = validate_finding_directory(str(path))
|
|
278
|
+
elif path.is_file():
|
|
279
|
+
valid, message = validate_report(str(path))
|
|
280
|
+
else:
|
|
281
|
+
print(f"Error: {path} is not a file or directory")
|
|
282
|
+
sys.exit(1)
|
|
283
|
+
|
|
284
|
+
print(message)
|
|
285
|
+
sys.exit(0 if valid else 1)
|