All checks were successful
check / check (push) Successful in 4s
Security hardening implementing three issues: CSRF Protection (#35): - Session-based CSRF tokens with cryptographically random generation - Constant-time token comparison to prevent timing attacks - CSRF middleware applied to /pages, /sources, /source, and /user routes - Hidden csrf_token field added to all 12+ POST forms in templates - Excluded from /webhook (inbound) and /api (stateless) routes SSRF Prevention (#36): - ValidateTargetURL blocks private/reserved IP ranges at target creation - Blocked ranges: 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, ::1, fc00::/7, fe80::/10, plus multicast, reserved, test-net, and CGN ranges - SSRF-safe HTTP transport with custom DialContext for defense-in-depth at delivery time (prevents DNS rebinding attacks) - Only http/https schemes allowed Login Rate Limiting (#37): - Per-IP rate limiter using golang.org/x/time/rate - 5 attempts per minute per IP on POST /pages/login - GET requests (form rendering) pass through unaffected - Automatic cleanup of stale per-IP limiter entries - X-Forwarded-For and X-Real-IP header support for reverse proxies Closes #35, closes #36, closes #37
42 lines
1.7 KiB
HTML
42 lines
1.7 KiB
HTML
{{template "base" .}}
|
|
|
|
{{define "title"}}Edit {{.Webhook.Name}} - Webhooker{{end}}
|
|
|
|
{{define "content"}}
|
|
<div class="max-w-2xl mx-auto px-6 py-8">
|
|
<div class="mb-6">
|
|
<a href="/source/{{.Webhook.ID}}" class="text-sm text-primary-600 hover:text-primary-700">← Back to {{.Webhook.Name}}</a>
|
|
<h1 class="text-2xl font-medium text-gray-900 mt-2">Edit Webhook</h1>
|
|
</div>
|
|
|
|
<div class="card p-6">
|
|
{{if .Error}}
|
|
<div class="alert-error">{{.Error}}</div>
|
|
{{end}}
|
|
|
|
<form method="POST" action="/source/{{.Webhook.ID}}/edit" class="space-y-6">
|
|
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
|
<div class="form-group">
|
|
<label for="name" class="label">Name</label>
|
|
<input type="text" id="name" name="name" value="{{.Webhook.Name}}" required class="input">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="description" class="label">Description</label>
|
|
<textarea id="description" name="description" rows="3" class="input">{{.Webhook.Description}}</textarea>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="retention_days" class="label">Retention (days)</label>
|
|
<input type="number" id="retention_days" name="retention_days" value="{{.Webhook.RetentionDays}}" min="1" max="365" class="input">
|
|
</div>
|
|
|
|
<div class="flex gap-3">
|
|
<button type="submit" class="btn-primary">Save Changes</button>
|
|
<a href="/source/{{.Webhook.ID}}" class="btn-secondary">Cancel</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{{end}}
|