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,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hackerone
|
|
3
|
+
description: HackerOne bug bounty automation - parses scope CSVs, deploys parallel pentesting agents per asset, validates PoCs, and generates platform-ready submission reports.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# HackerOne Bug Bounty
|
|
7
|
+
|
|
8
|
+
Automates: scope parsing → parallel testing per asset → **authoritative finding validation** → submission reports.
|
|
9
|
+
|
|
10
|
+
## Quick start
|
|
11
|
+
|
|
12
|
+
1. Input: HackerOne program URL or scope CSV.
|
|
13
|
+
2. Parse scope and program guidelines.
|
|
14
|
+
3. Spawn one coordinator per eligible asset (parallel).
|
|
15
|
+
4. Each coordinator runs the standard engagement flow (see `skills/coordination/SKILL.md`).
|
|
16
|
+
5. Run the **`validate-findings` workflow per asset** (authoritative submission gate). Generate HackerOne reports from the validated set ONLY — never submit a finding that is not `VALID`/`REPAIRED`.
|
|
17
|
+
|
|
18
|
+
## Scope CSV format
|
|
19
|
+
|
|
20
|
+
Expected columns:
|
|
21
|
+
- `identifier` — asset URL/domain.
|
|
22
|
+
- `asset_type` — URL, WILDCARD, API, CIDR.
|
|
23
|
+
- `eligible_for_submission` — must be `true`.
|
|
24
|
+
- `max_severity` — critical / high / medium / low.
|
|
25
|
+
- `instruction` — asset-specific notes.
|
|
26
|
+
|
|
27
|
+
Parse with `skills/hackerone/tools/csv_parser.py`. Filter for `eligible_for_submission=true`.
|
|
28
|
+
|
|
29
|
+
## Agent deployment
|
|
30
|
+
|
|
31
|
+
One coordinator per asset, spawned in parallel:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
coordinator_role = Read("skills/coordination/SKILL.md")
|
|
35
|
+
Agent(prompt=f"{coordinator_role}\n\nTARGET: {asset_url}\nSCOPE: {program_guidelines}\nOUTPUT_DIR: ...",
|
|
36
|
+
run_in_background=True)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
10 assets → 10 parallel coordinators (~2-4 h vs 20-40 h sequential). Each coordinator follows `skills/coordination/SKILL.md` and `reference/role-matrix.md`.
|
|
40
|
+
|
|
41
|
+
## Finding validation (authoritative submission gate)
|
|
42
|
+
|
|
43
|
+
Every finding requires `poc.py` (executable exploit), `poc_output.txt` (timestamped execution proof), manual repro steps, and evidence (screenshots / HTTP captures / video). This is the input the validator consumes.
|
|
44
|
+
|
|
45
|
+
**After each asset's coordinator returns, the orchestrator runs the `validate-findings` workflow for that asset and submits ONLY findings it marks `VALID` or `REPAIRED`:**
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
v = Workflow(name="validate-findings", args={
|
|
49
|
+
"output_dir": asset_output_dir, # the coordinator's OUTPUT_DIR
|
|
50
|
+
"target": asset_url,
|
|
51
|
+
"business_tier": "revenue", # production/external bug-bounty scope; else "unknown"
|
|
52
|
+
"votes": 3, # adversarial refuters per finding (bounty = stricter)
|
|
53
|
+
"repair": True, # regenerate broken/absent PoCs so each finding has a runnable evidence script
|
|
54
|
+
"strict": True, # one failed gate => REJECTED
|
|
55
|
+
})
|
|
56
|
+
# v.counts -> {total, valid, repaired, rejected}; v.validated[] / v.rejected[]
|
|
57
|
+
# Verdicts at {asset_output_dir}/artifacts/validated/{id}.json (+ false-positives/).
|
|
58
|
+
# Asset report at {asset_output_dir}/reports/validation-report.md.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`validate-findings` is the authoritative gate because it directly preempts the most common HackerOne rejections: it verifies each CVE against **NVD + a recomputed CVSS base score (from the vector) + CISA KEV + the vendor advisory**, **runs (and repairs) every PoC** until it emits the evidence that proves the issue, **recomputes the risk/severity**, corroborates every claim against raw evidence, and kills false positives via **adversarial refutation**.
|
|
62
|
+
|
|
63
|
+
The orchestrator is the only layer that runs this workflow (it is top-level; the workflow is one level below — legal nesting). To avoid double work, coordinators may treat their **interleaved per-finding validators** (run the instant each candidate is materialized) as a provisional self-check; the standalone `validate-findings` verdict is authoritative for submission. A `REJECTED` finding is a false positive — it never enters a submission, an appendix, or a count; its `artifacts/false-positives/{id}.json` is the sole record. Likewise a coordinator-dropped candidate (uncured DEMOTED, in `artifacts/dropped/`) is never submitted.
|
|
64
|
+
|
|
65
|
+
The HackerOne PoC contract is a superset of the standard finding contract (`skills/coordination/reference/validator-role.md`).
|
|
66
|
+
|
|
67
|
+
## Submission report format
|
|
68
|
+
|
|
69
|
+
Build each report from a validated finding only. Required sections per HackerOne standard:
|
|
70
|
+
1. Summary (2-3 sentences).
|
|
71
|
+
2. Severity (CVSS + business impact; v4.0 primary, v3.1 → v3.0 → v2.0 fallback) — use the score `validate-findings` recomputed from the vector and the risk bucket it assigned, not a hand-typed number.
|
|
72
|
+
3. Steps to Reproduce (numbered, clear) — mirror the validated `verification-script.py`.
|
|
73
|
+
4. Visual Evidence (from `evidence/validation/`).
|
|
74
|
+
5. Impact (realistic attack scenario).
|
|
75
|
+
6. Remediation (actionable fixes).
|
|
76
|
+
|
|
77
|
+
Validate report *format* with `skills/hackerone/tools/report_validator.py` (complements the finding validation).
|
|
78
|
+
|
|
79
|
+
## Output structure
|
|
80
|
+
|
|
81
|
+
Standard `OUTPUT_DIR` (`skills/coordination/reference/output-discipline.md`) plus a per-asset `reports/submissions/` containing the platform-ready markdown. `validate-findings` writes the verdicts under `artifacts/validated|false-positives/` and the asset `reports/validation-report.md`.
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
{OUTPUT_DIR}/
|
|
85
|
+
├── findings/
|
|
86
|
+
│ └── finding-NNN/evidence/validation/ # validate-findings proof packages
|
|
87
|
+
├── reports/
|
|
88
|
+
│ ├── submissions/ # built ONLY from artifacts/validated/
|
|
89
|
+
│ │ ├── H1_CRITICAL_001.md
|
|
90
|
+
│ │ └── H1_HIGH_001.md
|
|
91
|
+
│ ├── validation-report.md # validate-findings asset report
|
|
92
|
+
│ └── SUBMISSION_GUIDE.md
|
|
93
|
+
├── recon/
|
|
94
|
+
├── logs/
|
|
95
|
+
└── artifacts/
|
|
96
|
+
├── validated/ # {id}.json — submit these
|
|
97
|
+
└── false-positives/ # {id}.json — never submit these
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Program selection
|
|
101
|
+
|
|
102
|
+
**High-value:** new programs (< 30 days), fast response (< 24 h), high bounties, large attack surface. **Avoid:** slow response (> 1 week), low bounties, restrictive scope.
|
|
103
|
+
|
|
104
|
+
## Submission checklist
|
|
105
|
+
|
|
106
|
+
- [ ] **`validate-findings` verdict = `VALID` or `REPAIRED`** (`artifacts/validated/{id}.json` exists). Never submit a `REJECTED` finding.
|
|
107
|
+
- [ ] CVSS **recomputed from the vector** (v4.0 primary; v3.1 → v3.0 → v2.0 fallback) and matching NVD when a CVE applies (`evidence/validation/cve-verification.md`).
|
|
108
|
+
- [ ] Working PoC with `poc_output.txt` (the validator re-ran/repaired it and confirmed the evidence token).
|
|
109
|
+
- [ ] Step-by-step reproduction.
|
|
110
|
+
- [ ] Visual evidence.
|
|
111
|
+
- [ ] Realistic impact (risk score/bucket from `evidence/validation/risk-assessment.md`).
|
|
112
|
+
- [ ] Remediation guidance.
|
|
113
|
+
- [ ] Sensitive data sanitized.
|
|
114
|
+
- [ ] Asset is `eligible_for_submission=true`.
|
|
115
|
+
|
|
116
|
+
## Common rejections (preempt)
|
|
117
|
+
|
|
118
|
+
| Rejection | Prevention |
|
|
119
|
+
|-----------|------------|
|
|
120
|
+
| Out of Scope | Verify `eligible_for_submission=true` and asset-type match |
|
|
121
|
+
| Cannot Reproduce | `validate-findings` re-runs (and repairs) the PoC 3× and requires a vuln-class evidence token — only reproducible findings pass the gate |
|
|
122
|
+
| Duplicate | Search disclosed reports before submission; submit quickly |
|
|
123
|
+
| Insufficient Impact | Document realistic attack scenario; `validate-findings` recomputes risk/severity so the rating is defensible |
|
|
124
|
+
| Incorrect severity / CVSS | `validate-findings` recomputes the base score from the vector and cross-checks NVD + KEV; any mismatch is flagged before you submit |
|
|
125
|
+
|
|
126
|
+
## Tools
|
|
127
|
+
|
|
128
|
+
- `skills/hackerone/tools/csv_parser.py` — parse HackerOne scope CSVs.
|
|
129
|
+
- **`validate-findings` workflow** — authoritative per-asset finding validation (NVD/CVSS-math/KEV/exploit-run/repair/risk/adversarial). The submission gate; run once per asset.
|
|
130
|
+
- `skills/hackerone/tools/report_validator.py` — validate report *format* completeness (the 6 submission sections). Complements `validate-findings` (which validates the *finding*); run it on each report built from a validated finding.
|
|
131
|
+
- `skills/coordination/SKILL.md` — coordinator scaffold.
|
|
132
|
+
|
|
133
|
+
## Usage
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
/hackerone <program_url_or_csv_path>
|
|
137
|
+
```
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Sensitive Data Metadata Integration Guide
|
|
2
|
+
|
|
3
|
+
How to track sensitive data discovery in HackerOne workflows using `SensitiveDataTracker`.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from tools.sensitive_data_tracker import SensitiveDataTracker
|
|
9
|
+
|
|
10
|
+
tracker = SensitiveDataTracker(
|
|
11
|
+
program_name="ACME Corp Bug Bounty",
|
|
12
|
+
asset_identifier="https://api.example.com",
|
|
13
|
+
output_dir="{OUTPUT_DIR}",
|
|
14
|
+
)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Logging Discoveries
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
# Credentials
|
|
21
|
+
tracker.add_credentials(
|
|
22
|
+
username="admin", password_hash="$2y$10$abc...", account_type="admin",
|
|
23
|
+
location="SQLi in /search", finding_id="finding-001",
|
|
24
|
+
hash_algorithm="bcrypt",
|
|
25
|
+
evidence={"poc_script": "findings/finding-001/poc.py",
|
|
26
|
+
"screenshot": "evidence/screenshots/finding-001-admin.png"},
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# API keys
|
|
30
|
+
tracker.add_api_key(
|
|
31
|
+
key_id="sk_live_abc123xyz", key_preview="sk_live_****...xyz",
|
|
32
|
+
scope=["read:users", "write:data", "admin"],
|
|
33
|
+
location="Hardcoded in React app.js",
|
|
34
|
+
finding_id="finding-003", token_type="Stripe API Key",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Private keys
|
|
38
|
+
tracker.add_private_key(
|
|
39
|
+
key_type="RSA", key_length=2048,
|
|
40
|
+
purpose="AWS EC2 prod key pair",
|
|
41
|
+
location=".git/config in repo",
|
|
42
|
+
finding_id="finding-005",
|
|
43
|
+
systems_accessible=["prod-api", "prod-db", "prod-cache"],
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Database credentials
|
|
47
|
+
tracker.add_database_credentials(
|
|
48
|
+
database_type="PostgreSQL", host="db.internal.company.com", port=5432,
|
|
49
|
+
database_name="production_users",
|
|
50
|
+
location="Config file via path traversal",
|
|
51
|
+
finding_id="finding-002", records_affected=2547,
|
|
52
|
+
evidence={"poc_script": "findings/finding-002/poc.py",
|
|
53
|
+
"data_sample": "evidence/http-captures/finding-002-db-dump.txt"},
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# PII access
|
|
57
|
+
tracker.add_user_pii(
|
|
58
|
+
pii_types=["email", "phone", "home_address", "dob"],
|
|
59
|
+
records_affected=2547,
|
|
60
|
+
location="DB via SQLi", finding_id="finding-001",
|
|
61
|
+
affected_jurisdictions=["EU", "California"],
|
|
62
|
+
evidence={"sample_record": "evidence/screenshots/finding-001-pii-sample.png",
|
|
63
|
+
"count_proof": "evidence/http-captures/finding-001-record-count.txt"},
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Finalize and Export
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
tracker.finalize()
|
|
71
|
+
report_path = tracker.export_summary() # markdown
|
|
72
|
+
# JSON saved to {OUTPUT_DIR}/artifacts/sensitive_data_metadata.json
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Integration Points
|
|
76
|
+
|
|
77
|
+
### Hunter agent
|
|
78
|
+
|
|
79
|
+
Initialize at testing start, process each returned finding through a dispatch helper that maps `credentials_found`, `api_keys_found`, `private_keys_found`, etc. into the corresponding `tracker.add_*` calls. Call `tracker.finalize()` and `tracker.export_summary()` at the end.
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
def process_finding(finding_data, tracker):
|
|
83
|
+
for cred in finding_data.get("credentials_found", []):
|
|
84
|
+
tracker.add_credentials(
|
|
85
|
+
username=cred["username"], password_hash=cred["password_hash"],
|
|
86
|
+
account_type=cred["type"], location=cred["location"],
|
|
87
|
+
finding_id=finding_data["finding_id"],
|
|
88
|
+
)
|
|
89
|
+
for key in finding_data.get("api_keys_found", []):
|
|
90
|
+
tracker.add_api_key(
|
|
91
|
+
key_id=key["key_id"], key_preview=key["preview"],
|
|
92
|
+
scope=key["scope"], location=key["location"],
|
|
93
|
+
finding_id=finding_data["finding_id"], token_type=key["type"],
|
|
94
|
+
)
|
|
95
|
+
# ... handle other categories
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Pentester agent — extracting indicators from PoC output
|
|
99
|
+
|
|
100
|
+
Inspect `poc_output.txt` and tag indicators by regex match before passing to the tracker.
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
def collect_indicators(finding):
|
|
104
|
+
indicators = {k: [] for k in
|
|
105
|
+
("credentials_found","api_keys_found","private_keys_found",
|
|
106
|
+
"database_credentials","user_pii_accessed","config_data_exposed")}
|
|
107
|
+
poc_output = open(finding["poc_output.txt"]).read()
|
|
108
|
+
if re.search(r"username[:\s]+\w+", poc_output):
|
|
109
|
+
indicators["credentials_found"].append({"detected_in": "poc_output", "type": "username"})
|
|
110
|
+
if re.search(r"(sk_live|pk_test|Bearer|Authorization)", poc_output):
|
|
111
|
+
indicators["api_keys_found"].append({"detected_in": "poc_output", "type": "api_key"})
|
|
112
|
+
if re.search(r"-----BEGIN.*PRIVATE KEY-----", poc_output):
|
|
113
|
+
indicators["private_keys_found"].append({"detected_in": "poc_output", "type": "private_key"})
|
|
114
|
+
return indicators
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Detection Patterns
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
CREDENTIAL_PATTERNS = [
|
|
121
|
+
r"username[:\s]+(['\"]?)(\w+)\1",
|
|
122
|
+
r"password[:\s]+(['\"]?)(.+?)\1",
|
|
123
|
+
r"user[:\s]+(['\"]?)(\w+)\1",
|
|
124
|
+
r"pass[:\s]+(['\"]?)(.+?)\1",
|
|
125
|
+
r"admin[:\s]+(['\"]?)(\w+)\1",
|
|
126
|
+
]
|
|
127
|
+
API_KEY_PATTERNS = [
|
|
128
|
+
r"(sk_live|sk_test)_[A-Za-z0-9]{20,}",
|
|
129
|
+
r"(pk_live|pk_test)_[A-Za-z0-9]{20,}",
|
|
130
|
+
r"Bearer\s+[A-Za-z0-9._\-]+",
|
|
131
|
+
r"Authorization[:\s]+Bearer\s+\S+",
|
|
132
|
+
r"api[_-]?key[:\s]+(['\"]?)([A-Za-z0-9_\-]+)\1",
|
|
133
|
+
]
|
|
134
|
+
PRIVATE_KEY_PATTERNS = [
|
|
135
|
+
r"-----BEGIN\s+(?:RSA\s+)?PRIVATE KEY-----",
|
|
136
|
+
r"-----BEGIN\s+EC\s+PRIVATE KEY-----",
|
|
137
|
+
r"-----BEGIN\s+OPENSSH PRIVATE KEY-----",
|
|
138
|
+
r"-----BEGIN\s+PGP PRIVATE KEY BLOCK-----",
|
|
139
|
+
]
|
|
140
|
+
DB_CREDENTIAL_PATTERNS = [
|
|
141
|
+
r"(mongodb|postgres|mysql|mssql)://([^:]+):([^@]+)@",
|
|
142
|
+
r"db[_-]?(user|pass)[:\s]+(['\"]?)(.+?)\2",
|
|
143
|
+
r"database[_-]?(url|connection)[:\s]+(['\"]?)(.+?)\3",
|
|
144
|
+
r"jdbc:.*://(.*):(.*)@",
|
|
145
|
+
]
|
|
146
|
+
PII_PATTERNS = {
|
|
147
|
+
"email": r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
|
|
148
|
+
"phone": r"\+?1?\s*\(?(\d{3})\)?[\s.-]?(\d{3})[\s.-]?(\d{4})",
|
|
149
|
+
"ssn": r"\d{3}-\d{2}-\d{4}",
|
|
150
|
+
"credit_card": r"\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}",
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Output
|
|
155
|
+
|
|
156
|
+
- `{OUTPUT_DIR}/artifacts/sensitive_data_metadata.json` — per-finding records with `program`, `asset_identifier`, dates, `sensitive_data_categories` (credentials / api_keys_and_tokens / private_data / configuration_data / user_pii / other_sensitive), and `summary` block.
|
|
157
|
+
- `{OUTPUT_DIR}/reports/sensitive_data_report.md` — summary grouped by severity, immediate actions, remediation timeline (0-1h / 1-24h / 1-7d / 30d+).
|
|
158
|
+
|
|
159
|
+
## Privacy and Redaction
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
REDACTION_RULES = {
|
|
163
|
+
"passwords": "[REDACTED]",
|
|
164
|
+
"api_keys": "****...last_4_chars",
|
|
165
|
+
"tokens": "[REDACTED]",
|
|
166
|
+
"private_keys": "[REDACTED]",
|
|
167
|
+
"credit_cards": "****-****-****-last_4",
|
|
168
|
+
"ssn": "***-**-last_4",
|
|
169
|
+
"phone": "[REDACTED]",
|
|
170
|
+
"email": "[REDACTED]",
|
|
171
|
+
"ip_address": "redacted",
|
|
172
|
+
"database_host": "[REDACTED]",
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**GDPR**: document personal-data accessed; notify supervisory authority within 72h on high risk; notify individuals if breached. **CCPA**: document Californian residents' data; respect access/delete rights; file breach notice if PII involved. **General**: minimize PII in reports, secure data in transit/storage, follow responsible-disclosure timeline.
|
|
177
|
+
|
|
178
|
+
## Validation Checklist
|
|
179
|
+
|
|
180
|
+
- [ ] `sensitive_data_metadata.json` generated; each item has `discovered_date`
|
|
181
|
+
- [ ] Evidence references resolve to PoC / screenshots
|
|
182
|
+
- [ ] Severity assigned correctly; impact assessed; remediation guidance present
|
|
183
|
+
- [ ] Markdown report properly redacted; GDPR/CCPA implications noted on PII
|
|
184
|
+
- [ ] Highest-risk finding identified; `sensitive_data_report.md` generated
|
|
185
|
+
|
|
186
|
+
## Troubleshooting
|
|
187
|
+
|
|
188
|
+
- Not tracked → tracker not initialized at start, or discovery not logged.
|
|
189
|
+
- Incomplete JSON → `tracker.finalize()` not called before export.
|
|
190
|
+
- Report missing → call `tracker.export_summary()` after `finalize()`; ensure output dir exists.
|
|
191
|
+
- Unsure what to track → see `formats/sensitive-data-metadata.md` for the 6 categories.
|
|
192
|
+
|
|
193
|
+
## References
|
|
194
|
+
|
|
195
|
+
- `formats/sensitive-data-metadata.md`
|
|
196
|
+
- `.claude/skills/hackerone/tools/sensitive_data_tracker.py`
|
|
197
|
+
- GDPR: https://gdpr-info.eu/
|
|
198
|
+
- CCPA: https://oag.ca.gov/privacy/ccpa
|
|
199
|
+
- HackerOne: https://www.hackerone.com/
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Sensitive Data Tracking Reference
|
|
2
|
+
|
|
3
|
+
Track and document all credentials, tokens, API keys, and sensitive data discovered during HackerOne testing.
|
|
4
|
+
|
|
5
|
+
## Data Categories
|
|
6
|
+
|
|
7
|
+
| Category | Types | Default Severity |
|
|
8
|
+
|----------|-------|-----------------|
|
|
9
|
+
| Credentials | Usernames, passwords, hashes, service accounts | CRITICAL |
|
|
10
|
+
| API Keys & Tokens | Bearer tokens, API keys, OAuth tokens, JWTs | HIGH |
|
|
11
|
+
| Private Data | Private keys (RSA/EC/SSH), certificates | CRITICAL |
|
|
12
|
+
| Configuration Data | Database connection strings, config files, env vars | CRITICAL |
|
|
13
|
+
| User PII | Email, phone, address, SSN, DOB | CRITICAL |
|
|
14
|
+
| Other Sensitive | Internal IPs, architecture info, service discovery | MEDIUM |
|
|
15
|
+
|
|
16
|
+
## Metadata Schema
|
|
17
|
+
|
|
18
|
+
Each engagement produces a `sensitive_data_metadata.json`:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"program": "Program Name",
|
|
23
|
+
"program_handle": "handle",
|
|
24
|
+
"asset_identifier": "https://example.com",
|
|
25
|
+
"asset_type": "URL",
|
|
26
|
+
"testing_date_start": "2025-01-16T10:00:00Z",
|
|
27
|
+
"testing_date_end": "2025-01-16T14:30:00Z",
|
|
28
|
+
"tester": "Pentester Agent",
|
|
29
|
+
"sensitive_data_categories": {
|
|
30
|
+
"credentials": [],
|
|
31
|
+
"api_keys_and_tokens": [],
|
|
32
|
+
"private_data": [],
|
|
33
|
+
"configuration_data": [],
|
|
34
|
+
"user_pii": [],
|
|
35
|
+
"other_sensitive": []
|
|
36
|
+
},
|
|
37
|
+
"summary": {
|
|
38
|
+
"total_items_discovered": 0,
|
|
39
|
+
"by_category": {},
|
|
40
|
+
"by_severity": {},
|
|
41
|
+
"highest_risk_finding": null
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Each item in a category array contains: `type`, `location`, `finding_id`, `discovered_date`, `data` (redacted), `evidence` (file refs), `impact_assessment` (severity, scope), `remediation`, `status`.
|
|
47
|
+
|
|
48
|
+
## Detection Patterns
|
|
49
|
+
|
|
50
|
+
### Credentials
|
|
51
|
+
```python
|
|
52
|
+
CREDENTIAL_PATTERNS = [
|
|
53
|
+
r"username[:\s]+(['\"]?)(\w+)\1",
|
|
54
|
+
r"password[:\s]+(['\"]?)(.+?)\1",
|
|
55
|
+
r"admin[:\s]+(['\"]?)(\w+)\1"
|
|
56
|
+
]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### API Keys
|
|
60
|
+
```python
|
|
61
|
+
API_KEY_PATTERNS = [
|
|
62
|
+
r"(sk_live|sk_test)_[A-Za-z0-9]{20,}",
|
|
63
|
+
r"(pk_live|pk_test)_[A-Za-z0-9]{20,}",
|
|
64
|
+
r"Bearer\s+[A-Za-z0-9._\-]+",
|
|
65
|
+
r"api[_-]?key[:\s]+(['\"]?)([A-Za-z0-9_\-]+)\1"
|
|
66
|
+
]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Private Keys
|
|
70
|
+
```python
|
|
71
|
+
PRIVATE_KEY_PATTERNS = [
|
|
72
|
+
r"-----BEGIN\s+(?:RSA\s+)?PRIVATE KEY-----",
|
|
73
|
+
r"-----BEGIN\s+EC\s+PRIVATE KEY-----",
|
|
74
|
+
r"-----BEGIN\s+OPENSSH PRIVATE KEY-----"
|
|
75
|
+
]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Database Credentials
|
|
79
|
+
```python
|
|
80
|
+
DB_CREDENTIAL_PATTERNS = [
|
|
81
|
+
r"(mongodb|postgres|mysql|mssql)://([^:]+):([^@]+)@",
|
|
82
|
+
r"jdbc:.*://(.*):(.*)@"
|
|
83
|
+
]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### PII
|
|
87
|
+
```python
|
|
88
|
+
PII_PATTERNS = {
|
|
89
|
+
"email": r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
|
|
90
|
+
"phone": r"\+?1?\s*\(?(\d{3})\)?[\s.-]?(\d{3})[\s.-]?(\d{4})",
|
|
91
|
+
"ssn": r"\d{3}-\d{2}-\d{4}",
|
|
92
|
+
"credit_card": r"\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}"
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Redaction Rules
|
|
97
|
+
|
|
98
|
+
| Data Type | Redaction Format |
|
|
99
|
+
|-----------|-----------------|
|
|
100
|
+
| Passwords | `[REDACTED]` |
|
|
101
|
+
| API keys | `****...last_4_chars` |
|
|
102
|
+
| Tokens | `[REDACTED]` |
|
|
103
|
+
| Private keys | `[REDACTED]` |
|
|
104
|
+
| Credit cards | `****-****-****-last_4` |
|
|
105
|
+
| SSN | `***-**-last_4` |
|
|
106
|
+
| Phone/Email | `[REDACTED]` |
|
|
107
|
+
| Database hosts | `[REDACTED]` |
|
|
108
|
+
|
|
109
|
+
**Safe to show**: Key ID/fingerprint, last 4 chars, key type/algorithm, scope/permissions, account type.
|
|
110
|
+
|
|
111
|
+
## Tracker API
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from tools.sensitive_data_tracker import SensitiveDataTracker
|
|
115
|
+
|
|
116
|
+
tracker = SensitiveDataTracker(program_name, asset_identifier, output_dir)
|
|
117
|
+
|
|
118
|
+
tracker.add_credentials(username, password_hash, account_type, location, finding_id, ...)
|
|
119
|
+
tracker.add_api_key(key_id, key_preview, scope, location, finding_id, ...)
|
|
120
|
+
tracker.add_private_key(key_type, key_length, purpose, location, finding_id, ...)
|
|
121
|
+
tracker.add_database_credentials(database_type, host, port, database_name, location, finding_id, ...)
|
|
122
|
+
tracker.add_user_pii(pii_types, records_affected, location, finding_id, ...)
|
|
123
|
+
tracker.add_configuration_data(...)
|
|
124
|
+
tracker.add_other_sensitive_data(...)
|
|
125
|
+
|
|
126
|
+
tracker.finalize()
|
|
127
|
+
tracker.export_summary() # -> sensitive_data_report.md + sensitive_data_metadata.json
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Compliance Notes
|
|
131
|
+
|
|
132
|
+
- **GDPR**: Document all personal data accessed. Notify supervisory authority within 72 hours if high risk. Notify affected individuals.
|
|
133
|
+
- **CCPA**: Document California resident data accessed. Provide right to access/delete. File breach notification if applicable.
|
|
134
|
+
- **General**: Minimize PII in reports. Secure all data during testing. Follow responsible disclosure practices.
|
|
File without changes
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HackerOne Scope CSV Parser
|
|
3
|
+
|
|
4
|
+
Parses HackerOne scope CSV files and extracts eligible assets for testing.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import csv
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import List, Dict, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def parse_scope_csv(csv_path: str) -> List[Dict[str, str]]:
|
|
13
|
+
"""
|
|
14
|
+
Parse HackerOne scope CSV file and extract eligible assets.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
csv_path: Path to the CSV scope file
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
List of asset dictionaries with parsed fields
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
>>> assets = parse_scope_csv("scopes_for_program.csv")
|
|
24
|
+
>>> print(assets[0])
|
|
25
|
+
{
|
|
26
|
+
'identifier': 'example.com',
|
|
27
|
+
'asset_type': 'URL',
|
|
28
|
+
'max_severity': 'critical',
|
|
29
|
+
'instruction': '',
|
|
30
|
+
'eligible_for_bounty': True,
|
|
31
|
+
'eligible_for_submission': True
|
|
32
|
+
}
|
|
33
|
+
"""
|
|
34
|
+
assets = []
|
|
35
|
+
csv_file = Path(csv_path)
|
|
36
|
+
|
|
37
|
+
if not csv_file.exists():
|
|
38
|
+
raise FileNotFoundError(f"CSV file not found: {csv_path}")
|
|
39
|
+
|
|
40
|
+
with open(csv_file, 'r', encoding='utf-8') as f:
|
|
41
|
+
reader = csv.DictReader(f)
|
|
42
|
+
|
|
43
|
+
# Validate required columns
|
|
44
|
+
required_columns = ['identifier', 'asset_type', 'eligible_for_submission']
|
|
45
|
+
if not all(col in reader.fieldnames for col in required_columns):
|
|
46
|
+
raise ValueError(f"CSV missing required columns: {required_columns}")
|
|
47
|
+
|
|
48
|
+
for row_num, row in enumerate(reader, start=2): # Start at 2 (header is 1)
|
|
49
|
+
# Only include assets eligible for submission
|
|
50
|
+
if row.get('eligible_for_submission', '').lower() != 'true':
|
|
51
|
+
continue
|
|
52
|
+
|
|
53
|
+
asset = {
|
|
54
|
+
'identifier': row['identifier'].strip(),
|
|
55
|
+
'asset_type': row['asset_type'].strip(),
|
|
56
|
+
'max_severity': row.get('max_severity', 'critical').strip(),
|
|
57
|
+
'instruction': row.get('instruction', '').strip(),
|
|
58
|
+
'eligible_for_bounty': row.get('eligible_for_bounty', 'true').lower() == 'true',
|
|
59
|
+
'eligible_for_submission': True,
|
|
60
|
+
'csv_row': row_num
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Validate identifier is not empty
|
|
64
|
+
if not asset['identifier']:
|
|
65
|
+
print(f"Warning: Skipping row {row_num} - empty identifier")
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
assets.append(asset)
|
|
69
|
+
|
|
70
|
+
return assets
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def categorize_assets(assets: List[Dict[str, str]]) -> Dict[str, List[Dict[str, str]]]:
|
|
74
|
+
"""
|
|
75
|
+
Categorize assets by type for organized testing.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
assets: List of parsed assets
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Dictionary mapping asset types to asset lists
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
>>> categorized = categorize_assets(assets)
|
|
85
|
+
>>> print(categorized.keys())
|
|
86
|
+
dict_keys(['URL', 'WILDCARD', 'API', 'CIDR'])
|
|
87
|
+
"""
|
|
88
|
+
categorized = {}
|
|
89
|
+
|
|
90
|
+
for asset in assets:
|
|
91
|
+
asset_type = asset['asset_type']
|
|
92
|
+
if asset_type not in categorized:
|
|
93
|
+
categorized[asset_type] = []
|
|
94
|
+
categorized[asset_type].append(asset)
|
|
95
|
+
|
|
96
|
+
return categorized
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def filter_by_severity(assets: List[Dict[str, str]], min_severity: str = 'low') -> List[Dict[str, str]]:
|
|
100
|
+
"""
|
|
101
|
+
Filter assets by minimum severity level.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
assets: List of parsed assets
|
|
105
|
+
min_severity: Minimum severity ('critical', 'high', 'medium', 'low')
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Filtered list of assets
|
|
109
|
+
"""
|
|
110
|
+
severity_levels = {'low': 0, 'medium': 1, 'high': 2, 'critical': 3}
|
|
111
|
+
min_level = severity_levels.get(min_severity.lower(), 0)
|
|
112
|
+
|
|
113
|
+
filtered = []
|
|
114
|
+
for asset in assets:
|
|
115
|
+
asset_level = severity_levels.get(asset['max_severity'].lower(), 0)
|
|
116
|
+
if asset_level >= min_level:
|
|
117
|
+
filtered.append(asset)
|
|
118
|
+
|
|
119
|
+
return filtered
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def get_bounty_eligible_assets(assets: List[Dict[str, str]]) -> List[Dict[str, str]]:
|
|
123
|
+
"""
|
|
124
|
+
Filter to only bounty-eligible assets.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
assets: List of parsed assets
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
List of bounty-eligible assets
|
|
131
|
+
"""
|
|
132
|
+
return [asset for asset in assets if asset.get('eligible_for_bounty', False)]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def generate_summary(assets: List[Dict[str, str]]) -> str:
|
|
136
|
+
"""
|
|
137
|
+
Generate a summary of parsed assets.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
assets: List of parsed assets
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Formatted summary string
|
|
144
|
+
"""
|
|
145
|
+
total = len(assets)
|
|
146
|
+
bounty_eligible = len(get_bounty_eligible_assets(assets))
|
|
147
|
+
categorized = categorize_assets(assets)
|
|
148
|
+
|
|
149
|
+
summary = f"Total assets: {total}\n"
|
|
150
|
+
summary += f"Bounty eligible: {bounty_eligible}\n\n"
|
|
151
|
+
summary += "By type:\n"
|
|
152
|
+
|
|
153
|
+
for asset_type, type_assets in sorted(categorized.items()):
|
|
154
|
+
summary += f" {asset_type}: {len(type_assets)}\n"
|
|
155
|
+
|
|
156
|
+
summary += "\nBy severity:\n"
|
|
157
|
+
severity_counts = {}
|
|
158
|
+
for asset in assets:
|
|
159
|
+
sev = asset['max_severity']
|
|
160
|
+
severity_counts[sev] = severity_counts.get(sev, 0) + 1
|
|
161
|
+
|
|
162
|
+
for sev in ['critical', 'high', 'medium', 'low']:
|
|
163
|
+
if sev in severity_counts:
|
|
164
|
+
summary += f" {sev.capitalize()}: {severity_counts[sev]}\n"
|
|
165
|
+
|
|
166
|
+
return summary
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
if __name__ == "__main__":
|
|
170
|
+
import sys
|
|
171
|
+
|
|
172
|
+
if len(sys.argv) < 2:
|
|
173
|
+
print("Usage: python csv_parser.py <csv_file>")
|
|
174
|
+
sys.exit(1)
|
|
175
|
+
|
|
176
|
+
csv_path = sys.argv[1]
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
assets = parse_scope_csv(csv_path)
|
|
180
|
+
print(generate_summary(assets))
|
|
181
|
+
print(f"\nParsed {len(assets)} eligible assets from {csv_path}")
|
|
182
|
+
|
|
183
|
+
except Exception as e:
|
|
184
|
+
print(f"Error: {e}")
|
|
185
|
+
sys.exit(1)
|