Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,3 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GPG_FINGERPRINT: ${{ secrets.GPG_FINGERPRINT }}
COSIGN_KEY: ${{ secrets.COSIGN_KEY }}
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
COSIGN_CERT_PATH: "."
COSIGN_SIG_PATH: "."
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ profile.out
# Output
/output/
/reports/
*.html
*.html
*.sarif

# HTML reports (but not docs/)
/reports/*.html
/*.html

# Env
.env
.env.local
Expand Down
28 changes: 11 additions & 17 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,19 @@ builds:

archives:
- id: nice_scan
format: tar.gz
formats: [tar.gz]
format_overrides:
- goos: windows
format: zip
formats: [zip]
name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}_{{.Arch}}"
files:
- README.md
- LICENSE

checksum:
name_template: "{{.ProjectName}}_{{.Version}}_checksums.txt"
algorithm: sha256

# ── GPG sign the checksums file ──
# ── Signing (GPG + cosign/Sigstore) ──
signs:
- id: checksum_sign
cmd: gpg
Expand All @@ -53,18 +52,15 @@ signs:
artifacts: checksum
signature: "${artifact}.sig"

# ── cosign — Sigstore signing for SLSA provenance ──
# Requires: COSIGN_KEY and COSIGN_PASSWORD env vars
# https://docs.sigstore.dev/cosign/overview/
cosigns:
- id: cosign_blob
artifacts: checksum
cmd: cosign
signature: "${artifact}.sigstore.json"
args:
- sign-blob
- "--output-certificate={{.Env.COSIGN_CERT_PATH}}/{{.ProjectName}}_{{.Version}}_checksums.pem"
- "--output-signature={{.Env.COSIGN_SIG_PATH}}/{{.ProjectName}}_{{.Version}}_checksums.sig"
- "--bundle=${signature}"
- "${artifact}"
certificate: "${artifact}.pem"
- "--yes"
artifacts: checksum
output: true

# ── SBOM with syft (software bill of materials) ──
Expand Down Expand Up @@ -100,10 +96,9 @@ release:
# 3. Verify GPG signature
gpg --verify nice_scan_{{.Version}}_checksums.txt.sig

# 4. Verify Sigstore (cosign)
# 4. Verify Sigstore (cosign — keyless OIDC)
cosign verify-blob \
--certificate nice_scan_{{.Version}}_checksums.pem \
--signature nice_scan_{{.Version}}_checksums.sig \
--bundle nice_scan_{{.Version}}_checksums.sigstore.json \
nice_scan_{{.Version}}_checksums.txt
```

Expand All @@ -112,8 +107,7 @@ release:
|------|-------------|
| `nice_scan_{{.Version}}_checksums.txt` | SHA256 checksums for all binaries |
| `nice_scan_{{.Version}}_checksums.txt.sig` | GPG signature of checksums |
| `nice_scan_{{.Version}}_checksums.pem` | Sigstore certificate |
| `nice_scan_{{.Version}}_checksums.sig` | Sigstore signature |
| `nice_scan_{{.Version}}_checksums.sigstore.json` | Cosign Sigstore bundle (keyless) |
| `nice_scan_{{.Version}}_sbom.spdx.json` | SPDX SBOM (dependencies) |

## 🚀 Quick Start
Expand Down
200 changes: 200 additions & 0 deletions cmd/nice_scan/attack_report.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NICE HACKER — Attack Report</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #1a1b26; color: #c0caf5; padding: 40px; line-height: 1.6;
}
.container { max-width: 1100px; margin: 0 auto; }

.header {
border-bottom: 2px solid #3B4261; padding-bottom: 20px; margin-bottom: 30px;
}
.header h1 { color: #7DCFFF; font-size: 28px; margin-bottom: 5px; }
.header .subtitle { color: #565F89; font-size: 14px; }

.meta-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 30px; }
.meta-card {
background: #24283B; border: 1px solid #3B4261; border-radius: 8px; padding: 15px;
}
.meta-card .label { color: #565F89; font-size: 11px; text-transform: uppercase; letter-spacing: 1px; }
.meta-card .value { color: #C0CAF5; font-size: 18px; font-weight: bold; margin-top: 5px; }

.risk-critical { color: #F7768E; }
.risk-high { color: #FF9E64; }
.risk-medium { color: #E0AF68; }
.risk-low { color: #7DCFFF; }

section { margin-bottom: 30px; }
section h2 { color: #7DCFFF; font-size: 18px; margin-bottom: 15px; padding-bottom: 8px; border-bottom: 1px solid #3B4261; }

.finding {
background: #24283B; border-left: 3px solid #3B4261; border-radius: 6px; padding: 12px 16px; margin-bottom: 10px;
}
.finding.critical { border-left-color: #F7768E; }
.finding.high { border-left-color: #FF9E64; }
.finding.medium { border-left-color: #E0AF68; }
.finding.low { border-left-color: #7DCFFF; }
.finding.info { border-left-color: #565F89; }
.finding .sev { font-weight: bold; font-size: 11px; text-transform: uppercase; letter-spacing: 1px; }
.finding .name { font-size: 14px; margin: 3px 0; }
.finding .desc { color: #9AA5CE; font-size: 12px; }
.finding .evid { color: #565F89; font-size: 11px; margin-top: 5px; word-break: break-all; }

.chain {
background: linear-gradient(135deg, #24283B 0%, #2a1e3a 100%);
border: 1px solid #F7768E; border-radius: 8px; padding: 15px; margin-bottom: 10px;
}
.chain .chain-name { color: #F7768E; font-weight: bold; font-size: 15px; }
.chain .chain-risk { font-size: 13px; margin: 5px 0; }

.cap-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.cap-badge {
background: #1a3a2a; color: #9ECE6A; border: 1px solid #2d5a3a;
border-radius: 20px; padding: 3px 12px; font-size: 12px;
}

.endpoint { font-family: 'Fira Code', monospace; padding: 4px 0; }
.endpoint .method {
display: inline-block; width: 50px; text-align: center; font-weight: bold;
border-radius: 3px; padding: 1px 4px; margin-right: 8px; font-size: 11px;
}
.method-GET { background: #1a3a5a; color: #7DCFFF; }
.method-POST { background: #3a3a1a; color: #E0AF68; }
.method-PUT { background: #3a2a1a; color: #FF9E64; }
.method-DELETE { background: #3a1a1a; color: #F7768E; }

.page-item { padding: 6px 0; }
.page-item .url { color: #7DCFFF; }
.page-item .meta { color: #565F89; font-size: 12px; }

.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #3B4261; color: #565F89; font-size: 12px; text-align: center; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>NICE HACKER — Attack Report</h1>
<div class="subtitle">Autonomous penetration test generated on May 29, 2026 at 12:44</div>
</div>

<div class="meta-grid">
<div class="meta-card">
<div class="label">Target</div>
<div class="value" style="font-size:14px">https://example.com</div>
</div>
<div class="meta-card">
<div class="label">Duration</div>
<div class="value">16s</div>
</div>
<div class="meta-card">
<div class="label">Steps</div>
<div class="value">7</div>
</div>
<div class="meta-card">
<div class="label">Risk Score</div>
<div class="value critical">10.0 / 10</div>
</div>
</div>
<section>
<h2>Impact Summary</h2>
<p style="color:#9AA5CE">Multiple attack chains detected; Secrets → Cloud Compromise</p>
</section>
<section>
<h2>Attack Chains Discovered</h2>
<div class="chain">
<div class="chain-name">⚡ Secrets → Cloud Compromise</div>
<div class="chain-risk risk-critical">Risk: 10/10</div>
<div style="color:#9AA5CE;font-size:13px">→ Exposed secrets (AWS keys, tokens) allow cloud account compromise</div>
</div>
</section>
<section>
<h2>Findings</h2>
<div class="finding medium">
<div class="sev sev-medium">medium</div>
<div class="name">Subdomains discovered via crt.sh (6)</div>
<div class="desc">Certificate Transparency logs reveal 6 subdomains</div>
<div class="evid">example.com, www.example.com, m.example.com, dev.example.com, products.example.com, support.example.com</div>
</div>
<div class="finding high">
<div class="sev sev-high">high</div>
<div class="name">Interesting subdomain: dev.example.com</div>
<div class="desc">Subdomain &#39;dev.example.com&#39; suggests a potentially sensitive service</div>
<div class="evid">dev.example.com</div>
</div>
<div class="finding medium">
<div class="sev sev-medium">medium</div>
<div class="name">Open port: example.com/80 (HTTP)</div>
<div class="desc">Port 80 open on example.com — HTTP</div>
<div class="evid">example.com:80</div>
</div>
<div class="finding medium">
<div class="sev sev-medium">medium</div>
<div class="name">Open port: example.com/443 (HTTPS)</div>
<div class="desc">Port 443 open on example.com — HTTPS</div>
<div class="evid">example.com:443</div>
</div>
<div class="finding medium">
<div class="sev sev-medium">medium</div>
<div class="name">Open port: www.example.com/80 (HTTP)</div>
<div class="desc">Port 80 open on www.example.com — HTTP</div>
<div class="evid">www.example.com:80</div>
</div>
<div class="finding medium">
<div class="sev sev-medium">medium</div>
<div class="name">Open port: www.example.com/443 (HTTPS)</div>
<div class="desc">Port 443 open on www.example.com — HTTPS</div>
<div class="evid">www.example.com:443</div>
</div>
<div class="finding critical">
<div class="sev sev-critical">critical</div>
<div class="name">Public S3 bucket: dev-static</div>
<div class="desc">Bucket dev-static is publicly accessible</div>
<div class="evid">https://dev-static.s3.amazonaws.com</div>
</div>
<div class="finding info">
<div class="sev sev-info">info</div>
<div class="name">OOB callback server ready</div>
<div class="desc">Listening on port 9999 — use for blind SSRF/SSTI/XSS</div>
<div class="evid">http://localhost:9999/callback</div>
</div>
<div class="finding critical">
<div class="sev sev-critical">critical</div>
<div class="name">Secrets → Cloud Compromise</div>
<div class="desc">Exposed secrets (AWS keys, tokens) allow cloud account compromise</div>
<div class="evid">Risk Score: 10/10</div>
</div>
</section>
<section>
<h2>Capabilities Acquired</h2>
<div class="cap-grid">
<span class="cap-badge">has_subdomain</span>
<span class="cap-badge">has_recon</span>
<span class="cap-badge">has_s3</span>
<span class="cap-badge">has_secret</span>
<span class="cap-badge">has_xss</span>
<span class="cap-badge">has_token</span>
</div>
</section>
<section>
<h2>Endpoints Discovered</h2>
<div class="endpoint"><span class="method method-TCP">TCP</span> <span style="color:#C0CAF5">example.com:80</span> <span style="color:#565F89">[0]</span></div>
<div class="endpoint"><span class="method method-TCP">TCP</span> <span style="color:#C0CAF5">example.com:443</span> <span style="color:#565F89">[0]</span></div>
<div class="endpoint"><span class="method method-TCP">TCP</span> <span style="color:#C0CAF5">www.example.com:80</span> <span style="color:#565F89">[0]</span></div>
<div class="endpoint"><span class="method method-TCP">TCP</span> <span style="color:#C0CAF5">www.example.com:443</span> <span style="color:#565F89">[0]</span></div>
</section>
<section>
<h2>Pages Crawled (1)</h2>
<div class="page-item"><span class="url">https://example.com</span> <span class="meta">(298 bytes, 0 forms, 0 links, 0 JS)</span></div>
</section>
<div class="footer">
NICE HACKER — Attack Complete &mdash; 7 steps in 16 seconds
</div>
</div>
</body>
</html>
9 changes: 1 addition & 8 deletions cmd/nice_scan/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ func exploitCmd() *cobra.Command {

if runIdor {
var startID, endID int
fmt.Sscanf(idorRange, "%d-%d", &startID, &endID)
_, _ = fmt.Sscanf(idorRange, "%d-%d", &startID, &endID)
if startID <= 0 || endID <= 0 {
startID, endID = 1, 10
}
Expand Down Expand Up @@ -748,13 +748,6 @@ func truncateStr(s string, n int) string {
return s[:n-1] + "…"
}

func min(a, b int) int {
if a < b {
return a
}
return b
}

func abs(x int) int {
if x < 0 {
return -x
Expand Down
4 changes: 4 additions & 0 deletions docs/_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# GitHub Pages — no Jekyll processing
include:
- css
- js
Loading
Loading