diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a0d0522..ba54a88 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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: "." diff --git a/.gitignore b/.gitignore index 8197f6b..993419e 100644 --- a/.gitignore +++ b/.gitignore @@ -40,10 +40,12 @@ profile.out # Output /output/ /reports/ -*.html -*.html *.sarif +# HTML reports (but not docs/) +/reports/*.html +/*.html + # Env .env .env.local diff --git a/.goreleaser.yaml b/.goreleaser.yaml index f2f5761..d73055b 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -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 @@ -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) ── @@ -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 ``` @@ -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 diff --git a/cmd/nice_scan/attack_report.html b/cmd/nice_scan/attack_report.html new file mode 100644 index 0000000..f5cafdc --- /dev/null +++ b/cmd/nice_scan/attack_report.html @@ -0,0 +1,200 @@ + + + + + +NICE HACKER — Attack Report + + + +
+
+

NICE HACKER — Attack Report

+
Autonomous penetration test generated on May 29, 2026 at 12:44
+
+ +
+
+
Target
+
https://example.com
+
+
+
Duration
+
16s
+
+
+
Steps
+
7
+
+
+
Risk Score
+
10.0 / 10
+
+
+
+

Impact Summary

+

Multiple attack chains detected; Secrets → Cloud Compromise

+
+
+

Attack Chains Discovered

+
+
⚡ Secrets → Cloud Compromise
+
Risk: 10/10
+
→ Exposed secrets (AWS keys, tokens) allow cloud account compromise
+
+
+
+

Findings

+
+
medium
+
Subdomains discovered via crt.sh (6)
+
Certificate Transparency logs reveal 6 subdomains
+
example.com, www.example.com, m.example.com, dev.example.com, products.example.com, support.example.com
+
+
+
high
+
Interesting subdomain: dev.example.com
+
Subdomain 'dev.example.com' suggests a potentially sensitive service
+
dev.example.com
+
+
+
medium
+
Open port: example.com/80 (HTTP)
+
Port 80 open on example.com — HTTP
+
example.com:80
+
+
+
medium
+
Open port: example.com/443 (HTTPS)
+
Port 443 open on example.com — HTTPS
+
example.com:443
+
+
+
medium
+
Open port: www.example.com/80 (HTTP)
+
Port 80 open on www.example.com — HTTP
+
www.example.com:80
+
+
+
medium
+
Open port: www.example.com/443 (HTTPS)
+
Port 443 open on www.example.com — HTTPS
+
www.example.com:443
+
+
+
critical
+
Public S3 bucket: dev-static
+
Bucket dev-static is publicly accessible
+
https://dev-static.s3.amazonaws.com
+
+
+
info
+
OOB callback server ready
+
Listening on port 9999 — use for blind SSRF/SSTI/XSS
+
http://localhost:9999/callback
+
+
+
critical
+
Secrets → Cloud Compromise
+
Exposed secrets (AWS keys, tokens) allow cloud account compromise
+
Risk Score: 10/10
+
+
+
+

Capabilities Acquired

+
+ has_subdomain + has_recon + has_s3 + has_secret + has_xss + has_token +
+
+
+

Endpoints Discovered

+
TCP example.com:80 [0]
+
TCP example.com:443 [0]
+
TCP www.example.com:80 [0]
+
TCP www.example.com:443 [0]
+
+
+

Pages Crawled (1)

+
https://example.com (298 bytes, 0 forms, 0 links, 0 JS)
+
+ +
+ + \ No newline at end of file diff --git a/cmd/nice_scan/main.go b/cmd/nice_scan/main.go index 0bb29f6..0c62604 100644 --- a/cmd/nice_scan/main.go +++ b/cmd/nice_scan/main.go @@ -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 } @@ -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 diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000..324abbf --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,4 @@ +# GitHub Pages — no Jekyll processing +include: + - css + - js diff --git a/docs/css/style.css b/docs/css/style.css new file mode 100644 index 0000000..5f471da --- /dev/null +++ b/docs/css/style.css @@ -0,0 +1,878 @@ +/* ==================================================== + NICE_SCAN — Landing Page Styles + Graphite / Slate palette, terminal aesthetic + ==================================================== */ + +:root { + --bg-primary: #0f1117; + --bg-secondary: #161822; + --bg-card: #1a1d2e; + --bg-card-hover: #1f2340; + --bg-terminal: #0a0c12; + --border: #232740; + --border-light: #2e3355; + --text-primary: #e2e4f0; + --text-secondary: #9498b8; + --text-dim: #5c6080; + --accent-cyan: #58d6e6; + --accent-purple: #b484e6; + --accent-green: #5ce6a0; + --accent-orange: #e6a058; + --accent-red: #e6586a; + --accent-yellow: #e6d058; + --gradient-hero: radial-gradient(ellipse at 50% 0%, rgba(88,214,230,0.06) 0%, transparent 60%), + radial-gradient(ellipse at 80% 20%, rgba(180,132,230,0.04) 0%, transparent 50%); + --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; + --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace; + --radius: 12px; + --radius-sm: 8px; + --radius-lg: 16px; +} + +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +html { scroll-behavior: smooth; } + +body { + font-family: var(--font-sans); + background: var(--bg-primary); + color: var(--text-primary); + line-height: 1.6; + -webkit-font-smoothing: antialiased; + overflow-x: hidden; +} + +::selection { background: var(--accent-cyan); color: var(--bg-primary); } + +a { color: var(--accent-cyan); text-decoration: none; transition: color 0.2s; } +a:hover { color: #7ae2f0; } + +img { max-width: 100%; } +code { font-family: var(--font-mono); font-size: 0.875em; } +pre { font-family: var(--font-mono); } + +/* ─── Navbar ─── */ +.navbar { + position: fixed; + top: 0; left: 0; right: 0; + z-index: 100; + background: rgba(15,17,23,0.8); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border-bottom: 1px solid var(--border); +} + +.nav-inner { + max-width: 1200px; + margin: 0 auto; + padding: 0 24px; + height: 64px; + display: flex; + align-items: center; + justify-content: space-between; +} + +.nav-logo { + display: flex; + align-items: center; + gap: 2px; + font-family: var(--font-mono); + font-weight: 600; + font-size: 1.1rem; + color: var(--text-primary); +} +.logo-bracket { color: var(--accent-cyan); } +.logo-name { color: var(--text-primary); letter-spacing: -0.5px; } + +.nav-links { + display: flex; + align-items: center; + gap: 28px; +} +.nav-links a { + color: var(--text-secondary); + font-size: 0.9rem; + font-weight: 500; + transition: color 0.2s; +} +.nav-links a:hover { color: var(--text-primary); } + +.nav-gh { + display: flex; + align-items: center; + gap: 6px; + padding: 6px 14px; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + font-size: 0.85rem; +} +.nav-gh:hover { border-color: var(--accent-cyan); } + +.nav-toggle { + display: none; + flex-direction: column; + gap: 5px; + background: none; + border: none; + cursor: pointer; + padding: 4px; +} +.nav-toggle span { + display: block; + width: 24px; + height: 2px; + background: var(--text-secondary); + border-radius: 1px; + transition: all 0.3s; +} + +/* ─── Hero ─── */ +.hero { + position: relative; + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: 120px 24px 80px; + overflow: hidden; +} + +.hero-bg { + position: absolute; + inset: 0; + background: var(--gradient-hero); + pointer-events: none; +} + +.hero-grid { + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(88,214,230,0.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(88,214,230,0.03) 1px, transparent 1px); + background-size: 60px 60px; + mask-image: radial-gradient(ellipse at 50% 30%, black 30%, transparent 70%); + -webkit-mask-image: radial-gradient(ellipse at 50% 30%, black 30%, transparent 70%); +} + +.hero-glow { + position: absolute; + top: 20%; + left: 50%; + transform: translate(-50%, -50%); + width: 600px; + height: 600px; + background: radial-gradient(circle, rgba(88,214,230,0.08) 0%, transparent 70%); + pointer-events: none; +} + +.hero-content { + position: relative; + max-width: 900px; + width: 100%; + text-align: center; +} + +.hero-badge { + display: inline-block; + padding: 6px 16px; + border: 1px solid var(--border); + border-radius: 100px; + font-size: 0.8rem; + color: var(--text-secondary); + font-family: var(--font-mono); + margin-bottom: 28px; + background: rgba(26,29,46,0.5); +} + +.hero-title { + font-size: clamp(2.8rem, 7vw, 5rem); + font-weight: 800; + line-height: 1.1; + letter-spacing: -2px; + margin-bottom: 20px; +} +.hl-cyan { color: var(--accent-cyan); } +.hl-purple { color: var(--accent-purple); } +.hl-green { color: var(--accent-green); } + +.hero-sub { + font-size: 1.15rem; + color: var(--text-secondary); + max-width: 700px; + margin: 0 auto 36px; + line-height: 1.7; +} +.hero-sub strong { color: var(--text-primary); } + +.hero-actions { + display: flex; + gap: 12px; + justify-content: center; + flex-wrap: wrap; + margin-bottom: 60px; +} + +.btn { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 12px 24px; + border-radius: var(--radius-sm); + font-family: var(--font-sans); + font-size: 0.95rem; + font-weight: 600; + cursor: pointer; + transition: all 0.25s; + border: 1px solid transparent; +} +.btn-primary { + background: var(--accent-cyan); + color: var(--bg-primary); +} +.btn-primary:hover { + background: #7ae2f0; + transform: translateY(-1px); + box-shadow: 0 8px 24px rgba(88,214,230,0.2); +} +.btn-secondary { + background: transparent; + color: var(--text-primary); + border-color: var(--border); +} +.btn-secondary:hover { + border-color: var(--accent-purple); + background: rgba(180,132,230,0.08); +} +.btn-ghost { + background: transparent; + color: var(--text-secondary); +} +.btn-ghost:hover { color: var(--text-primary); } + +/* ─── Terminal ─── */ +.hero-terminal { + max-width: 700px; + margin: 0 auto; + border-radius: var(--radius); + overflow: hidden; + border: 1px solid var(--border); + background: var(--bg-terminal); + box-shadow: 0 24px 48px rgba(0,0,0,0.4); + text-align: left; +} + +.term-bar { + display: flex; + align-items: center; + gap: 8px; + padding: 12px 16px; + background: var(--bg-card); + border-bottom: 1px solid var(--border); +} +.term-dot { + width: 10px; + height: 10px; + border-radius: 50%; +} +.term-dot.red { background: #e6586a; } +.term-dot.yellow { background: #e6d058; } +.term-dot.green { background: #5ce6a0; } +.term-title { + flex: 1; + text-align: center; + font-family: var(--font-mono); + font-size: 0.75rem; + color: var(--text-dim); +} + +.term-body { + padding: 16px 20px; + font-family: var(--font-mono); + font-size: 0.82rem; + line-height: 1.8; + min-height: 260px; +} + +.term-line { opacity: 0; animation: fadeIn 0.4s ease forwards; } +.term-line:nth-child(1) { animation-delay: 0.5s; } +.term-line:nth-child(2) { animation-delay: 1.5s; } +.term-line:nth-child(3) { animation-delay: 2.5s; } +.term-line:nth-child(4) { animation-delay: 3.5s; } +.term-line:nth-child(5) { animation-delay: 5s; } +.term-line:nth-child(6) { animation-delay: 6s; } +.term-line:nth-child(7) { animation-delay: 7.5s; } + +.term-spawn { padding-left: 20px; } + +.term-cursor { + display: inline-block; + width: 8px; + height: 16px; + background: var(--accent-cyan); + animation: blink 1s step-end infinite; + vertical-align: text-bottom; + margin-left: 2px; +} + +.t-cyan { color: var(--accent-cyan); } +.t-green { color: var(--accent-green); } +.t-purple { color: var(--accent-purple); } +.t-red { color: var(--accent-red); } +.t-yellow { color: var(--accent-yellow); } +.t-orange { color: var(--accent-orange); } +.t-gray { color: var(--text-dim); } +.t-dim { color: var(--text-dim); font-size: 0.78rem; } +.t-bold { font-weight: 700; color: var(--accent-cyan); } + +/* ─── Sections ─── */ +.section { + padding: 100px 24px; + max-width: 1200px; + margin: 0 auto; +} +.section-alt { background: var(--bg-secondary); } +.section-alt { max-width: none; padding-left: 0; padding-right: 0; } +.section-alt > * { max-width: 1200px; margin-left: auto; margin-right: auto; padding-left: 24px; padding-right: 24px; } +.section-alt .section-header { padding-left: 0; padding-right: 0; } + +.section-header { + text-align: center; + max-width: 680px; + margin: 0 auto 60px; +} +.section-tag { + display: inline-block; + padding: 4px 12px; + border: 1px solid var(--border); + border-radius: 100px; + font-size: 0.75rem; + font-family: var(--font-mono); + color: var(--accent-cyan); + text-transform: uppercase; + letter-spacing: 1.5px; + margin-bottom: 16px; +} +.section-header h2 { + font-size: clamp(2rem, 4vw, 2.8rem); + font-weight: 700; + letter-spacing: -1px; + margin-bottom: 16px; +} +.section-header p { + color: var(--text-secondary); + font-size: 1.05rem; + line-height: 1.7; +} + +/* ─── Features Grid ─── */ +.features-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); + gap: 20px; +} + +.feature-card { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 32px; + transition: all 0.3s; +} +.feature-card:hover { + border-color: var(--border-light); + background: var(--bg-card-hover); + transform: translateY(-2px); +} + +.fc-icon { + width: 48px; + height: 48px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 20px; +} +.fc-brain { background: rgba(88,214,230,0.1); color: var(--accent-cyan); } +.fc-chain { background: rgba(180,132,230,0.1); color: var(--accent-purple); } +.fc-data { background: rgba(92,230,160,0.1); color: var(--accent-green); } +.fc-report { background: rgba(230,160,88,0.1); color: var(--accent-orange); } +.fc-shield { background: rgba(230,88,106,0.1); color: var(--accent-red); } +.fc-cmd { background: rgba(230,208,88,0.1); color: var(--accent-yellow); } + +.feature-card h3 { + font-size: 1.15rem; + font-weight: 600; + margin-bottom: 10px; +} +.feature-card p { + color: var(--text-secondary); + font-size: 0.9rem; + line-height: 1.7; +} +.feature-card code { + color: var(--accent-cyan); + font-size: 0.85em; + background: rgba(88,214,230,0.06); + padding: 1px 6px; + border-radius: 4px; +} + +/* ─── Stats ─── */ +.section-stats { + padding: 80px 24px; +} +.stats-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 24px; + text-align: center; +} +.stat-item { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; +} +.stat-num { + font-family: var(--font-mono); + font-size: 3rem; + font-weight: 700; + color: var(--accent-cyan); + line-height: 1; +} +.stat-label { + font-size: 0.9rem; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 1px; +} + +/* ─── Install ─── */ +.install-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 16px; + margin-bottom: 28px; +} + +.install-card { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 24px; + text-align: center; + cursor: pointer; + transition: all 0.3s; +} +.install-card:hover { border-color: var(--border-light); } +.install-card.active { + border-color: var(--accent-cyan); + background: rgba(88,214,230,0.04); +} +.ic-icon { font-size: 2rem; margin-bottom: 12px; } +.install-card h3 { font-size: 1rem; font-weight: 600; margin-bottom: 4px; } +.ic-desc { font-size: 0.8rem; color: var(--text-dim); } + +.install-code { + background: var(--bg-terminal); + border: 1px solid var(--border); + border-radius: var(--radius); + overflow: hidden; +} + +.code-tab-bar { + display: flex; + border-bottom: 1px solid var(--border); + overflow-x: auto; +} +.code-tab { + padding: 12px 20px; + font-family: var(--font-mono); + font-size: 0.82rem; + color: var(--text-dim); + background: none; + border: none; + cursor: pointer; + transition: all 0.2s; + border-bottom: 2px solid transparent; +} +.code-tab:hover { color: var(--text-secondary); } +.code-tab.active { + color: var(--accent-cyan); + border-bottom-color: var(--accent-cyan); +} + +.code-block { + padding: 0; +} +.code-block.hidden { display: none; } + +.code-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 20px; + border-bottom: 1px solid var(--border); + font-family: var(--font-mono); + font-size: 0.78rem; + color: var(--text-dim); +} + +.code-block pre { + padding: 20px; + overflow-x: auto; + font-size: 0.85rem; + line-height: 1.7; + color: var(--text-primary); +} +.c-comment { color: var(--text-dim); font-style: italic; } + +.copy-btn { + padding: 4px 12px; + font-family: var(--font-mono); + font-size: 0.75rem; + background: rgba(88,214,230,0.1); + color: var(--accent-cyan); + border: 1px solid rgba(88,214,230,0.2); + border-radius: 4px; + cursor: pointer; + transition: all 0.2s; +} +.copy-btn:hover { + background: rgba(88,214,230,0.2); +} + +/* ─── Usage ─── */ +.usage-steps { + display: flex; + flex-direction: column; + gap: 24px; + max-width: 800px; + margin: 0 auto; +} + +.usage-step { + display: flex; + gap: 24px; + align-items: flex-start; +} + +.us-num { + font-family: var(--font-mono); + font-size: 1.2rem; + font-weight: 700; + color: var(--accent-cyan); + min-width: 48px; + padding-top: 2px; +} + +.us-content h3 { + font-size: 1.1rem; + font-weight: 600; + margin-bottom: 6px; +} +.us-content p { + color: var(--text-secondary); + font-size: 0.9rem; + line-height: 1.6; + margin-bottom: 12px; +} + +.us-code { + display: flex; + align-items: center; + gap: 12px; + background: var(--bg-terminal); + border: 1px solid var(--border); + border-radius: var(--radius-sm); + padding: 10px 16px; +} +.us-code pre { + flex: 1; + font-size: 0.85rem; + color: var(--accent-cyan); + overflow-x: auto; +} +.us-code pre code { color: inherit; } + +/* ─── Report Preview ─── */ +.section-report-preview { + padding-bottom: 120px; +} + +.report-preview { + max-width: 700px; + margin: 0 auto; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + overflow: hidden; + box-shadow: 0 16px 32px rgba(0,0,0,0.3); +} + +.rp-header { + padding: 20px 24px; + border-bottom: 1px solid var(--border); +} + +.rp-title-bar { + display: flex; + align-items: center; + gap: 10px; + font-weight: 600; + margin-bottom: 8px; +} +.rp-icon { color: var(--accent-cyan); } +.rp-badge { + margin-left: auto; + padding: 2px 10px; + border-radius: 4px; + font-family: var(--font-mono); + font-size: 0.8rem; + background: rgba(230,88,106,0.1); + color: var(--accent-red); + border: 1px solid rgba(230,88,106,0.2); +} + +.rp-meta { + display: flex; + gap: 8px; + font-size: 0.78rem; + color: var(--text-dim); + font-family: var(--font-mono); +} + +.rp-body { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0; +} + +.rp-col { + padding: 20px 24px; +} +.rp-col:first-child { border-right: 1px solid var(--border); } + +.rp-section-title { + font-size: 0.72rem; + text-transform: uppercase; + letter-spacing: 1.5px; + color: var(--text-dim); + margin-bottom: 16px; +} + +.rp-finding { + display: flex; + gap: 10px; + padding: 10px 0; + border-bottom: 1px solid var(--border); +} +.rp-finding:last-child { border-bottom: none; } + +.rp-sev { + font-family: var(--font-mono); + font-size: 0.8rem; + min-width: 20px; +} +.rp-finding.critical .rp-sev { color: var(--accent-red); } +.rp-finding.high .rp-sev { color: var(--accent-orange); } +.rp-finding.medium .rp-sev { color: var(--accent-yellow); } + +.rp-fname { font-size: 0.85rem; font-weight: 500; } +.rp-fdesc { font-size: 0.78rem; color: var(--text-dim); } + +.rp-datum { + display: flex; + justify-content: space-between; + padding: 8px 0; + border-bottom: 1px solid var(--border); + font-size: 0.85rem; +} +.rp-datum:last-child { border-bottom: none; } +.rp-dlabel { color: var(--text-secondary); } +.rp-dval { font-family: var(--font-mono); color: var(--accent-green); } + +/* ─── Trust ─── */ +.trust-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 20px; + margin-bottom: 48px; +} + +.trust-card { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 28px; +} +.tc-icon { font-size: 2rem; margin-bottom: 16px; } +.trust-card h3 { + font-size: 1rem; + font-weight: 600; + margin-bottom: 8px; +} +.trust-card p { + color: var(--text-secondary); + font-size: 0.85rem; + line-height: 1.6; + margin-bottom: 12px; +} +.trust-card pre { + background: var(--bg-terminal); + padding: 10px 14px; + border-radius: var(--radius-sm); + font-size: 0.75rem; + color: var(--accent-cyan); + overflow-x: auto; + border: 1px solid var(--border); +} + +.trust-cta { + text-align: center; + padding: 40px 0; +} +.trust-cta p { + color: var(--text-secondary); + margin-bottom: 20px; + font-size: 1.05rem; +} + +/* ─── Footer ─── */ +.footer { + border-top: 1px solid var(--border); + background: var(--bg-secondary); +} + +.footer-inner { + max-width: 1200px; + margin: 0 auto; + padding: 60px 24px; + display: grid; + grid-template-columns: 2fr 3fr; + gap: 60px; +} + +.footer-logo { + display: flex; + align-items: center; + gap: 2px; + font-family: var(--font-mono); + font-weight: 600; + font-size: 1.2rem; + margin-bottom: 12px; +} + +.footer-desc { + color: var(--text-secondary); + font-size: 0.9rem; + max-width: 300px; +} + +.footer-links { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 32px; +} + +.footer-col h4 { + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 1px; + color: var(--text-dim); + margin-bottom: 16px; +} +.footer-col a, .footer-col span { + display: block; + color: var(--text-secondary); + font-size: 0.9rem; + margin-bottom: 10px; + transition: color 0.2s; +} +.footer-col a:hover { color: var(--text-primary); } + +.footer-bottom { + border-top: 1px solid var(--border); + padding: 20px 24px; + display: flex; + justify-content: space-between; + align-items: center; + max-width: 1200px; + margin: 0 auto; + font-size: 0.78rem; + color: var(--text-dim); +} + +/* ─── Toast ─── */ +.toast { + position: fixed; + bottom: 24px; + right: 24px; + padding: 12px 20px; + background: var(--accent-green); + color: var(--bg-primary); + font-family: var(--font-mono); + font-size: 0.82rem; + border-radius: var(--radius-sm); + font-weight: 500; + opacity: 0; + transform: translateY(10px); + transition: all 0.3s; + pointer-events: none; + z-index: 200; +} +.toast.show { + opacity: 1; + transform: translateY(0); +} + +/* ─── Animations ─── */ +@keyframes fadeIn { + to { opacity: 1; } +} +@keyframes blink { + 50% { opacity: 0; } +} + +/* ─── Responsive ─── */ +@media (max-width: 768px) { + .nav-links { display: none; } + .nav-links.open { + display: flex; + flex-direction: column; + position: absolute; + top: 64px; + left: 0; right: 0; + background: var(--bg-primary); + border-bottom: 1px solid var(--border); + padding: 20px 24px; + gap: 16px; + } + .nav-toggle { display: flex; } + + .hero-title { font-size: 2.4rem; } + .hero-actions { flex-direction: column; align-items: stretch; } + .hero-actions .btn { justify-content: center; } + + .features-grid { grid-template-columns: 1fr; } + .stats-grid { grid-template-columns: repeat(2, 1fr); gap: 32px; } + .install-grid { grid-template-columns: repeat(2, 1fr); } + + .rp-body { grid-template-columns: 1fr; } + .rp-col:first-child { border-right: none; border-bottom: 1px solid var(--border); } + + .footer-inner { grid-template-columns: 1fr; gap: 32px; } + .footer-links { grid-template-columns: repeat(2, 1fr); } + .footer-bottom { flex-direction: column; gap: 8px; text-align: center; } + + .usage-step { flex-direction: column; gap: 12px; } + .us-num { min-width: auto; } +} + +@media (max-width: 480px) { + .install-grid { grid-template-columns: 1fr; } + .stats-grid { grid-template-columns: repeat(2, 1fr); } + .stat-num { font-size: 2.2rem; } +} diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..b4629e9 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,451 @@ + + + + + + + + NICE_SCAN — Security Reconnaissance Engine + + + + + + + + + + +
+
+
+
+
+
+
v0.1.0 — Public Release
+

+ Fast. + Precise. + Intelligent. +

+

Autonomous security reconnaissance engine with decision chaining — SQLi → users, LFI → files, CMD → shells, S3 → buckets, Upload → RCE

+ +
+
+ + + + terminal — nice_scan hack example.com -R report.html +
+
+
[0s] ▸ Passive Reconnaissance OK (3 endpoints)
+
[2s] ▸ Crawl Endpoints OK (47 paths)
+
[4s] ◈ SQL Injection !! CRITICAL — login.php
+
[5s] └ SQLi Data Extract OK (142 users dumped)
+
[7s] ◈ LFI !! CRITICAL — /etc/passwd
+
[8s] └ LFI File Read OK (12 files extracted)
+
[10s] ▸ Risk Score 10.0 / 10 — report.html saved
+
+
+
+
+
+ +
+
+ +

Why NICE_SCAN?

+

Not just another scanner — an autonomous security agent that chains attacks, extracts real data, and generates professional reports.

+
+
+
+
+ +
+

Decision Engine

+

Forward-chaining AI scans for vulnerabilities and automatically spawns exploit modules — SQLi dumps user tables, LFI reads server files, CMD injection opens shells.

+
+
+
+ +
+

Attack Chaining

+

10 chain patterns — CORS+XSS, JWT→Admin, Secrets→Cloud, Upload→RCE. Each finding automatically triggers deeper exploitation across interconnected vectors.

+
+
+
+ +
+

Real Data Extraction

+

Goes beyond detection — extracts SQL databases, LFI files, S3 bucket contents, deploys web shells, and dumps everything to reports/ organized by target.

+
+
+
+ +
+

HTML Attack Reports

+

Professional dark-theme reports with risk scoring, findings timeline, capabilities graph, extracted data summary, and credential inventory. Shareable with -R report.html.

+
+
+
+ +
+

Trust Verification

+

All releases signed with GPG + Sigstore/cosign. SHA256 checksums verified. Supply-chain security with SLSA provenance and SPDX SBOM included every release.

+
+
+
+ +
+

14 Attack Modules

+

Passive recon, crawl, fuzz, JWT forge, login brute, XSS, SQLi, GraphQL, S3 enum, LFI, CMD injection, upload, OOB server, port scan — all with auto-spawning chains.

+
+
+
+ +
+
+
+ 0 + Exploit Modules +
+
+ 0 + Chain Patterns +
+
+ 0 + Attack Actions +
+
+ 0 + Platforms +
+
+
+ +
+
+ +

Get NICE_SCAN

+

Four ways to install, all with cryptographic verification.

+
+
+
+
🍺
+

Scoop

+

Windows package manager

+
+
+
📦
+

Winget

+

Windows native package manager

+
+
+
⚡
+

Go Install

+

Cross-platform source build

+
+
+
📥
+

Manual

+

Download from GitHub Releases

+
+
+
+
+ + + + +
+
+
+ powershell + +
+
scoop bucket add nice-scan https://github.com/NICE-DEV226/nice-Scan
+scoop install nice-scan/nice_scan
+
+ + + +
+
+ +
+
+ +

From Zero to Exploit

+

One command. Autonomous attack chaining. Real data extraction.

+
+
+
+
01
+
+

Quick Scan

+

Full autonomous attack against any target — detects vulnerabilities and automatically exploits them.

+
+
nice_scan hack example.com -R report.html
+ +
+
+
+
+
02
+
+

Interactive Shell

+

Persistent REPL reconnaissance shell with command history, session context, and live results.

+
+
nice_scan shell
+ +
+
+
+
+
03
+
+

Live Dashboard

+

Real-time TUI dashboard with progress bars, findings stream, and live severity updates.

+
+
nice_scan scan example.com -i
+ +
+
+
+
+
04
+
+

Time-Boxed Attack

+

Set a timeout to control engagement duration — ideal for bug bounties and CTFs.

+
+
nice_scan hack target.com --timeout 30s -R report.html
+ +
+
+
+
+
+ +
+
+ +

Professional HTML Reports

+

Dark-theme, risk-scored, evidence-backed — ready for client delivery or team collaboration.

+
+
+
+
+ ◈ + Attack Report — example.com + 10.0 / 10 +
+
+ v0.1.0 + • + 14 actions + • + 8 chained steps + • + 25s elapsed +
+
+
+
+
Findings
+
+ !! +
+
SQL Injection
+
login.php — 142 users extracted
+
+
+
+ !! +
+
Local File Inclusion
+
/etc/passwd, /etc/shadow, .env, config.php (12 files)
+
+
+
+ ▸ +
+
CORS Misconfiguration
+
api.example.com — allows all origins
+
+
+
+ ◈ +
+
JWT Weak Secret
+
alg=none accepted, secret brute-forced
+
+
+
+
+
Extracted Data
+
+ Users + 142 records +
+
+ Files + 12 LFI + 3 config +
+
+ Credentials + 8 found +
+
+ Endpoints + 47 discovered +
+
+ Shells + 1 deployed +
+
+
+
+
+ +
+
+ +

Supply Chain Security

+

Every release is cryptographically signed. Every install script verifies before extraction.

+
+
+
+
🔑
+

GPG Signatures

+

Checksum files signed with NICE-DEV226's GPG key. Verify with:

+
gpg --verify checksums.txt.sig checksums.txt
+
+
+
🛡️
+

Sigstore / cosign

+

Keyless signing via GitHub OIDC. SLSA provenance with cosign bundles.

+
cosign verify-blob --bundle checksums.sigstore.json checksums.txt
+
+
+
📋
+

SPDX SBOM

+

Software Bill of Materials for every release — full dependency transparency.

+
syft scan nice_scan --from-release NICE-DEV226/nice-Scan:v0.1.0
+
+
+
✅
+

SLSA Level 2

+

Build integrity verified through Sigstore. Non-falsifiable provenance attestations.

+
gh attestation verify nice_scan_0.1.0_linux_amd64.tar.gz \
+  --repo NICE-DEV226/nice-Scan
+
+
+
+

Download the latest release and verify for yourself.

+ + + Latest Release + +
+
+ + + +
+ + + + \ No newline at end of file diff --git a/docs/js/main.js b/docs/js/main.js new file mode 100644 index 0000000..06a6a5b --- /dev/null +++ b/docs/js/main.js @@ -0,0 +1,175 @@ +/* ==================================================== + NICE_SCAN — Landing Page Interactions + ==================================================== */ + +document.addEventListener('DOMContentLoaded', () => { + + /* ─── Mobile Nav Toggle ─── */ + const navToggle = document.querySelector('.nav-toggle'); + const navLinks = document.querySelector('.nav-links'); + if (navToggle) { + navToggle.addEventListener('click', () => { + navLinks.classList.toggle('open'); + }); + } + + /* ─── Close mobile nav on link click ─── */ + document.querySelectorAll('.nav-links a').forEach(link => { + link.addEventListener('click', () => navLinks.classList.remove('open')); + }); + + /* ─── Install Method Tabs ─── */ + const installCards = document.querySelectorAll('.install-card'); + const codeTabs = document.querySelectorAll('.code-tab'); + const codeBlocks = { + scoop: document.getElementById('code-scoop'), + winget: document.getElementById('code-winget'), + go: document.getElementById('code-go'), + manual: document.getElementById('code-manual'), + }; + + function activateInstallMethod(method) { + // Update cards + installCards.forEach(c => c.classList.remove('active')); + document.querySelector(`.install-card[data-method="${method}"]`).classList.add('active'); + // Update code tabs + codeTabs.forEach(t => t.classList.remove('active')); + document.querySelector(`.code-tab[data-tab="${method}"]`).classList.add('active'); + // Show code block + Object.values(codeBlocks).forEach(b => b.classList.add('hidden')); + codeBlocks[method].classList.remove('hidden'); + } + + installCards.forEach(card => { + card.addEventListener('click', () => { + activateInstallMethod(card.dataset.method); + }); + }); + + codeTabs.forEach(tab => { + tab.addEventListener('click', () => { + activateInstallMethod(tab.dataset.tab); + }); + }); + + /* ─── Copy Buttons ─── */ + const toast = document.getElementById('toast'); + + document.querySelectorAll('.copy-btn').forEach(btn => { + btn.addEventListener('click', async () => { + const cmd = btn.dataset.cmd; + if (!cmd) return; + try { + await navigator.clipboard.writeText(cmd); + showToast('Copied to clipboard'); + } catch { + // Fallback + const ta = document.createElement('textarea'); + ta.value = cmd; + ta.style.position = 'fixed'; + ta.style.opacity = '0'; + document.body.appendChild(ta); + ta.select(); + document.execCommand('copy'); + document.body.removeChild(ta); + showToast('Copied to clipboard'); + } + }); + }); + + function showToast(msg) { + toast.textContent = msg; + toast.classList.add('show'); + clearTimeout(toast._timer); + toast._timer = setTimeout(() => toast.classList.remove('show'), 2000); + } + + /* ─── Counter Animation ─── */ + const statNumbers = document.querySelectorAll('.stat-num'); + let countersAnimated = false; + + function animateCounters() { + if (countersAnimated) return; + countersAnimated = true; + + statNumbers.forEach(el => { + const target = parseInt(el.dataset.target) || 0; + const duration = 1500; + const start = performance.now(); + + function update(now) { + const elapsed = now - start; + const progress = Math.min(elapsed / duration, 1); + const eased = 1 - Math.pow(1 - progress, 3); // ease-out cubic + const current = Math.floor(eased * target); + el.textContent = current; + if (progress < 1) requestAnimationFrame(update); + else el.textContent = target; + } + requestAnimationFrame(update); + }); + } + + /* ─── Intersection Observer for counters ─── */ + const statsSection = document.querySelector('.section-stats'); + if (statsSection && 'IntersectionObserver' in window) { + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + animateCounters(); + observer.disconnect(); + } + }); + }, { threshold: 0.3 }); + observer.observe(statsSection); + } else { + // Fallback: animate on load + animateCounters(); + } + + /* ─── Smooth scroll for anchor links ─── */ + document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', (e) => { + const target = document.querySelector(anchor.getAttribute('href')); + if (target) { + e.preventDefault(); + target.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }); + }); + + /* ─── Feature cards reveal on scroll ─── */ + const featureCards = document.querySelectorAll('.feature-card'); + if (featureCards.length && 'IntersectionObserver' in window) { + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.style.opacity = '1'; + entry.target.style.transform = 'translateY(0)'; + observer.unobserve(entry.target); + } + }); + }, { threshold: 0.1 }); + + featureCards.forEach(card => { + card.style.opacity = '0'; + card.style.transform = 'translateY(20px)'; + card.style.transition = 'opacity 0.5s ease, transform 0.5s ease'; + observer.observe(card); + }); + } + + /* ─── Navbar scroll effect ─── */ + const navbar = document.querySelector('.navbar'); + let lastScroll = 0; + + window.addEventListener('scroll', () => { + const currentScroll = window.pageYOffset; + if (currentScroll > 80) { + navbar.style.background = 'rgba(15,17,23,0.95)'; + } else { + navbar.style.background = 'rgba(15,17,23,0.8)'; + } + lastScroll = currentScroll; + }); +}); diff --git a/internal/engine/auth.go b/internal/engine/auth.go index 18ddd42..b1e75bc 100644 --- a/internal/engine/auth.go +++ b/internal/engine/auth.go @@ -15,8 +15,6 @@ var ( rxUsernameField = regexp.MustCompile(`(?i)]*(?:name|id)["']?\s*=\s*["']?(?:user|email|login|username|log)["'][^>]*>`) rxLoginAction = regexp.MustCompile(`(?i)]*action=["']([^"']+)["']`) - rxLoginEndpoint = regexp.MustCompile(`(?i)(login|signin|auth|authenticate|logon|account/login)(\.php|\.aspx|\.jsp)?/?$`) - rxAdminPanel = regexp.MustCompile(`(?i)(admin|dashboard|panel|backend|cpanel|administrator|wp-admin|manager)`) rxRegisterForm = regexp.MustCompile(`(?i)]*>.*?(?:register|signup|create-account).*?`) @@ -24,7 +22,6 @@ var ( rxForgotPassword = regexp.MustCompile(`(?i)(forgot|reset|recover|lost)\s*(password|pwd)`) rxUserEnumError = regexp.MustCompile(`(?i)(user|account|email|username).{0,20}(not found|doesn't exist|invalid|does not exist|incorrect|unknown)`) - rxUserEnumPass = regexp.MustCompile(`(?i)(user|account|email|username).{0,20}(found|exists|valid|correct)`) rxRoleIndicator = regexp.MustCompile(`(?i)(role|admin|moderator|editor|manager|superuser|privilege|permission|access_level)`) ) diff --git a/internal/engine/cors.go b/internal/engine/cors.go index 221e808..4c39d13 100644 --- a/internal/engine/cors.go +++ b/internal/engine/cors.go @@ -42,7 +42,7 @@ func (a *CORSAnalyzer) Analyze(ctx context.Context, resp *types.Response) []type Name: "Wildcard CORS with Credentials", Severity: types.SeverityCritical, Description: "CORS allows any origin (Access-Control-Allow-Origin: *) with credentials enabled — any website can read this resource on behalf of authenticated users", - Evidence: fmt.Sprintf("ACAO: * | ACAC: true"), + Evidence: "ACAO: * | ACAC: true", Confidence: 0.95, Metadata: map[string]string{ "aco": aco, diff --git a/internal/engine/extract.go b/internal/engine/extract.go index 18d04c3..57f369a 100644 --- a/internal/engine/extract.go +++ b/internal/engine/extract.go @@ -14,8 +14,6 @@ var ( rxPhone = regexp.MustCompile(`(?:\+\d{1,3}[-.\s])?\(?\d{3}\)?[-.\s]\d{3}[-.\s]?\d{4}`) - rxIP = regexp.MustCompile(`\b(?:\d{1,3}\.){3}\d{1,3}\b`) - rxSSN = regexp.MustCompile(`\b\d{3}-\d{2}-\d{4}\b`) rxCreditCard = regexp.MustCompile(`\b(?:\d{4}[-\s]?){3}\d{4}\b`) diff --git a/internal/engine/privesc.go b/internal/engine/privesc.go index 1dd16a9..969aef2 100644 --- a/internal/engine/privesc.go +++ b/internal/engine/privesc.go @@ -11,7 +11,6 @@ import ( var ( rxIDOR = regexp.MustCompile(`(?i)(id|user_id|account_id|uid|pid|order_id|profile_id|item_id)=(\d+)`) - rxIDORUUID = regexp.MustCompile(`(?i)(id|user_id|uid|token)=([a-f0-9\-]{32,})`) rxRoleMod = regexp.MustCompile(`(?i)(role|user_type|account_type|access|level|priv|permission)=(\w+)`) rxStatusMod = regexp.MustCompile(`(?i)(status|state|active|enabled|verified)=(0|1|true|false|yes|no)`) diff --git a/internal/engine/sqli.go b/internal/engine/sqli.go index 6c3cbe6..58df838 100644 --- a/internal/engine/sqli.go +++ b/internal/engine/sqli.go @@ -119,12 +119,3 @@ func (a *SQLiAnalyzer) Analyze(ctx context.Context, resp *types.Response) []type return findings } - -func hasAnyError(body string) bool { - for _, re := range sqliErrorIndicators { - if re.MatchString(body) { - return true - } - } - return false -} diff --git a/internal/engine/tokens.go b/internal/engine/tokens.go index 4325457..edd5ebf 100644 --- a/internal/engine/tokens.go +++ b/internal/engine/tokens.go @@ -18,7 +18,6 @@ var ( rxAPIToken = regexp.MustCompile(`(?i)api[_\-]key|api[_\-]token|app[_\-]token["']?\s*[:=]\s*["']([A-Za-z0-9\-_.@]+)["']`) rxSecret = regexp.MustCompile(`(?i)secret["']?\s*[:=]\s*["']([A-Za-z0-9\-_.@!]+)["']`) rxPassword = regexp.MustCompile(`(?i)password["']?\s*[:=]\s*["']([^"']{6,})["']`) - rxSession = regexp.MustCompile(`(?i)session["']?\s*[:=]\s*["']([A-Za-z0-9\-_.%]+)["']`) rxAuth = regexp.MustCompile(`(?i)authorization["']?\s*[:=]\s*["']([^"']+)["']`) sessionCookies = []string{ @@ -183,8 +182,8 @@ func (t *TokenExtractor) analyzeJWT(raw string) types.Finding { var cls jwtClaims hdrStr := decodeBase64URL(parts[0]) clsStr := decodeBase64URL(parts[1]) - json.Unmarshal([]byte(hdrStr), &hdr) - json.Unmarshal([]byte(clsStr), &cls) + _ = json.Unmarshal([]byte(hdrStr), &hdr) + _ = json.Unmarshal([]byte(clsStr), &cls) var details []string details = append(details, fmt.Sprintf("Alg: %s", hdr.Alg)) diff --git a/internal/engine/xss.go b/internal/engine/xss.go index 2a6a2cb..da4aa1f 100644 --- a/internal/engine/xss.go +++ b/internal/engine/xss.go @@ -10,8 +10,6 @@ import ( ) var ( - rxXSSReflected = regexp.MustCompile(`(?i)