Skip to content

Commit 6359e8a

Browse files
lizthegreyclaude
andcommittedFeb 7, 2026
feat: initial implementation of abuse reporting wizard
Go web app automating infrastructure discovery and abuse report lifecycle for endharassment.net. Server-rendered HTML with htmx, SQLite storage, SendGrid email delivery. Core workflow: URL submission → DNS/ASN/RDAP/BGP discovery → Cloudflare detection → evidence upload → X-ARF report generation → admin approval queue → SendGrid delivery → escalation to upstreams. Includes security review with rate limiting, HMAC-signed CSRF, content safety validation, and CSAM/legal risk analysis. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1443d42 commit 6359e8a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+8986
-0
lines changed
 

‎.gitignore‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Binary
2+
/wizard
3+
/reporting-wizard
4+
5+
# Evidence storage
6+
evidence/*
7+
!evidence/.gitkeep
8+
9+
# Database
10+
*.db
11+
*.db-wal
12+
*.db-shm
13+
14+
# Environment
15+
.env
16+
.env.*
17+
18+
# IDE
19+
.idea/
20+
.vscode/
21+
*.swp
22+
*~
23+
24+
# OS
25+
.DS_Store
26+
Thumbs.db

‎SECURITY_REVIEW.md‎

Lines changed: 347 additions & 0 deletions
Large diffs are not rendered by default.

‎cmd/wizard/main.go‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"flag"
6+
"io/fs"
7+
"log"
8+
"net/http"
9+
"os"
10+
"os/signal"
11+
"syscall"
12+
"time"
13+
14+
wizard "github.com/endharassment/reporting-wizard"
15+
"github.com/endharassment/reporting-wizard/internal/server"
16+
"github.com/endharassment/reporting-wizard/internal/store"
17+
)
18+
19+
func main() {
20+
listenAddr := flag.String("listen", envOr("WIZARD_LISTEN", ":8080"), "HTTP listen address")
21+
dbPath := flag.String("db", envOr("WIZARD_DB_PATH", "./wizard.db"), "SQLite database path")
22+
evidenceDir := flag.String("evidence-dir", envOr("WIZARD_EVIDENCE_DIR", "./evidence"), "Evidence storage directory")
23+
flag.Parse()
24+
25+
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
26+
defer cancel()
27+
28+
db, err := store.NewSQLiteStore(ctx, *dbPath)
29+
if err != nil {
30+
log.Fatalf("Failed to open database: %v", err)
31+
}
32+
defer db.Close()
33+
34+
// Ensure evidence directory exists.
35+
if err := os.MkdirAll(*evidenceDir, 0o750); err != nil {
36+
log.Fatalf("Failed to create evidence directory: %v", err)
37+
}
38+
39+
tmplFS, err := fs.Sub(wizard.TemplatesFS, "templates")
40+
if err != nil {
41+
log.Fatalf("Failed to create templates sub-FS: %v", err)
42+
}
43+
stFS, err := fs.Sub(wizard.StaticFS, "static")
44+
if err != nil {
45+
log.Fatalf("Failed to create static sub-FS: %v", err)
46+
}
47+
48+
cfg := server.Config{
49+
ListenAddr: *listenAddr,
50+
DBPath: *dbPath,
51+
EvidenceDir: *evidenceDir,
52+
SendGridKey: os.Getenv("WIZARD_SENDGRID_KEY"),
53+
FromEmail: envOr("WIZARD_FROM_EMAIL", "reports@endharassment.net"),
54+
FromName: envOr("WIZARD_FROM_NAME", "End Harassment"),
55+
BaseURL: envOr("WIZARD_BASE_URL", "http://localhost:8080"),
56+
GoogleClientID: os.Getenv("WIZARD_GOOGLE_CLIENT_ID"),
57+
GoogleSecret: os.Getenv("WIZARD_GOOGLE_SECRET"),
58+
GitHubClientID: os.Getenv("WIZARD_GITHUB_CLIENT_ID"),
59+
GitHubSecret: os.Getenv("WIZARD_GITHUB_SECRET"),
60+
EscalationDays: 14,
61+
SessionSecret: envOr("WIZARD_SESSION_SECRET", "change-me-in-production"),
62+
}
63+
64+
srv, err := server.NewServer(cfg, db, tmplFS, stFS)
65+
if err != nil {
66+
log.Fatalf("Failed to create server: %v", err)
67+
}
68+
defer srv.Stop()
69+
70+
log.Println("Escalation engine started (placeholder)")
71+
72+
httpSrv := &http.Server{
73+
Addr: *listenAddr,
74+
Handler: srv.Handler(),
75+
}
76+
77+
go func() {
78+
log.Printf("Listening on %s", *listenAddr)
79+
if err := httpSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
80+
log.Fatalf("HTTP server error: %v", err)
81+
}
82+
}()
83+
84+
<-ctx.Done()
85+
log.Println("Shutting down...")
86+
87+
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second)
88+
defer shutdownCancel()
89+
if err := httpSrv.Shutdown(shutdownCtx); err != nil {
90+
log.Fatalf("Shutdown error: %v", err)
91+
}
92+
}
93+
94+
func envOr(key, fallback string) string {
95+
if v := os.Getenv(key); v != "" {
96+
return v
97+
}
98+
return fallback
99+
}

‎embed.go‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package wizard
2+
3+
import "embed"
4+
5+
// TemplatesFS embeds the HTML templates directory.
6+
//
7+
//go:embed templates
8+
var TemplatesFS embed.FS
9+
10+
// StaticFS embeds the static assets directory.
11+
//
12+
//go:embed static
13+
var StaticFS embed.FS

‎evidence/.gitkeep‎

Whitespace-only changes.

‎go.mod‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module github.com/endharassment/reporting-wizard
2+
3+
go 1.25.6
4+
5+
require (
6+
github.com/ammario/ipisp/v2 v2.0.1
7+
github.com/go-chi/chi/v5 v5.2.5
8+
github.com/google/uuid v1.6.0
9+
github.com/openrdap/rdap v0.9.1
10+
github.com/sendgrid/sendgrid-go v3.16.1+incompatible
11+
golang.org/x/oauth2 v0.34.0
12+
golang.org/x/sync v0.19.0
13+
modernc.org/sqlite v1.44.3
14+
)
15+
16+
require (
17+
github.com/alecthomas/kingpin/v2 v2.3.2 // indirect
18+
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
19+
github.com/dustin/go-humanize v1.0.1 // indirect
20+
github.com/mattn/go-isatty v0.0.20 // indirect
21+
github.com/mitchellh/go-homedir v1.1.0 // indirect
22+
github.com/ncruces/go-strftime v1.0.0 // indirect
23+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
24+
github.com/sendgrid/rest v2.6.9+incompatible // indirect
25+
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
26+
golang.org/x/crypto v0.9.0 // indirect
27+
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
28+
golang.org/x/sys v0.37.0 // indirect
29+
modernc.org/libc v1.67.6 // indirect
30+
modernc.org/mathutil v1.7.1 // indirect
31+
modernc.org/memory v1.11.0 // indirect
32+
)

0 commit comments

Comments
 (0)
Please sign in to comment.