1.0/mvp #33

Open
opened 2026-03-04 12:09:21 +01:00 by sneak · 2 comments
Owner

@clawbot are we ready to put this into prod and tag a 1.0.0, exposed to the whole internet? are we ready? what are we missing? does it have all appropriate prod security headers, all security pitfalls have been checked?

@clawbot are we ready to put this into prod and tag a 1.0.0, exposed to the whole internet? are we ready? what are we missing? does it have all appropriate prod security headers, all security pitfalls have been checked?
sneak added the
bot
label 2026-03-04 12:09:21 +01:00
Collaborator

Pre-1.0 Security Audit -- sneak/webhooker

Comprehensive security audit for internet-facing 1.0.0 readiness.


BLOCKERS -- Must Fix Before Internet Exposure

1. No Security Headers (middleware)

File: internal/middleware/middleware.go (entire file -- missing middleware)
File: internal/server/routes.go (middleware stack, lines 10-40)

The application sets zero production security headers. None of the following are present:

Header Status
Strict-Transport-Security (HSTS) Missing
X-Content-Type-Options: nosniff Missing
X-Frame-Options: DENY Missing
Content-Security-Policy Missing
X-XSS-Protection: 0 Missing
Referrer-Policy: strict-origin-when-cross-origin Missing
Permissions-Policy Missing

Impact: Without these, the app is vulnerable to clickjacking (iframe embedding), MIME type sniffing attacks, and lacks defense-in-depth for XSS. HSTS is critical for an internet-facing app to prevent SSL stripping.

Suggested fix: Add a SecurityHeaders() middleware that sets all headers on every response. Apply it early in the middleware stack in routes.go. CSP needs unsafe-eval for Alpine.js and unsafe-inline for Tailwind/style blocks. These should be tightened with nonces if possible.

The README TODO section confirms this is known unfinished work.


2. No CSRF Protection on Any State-Changing Forms

Files: All form templates and their POST handlers:

  • templates/login.html -- login form
  • templates/sources_new.html -- create webhook
  • templates/source_edit.html -- edit webhook
  • templates/source_detail.html -- delete webhook, add/delete/toggle entrypoints and targets
  • internal/handlers/auth.go -- login handler
  • internal/handlers/source_management.go -- all CRUD handlers

None of the 12+ POST forms include a CSRF token. No CSRF middleware exists.

Impact: An attacker can craft a malicious webpage that, when visited by an authenticated user, silently submits forms to create webhooks, add HTTP targets pointing anywhere, delete webhooks, or modify configuration. This is especially dangerous combined with the SSRF issue below -- an attacker could create a target that exfiltrates data from the internal network.

Note: SameSite=Lax on the session cookie provides partial mitigation -- it blocks cross-site POST requests from some vectors, but Lax is not sufficient for security-critical state-changing operations.

Suggested fix: Implement CSRF token middleware (e.g., gorilla/csrf or justinas/nosurf). Add a hidden csrf_token field to every form. Validate on every POST handler.

The README TODO section confirms: "CSRF protection for forms" is listed as unfinished.


3. No SSRF Protection -- HTTP Targets Can Hit Internal/Private IPs

File: internal/handlers/source_management.go, HandleTargetCreate() (line ~497-548)
File: internal/delivery/engine.go, doHTTPRequest() (line ~459-505)

When a user creates an HTTP target, the URL is accepted without any validation beyond checking it's non-empty. The delivery engine then makes HTTP POST requests to whatever URL is configured, including:

  • http://127.0.0.1:* / http://localhost:* -- local services
  • http://169.254.169.254/ -- cloud metadata (AWS/GCP/Azure instance credentials)
  • http://10.0.0.0/8, http://172.16.0.0/12, http://192.168.0.0/16 -- internal networks
  • file://, gopher://, etc. -- other URL schemes

Impact: A user (or an attacker who gained access via CSRF) can use webhooker as a proxy to scan internal networks, access cloud metadata services, or attack internal services. This is a critical vulnerability for any internet-facing service that makes outbound HTTP requests to user-controlled URLs.

Suggested fix:

  1. Validate target URLs at creation time -- must be http:// or https://, must not resolve to private/reserved IP ranges.
  2. Add a DNS resolution check before making outbound requests in doHTTPRequest() -- resolve the hostname, check if the IP is in a private range, and reject if so. Use a custom net.Dialer with a Control function that blocks private IPs.
  3. Block well-known metadata endpoints explicitly (169.254.169.254, fd00::, etc.)

4. No Rate Limiting on Login Endpoint

File: internal/handlers/auth.go, HandleLoginSubmit() (lines 28-89)
File: internal/server/routes.go, line 52

There is no rate limiting, account lockout, or delay on failed login attempts. An attacker can make unlimited login attempts at full speed.

Impact: Trivial brute-force attacks against user passwords. Even with Argon2id (which provides some computational cost per attempt), the lack of any rate limiting means an attacker can try millions of passwords.

Suggested fix:

  1. Add per-IP rate limiting on the login endpoint (e.g., 5 attempts per minute per IP).
  2. Add account lockout after N consecutive failed attempts (e.g., 10 failures, 15 minute lockout).
  3. Consider adding a progressive delay between failed attempts.

5. Session Fixation Vulnerability -- No Session Regeneration on Login

File: internal/handlers/auth.go, HandleLoginSubmit() (lines 74-82)

After successful password verification, the handler calls h.session.Get(r) to retrieve the existing session, sets user info on it, and saves it. The session ID (cookie) is never regenerated.

Impact: If an attacker can set a session cookie before the victim logs in (via XSS, network sniffing on HTTP, or subdomain cookie injection), the attacker's cookie remains valid after login, giving them access to the authenticated session.

Suggested fix: After successful authentication, destroy the old session and create a new one. gorilla/sessions doesn't have a built-in "regenerate" method, so destroy the old session, save to clear the cookie, then get a new session and set user info.


SHOULD-FIX -- Important for Production Hardening

6. No Target URL Validation

File: internal/handlers/source_management.go, HandleTargetCreate() (line ~530)

The only check on the URL is url == "". There's no validation that it's a well-formed URL, uses an allowed scheme (http/https only), or that the hostname is not an IP literal in a private range.

7. User Profile Route Missing Auth Middleware

File: internal/server/routes.go, lines 62-64

The /user/{username} route is not wrapped with s.mw.RequireAuth(). The handler checks auth internally, but this is defense-by-implementation rather than defense-by-design.

Suggested fix: Add r.Use(s.mw.RequireAuth()) to the route group.

8. No Request Body Size Limit on Form Endpoints

File: internal/handlers/auth.go, internal/handlers/source_management.go

The webhook handler properly limits body size to 1MB, but the login form, create form, edit form, and all other POST handlers call r.ParseForm() without setting a body size limit.

Suggested fix: Wrap r.Body with http.MaxBytesReader(w, r.Body, maxFormSize) before calling ParseForm(), or add a global MaxBytesReader middleware for non-webhook routes.

9. Admin Password Logged as Structured Log Field

File: internal/database/database.go, line 133

The initial admin password is logged via d.log.Info("admin user created", "password", password, ...). In production with structured JSON logging, this password will be written to wherever logs are shipped.

Suggested fix: Print the password to stderr directly (not via slog) with a clear banner, or use a separate output channel.

10. No Inactivity-Based Session Timeout

File: internal/session/session.go, lines 79-82

Sessions have a 7-day MaxAge but no activity-based expiration. A session remains valid for the full 7 days regardless of whether the user has been active.

11. No Cache-Control on Authenticated Pages

Authenticated pages don't set Cache-Control: no-store. Browsers and proxies may cache these pages, potentially exposing sensitive webhook configuration and logs.

12. WriteTimeout (10s) Conflicts with Middleware Timeout (60s)

File: internal/server/http.go, line 13 -- WriteTimeout: 10 * time.Second
File: internal/server/routes.go, line 31 -- middleware.Timeout(60 * time.Second)

The HTTP server's WriteTimeout of 10 seconds will kill connections before the 60-second middleware timeout fires.


NICE-TO-HAVE -- Defense in Depth

13. No Webhook Signature Verification

The webhook receiver doesn't verify HMAC signatures from senders. Listed in the TODO. Not strictly required for a store-and-forward proxy, but important for validating webhook authenticity.

14. No Per-Webhook Rate Limiting in Receiver

Listed in the README TODO. Without this, a misbehaving sender could flood a webhook with events.

15. No Automatic Event Retention Cleanup

The retention_days field exists but automatic cleanup is not implemented. Per-webhook databases will grow unbounded.

16. Password Change Flow Not Implemented

Currently there's no way to change the admin password through the UI.


What's Already Good

  • Templates use html/template -- automatic context-aware escaping, no XSS. No use of template.HTML or other unescaped types.
  • GORM parameterized queries -- no SQL injection. All Where() calls use parameterized ? placeholders.
  • Argon2id password hashing -- 64MB memory, 1 iteration, 4 threads, 32-byte key, 16-byte salt. Good parameters.
  • Constant-time password comparison -- uses subtle.ConstantTimeCompare.
  • Session cookie security -- HttpOnly: true, Secure: true in prod, SameSite: Lax.
  • Container runs as non-root -- UID 1000, dedicated webhooker user.
  • Docker base images pinned by SHA256 -- compliant with REPO_POLICIES.
  • CORS is no-op in production -- no Access-Control-Allow-Origin: * in prod mode.
  • Error messages don't leak internals -- generic "Internal server error" to users, details only in server logs.
  • Webhook body size limited to 1MB -- prevents memory exhaustion from large payloads.
  • Outbound HTTP client has 30s timeout -- prevents indefinite hangs.
  • No secrets in error responses -- verified across all handlers.
  • 32-byte cryptographically random session key -- auto-generated and stored in DB.
  • 3,383 lines of tests -- good coverage of delivery engine, middleware, sessions, passwords.
  • docker build . passes -- CI gate is green.

Dependency Check

Go modules are reasonably current. Key dependency versions:

  • golang.org/x/crypto v0.38.0 -- current, no known CVEs
  • gorilla/sessions v1.4.0 -- current
  • gorilla/securecookie v1.1.2 -- current
  • gorm.io/gorm v1.25.5 -- current
  • go-chi/chi v1.5.5 -- current
  • sentry-go v0.25.0 -- current
  • prometheus/client_golang v1.18.0 -- slightly old but no security CVEs

No critical CVEs identified in the dependency tree.


Verdict: NO-GO

webhooker is not ready for internet-facing 1.0.0 deployment. The codebase has a solid foundation (good architecture, proper password hashing, template escaping, parameterized queries, non-root container), but it is missing five critical security features that the README itself acknowledges as TODO:

  1. Security headers -- zero production headers
  2. CSRF protection -- zero forms protected
  3. SSRF prevention -- delivery engine will POST to any URL including internal networks
  4. Login rate limiting -- zero brute-force protection
  5. Session fixation -- sessions not regenerated on login

Items 1-4 are explicitly listed as unfinished in the README TODO. Item 5 is a code-level vulnerability. All five are standard expectations for any internet-facing authenticated web application.

Minimum for 1.0: Fix all five BLOCKER items. The SHOULD-FIX items (especially #6 URL validation, #8 body size limits, #9 password logging) should also be addressed but are lower priority.


Suggested Issues to File

  1. Add production security headers middleware (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy)
  2. Implement CSRF protection for all forms
  3. Add SSRF prevention for HTTP delivery targets (private IP blocking, URL validation)
  4. Add rate limiting on login endpoint
  5. Fix session fixation: regenerate session on login
  6. Add request body size limits on form endpoints
  7. Improve admin password bootstrap logging (don't use structured slog for passwords)
# Pre-1.0 Security Audit -- sneak/webhooker Comprehensive security audit for internet-facing 1.0.0 readiness. --- ## BLOCKERS -- Must Fix Before Internet Exposure ### 1. No Security Headers (middleware) **File:** `internal/middleware/middleware.go` (entire file -- missing middleware) **File:** `internal/server/routes.go` (middleware stack, lines 10-40) The application sets **zero** production security headers. None of the following are present: | Header | Status | |--------|--------| | `Strict-Transport-Security` (HSTS) | Missing | | `X-Content-Type-Options: nosniff` | Missing | | `X-Frame-Options: DENY` | Missing | | `Content-Security-Policy` | Missing | | `X-XSS-Protection: 0` | Missing | | `Referrer-Policy: strict-origin-when-cross-origin` | Missing | | `Permissions-Policy` | Missing | **Impact:** Without these, the app is vulnerable to clickjacking (iframe embedding), MIME type sniffing attacks, and lacks defense-in-depth for XSS. HSTS is critical for an internet-facing app to prevent SSL stripping. **Suggested fix:** Add a `SecurityHeaders()` middleware that sets all headers on every response. Apply it early in the middleware stack in `routes.go`. CSP needs `unsafe-eval` for Alpine.js and `unsafe-inline` for Tailwind/style blocks. These should be tightened with nonces if possible. The README TODO section confirms this is known unfinished work. --- ### 2. No CSRF Protection on Any State-Changing Forms **Files:** All form templates and their POST handlers: - `templates/login.html` -- login form - `templates/sources_new.html` -- create webhook - `templates/source_edit.html` -- edit webhook - `templates/source_detail.html` -- delete webhook, add/delete/toggle entrypoints and targets - `internal/handlers/auth.go` -- login handler - `internal/handlers/source_management.go` -- all CRUD handlers **None** of the 12+ POST forms include a CSRF token. No CSRF middleware exists. **Impact:** An attacker can craft a malicious webpage that, when visited by an authenticated user, silently submits forms to create webhooks, add HTTP targets pointing anywhere, delete webhooks, or modify configuration. This is especially dangerous combined with the SSRF issue below -- an attacker could create a target that exfiltrates data from the internal network. **Note:** `SameSite=Lax` on the session cookie provides *partial* mitigation -- it blocks cross-site POST requests from some vectors, but Lax is not sufficient for security-critical state-changing operations. **Suggested fix:** Implement CSRF token middleware (e.g., `gorilla/csrf` or `justinas/nosurf`). Add a hidden `csrf_token` field to every form. Validate on every POST handler. The README TODO section confirms: "CSRF protection for forms" is listed as unfinished. --- ### 3. No SSRF Protection -- HTTP Targets Can Hit Internal/Private IPs **File:** `internal/handlers/source_management.go`, `HandleTargetCreate()` (line ~497-548) **File:** `internal/delivery/engine.go`, `doHTTPRequest()` (line ~459-505) When a user creates an HTTP target, the URL is accepted without any validation beyond checking it's non-empty. The delivery engine then makes HTTP POST requests to whatever URL is configured, including: - `http://127.0.0.1:*` / `http://localhost:*` -- local services - `http://169.254.169.254/` -- cloud metadata (AWS/GCP/Azure instance credentials) - `http://10.0.0.0/8`, `http://172.16.0.0/12`, `http://192.168.0.0/16` -- internal networks - `file://`, `gopher://`, etc. -- other URL schemes **Impact:** A user (or an attacker who gained access via CSRF) can use webhooker as a proxy to scan internal networks, access cloud metadata services, or attack internal services. This is a **critical** vulnerability for any internet-facing service that makes outbound HTTP requests to user-controlled URLs. **Suggested fix:** 1. Validate target URLs at creation time -- must be `http://` or `https://`, must not resolve to private/reserved IP ranges. 2. Add a DNS resolution check before making outbound requests in `doHTTPRequest()` -- resolve the hostname, check if the IP is in a private range, and reject if so. Use a custom `net.Dialer` with a `Control` function that blocks private IPs. 3. Block well-known metadata endpoints explicitly (169.254.169.254, fd00::, etc.) --- ### 4. No Rate Limiting on Login Endpoint **File:** `internal/handlers/auth.go`, `HandleLoginSubmit()` (lines 28-89) **File:** `internal/server/routes.go`, line 52 There is no rate limiting, account lockout, or delay on failed login attempts. An attacker can make unlimited login attempts at full speed. **Impact:** Trivial brute-force attacks against user passwords. Even with Argon2id (which provides some computational cost per attempt), the lack of any rate limiting means an attacker can try millions of passwords. **Suggested fix:** 1. Add per-IP rate limiting on the login endpoint (e.g., 5 attempts per minute per IP). 2. Add account lockout after N consecutive failed attempts (e.g., 10 failures, 15 minute lockout). 3. Consider adding a progressive delay between failed attempts. --- ### 5. Session Fixation Vulnerability -- No Session Regeneration on Login **File:** `internal/handlers/auth.go`, `HandleLoginSubmit()` (lines 74-82) After successful password verification, the handler calls `h.session.Get(r)` to retrieve the **existing** session, sets user info on it, and saves it. The session ID (cookie) is never regenerated. **Impact:** If an attacker can set a session cookie before the victim logs in (via XSS, network sniffing on HTTP, or subdomain cookie injection), the attacker's cookie remains valid after login, giving them access to the authenticated session. **Suggested fix:** After successful authentication, destroy the old session and create a new one. gorilla/sessions doesn't have a built-in "regenerate" method, so destroy the old session, save to clear the cookie, then get a new session and set user info. --- ## SHOULD-FIX -- Important for Production Hardening ### 6. No Target URL Validation **File:** `internal/handlers/source_management.go`, `HandleTargetCreate()` (line ~530) The only check on the URL is `url == ""`. There's no validation that it's a well-formed URL, uses an allowed scheme (http/https only), or that the hostname is not an IP literal in a private range. ### 7. User Profile Route Missing Auth Middleware **File:** `internal/server/routes.go`, lines 62-64 The `/user/{username}` route is **not** wrapped with `s.mw.RequireAuth()`. The handler checks auth internally, but this is defense-by-implementation rather than defense-by-design. **Suggested fix:** Add `r.Use(s.mw.RequireAuth())` to the route group. ### 8. No Request Body Size Limit on Form Endpoints **File:** `internal/handlers/auth.go`, `internal/handlers/source_management.go` The webhook handler properly limits body size to 1MB, but the login form, create form, edit form, and all other POST handlers call `r.ParseForm()` without setting a body size limit. **Suggested fix:** Wrap `r.Body` with `http.MaxBytesReader(w, r.Body, maxFormSize)` before calling `ParseForm()`, or add a global `MaxBytesReader` middleware for non-webhook routes. ### 9. Admin Password Logged as Structured Log Field **File:** `internal/database/database.go`, line 133 The initial admin password is logged via `d.log.Info("admin user created", "password", password, ...)`. In production with structured JSON logging, this password will be written to wherever logs are shipped. **Suggested fix:** Print the password to stderr directly (not via slog) with a clear banner, or use a separate output channel. ### 10. No Inactivity-Based Session Timeout **File:** `internal/session/session.go`, lines 79-82 Sessions have a 7-day `MaxAge` but no activity-based expiration. A session remains valid for the full 7 days regardless of whether the user has been active. ### 11. No Cache-Control on Authenticated Pages Authenticated pages don't set `Cache-Control: no-store`. Browsers and proxies may cache these pages, potentially exposing sensitive webhook configuration and logs. ### 12. WriteTimeout (10s) Conflicts with Middleware Timeout (60s) **File:** `internal/server/http.go`, line 13 -- `WriteTimeout: 10 * time.Second` **File:** `internal/server/routes.go`, line 31 -- `middleware.Timeout(60 * time.Second)` The HTTP server's `WriteTimeout` of 10 seconds will kill connections before the 60-second middleware timeout fires. --- ## NICE-TO-HAVE -- Defense in Depth ### 13. No Webhook Signature Verification The webhook receiver doesn't verify HMAC signatures from senders. Listed in the TODO. Not strictly required for a store-and-forward proxy, but important for validating webhook authenticity. ### 14. No Per-Webhook Rate Limiting in Receiver Listed in the README TODO. Without this, a misbehaving sender could flood a webhook with events. ### 15. No Automatic Event Retention Cleanup The `retention_days` field exists but automatic cleanup is not implemented. Per-webhook databases will grow unbounded. ### 16. Password Change Flow Not Implemented Currently there's no way to change the admin password through the UI. --- ## What's Already Good - **Templates use `html/template`** -- automatic context-aware escaping, no XSS. No use of `template.HTML` or other unescaped types. - **GORM parameterized queries** -- no SQL injection. All `Where()` calls use parameterized `?` placeholders. - **Argon2id password hashing** -- 64MB memory, 1 iteration, 4 threads, 32-byte key, 16-byte salt. Good parameters. - **Constant-time password comparison** -- uses `subtle.ConstantTimeCompare`. - **Session cookie security** -- `HttpOnly: true`, `Secure: true` in prod, `SameSite: Lax`. - **Container runs as non-root** -- UID 1000, dedicated `webhooker` user. - **Docker base images pinned by SHA256** -- compliant with REPO_POLICIES. - **CORS is no-op in production** -- no `Access-Control-Allow-Origin: *` in prod mode. - **Error messages don't leak internals** -- generic "Internal server error" to users, details only in server logs. - **Webhook body size limited to 1MB** -- prevents memory exhaustion from large payloads. - **Outbound HTTP client has 30s timeout** -- prevents indefinite hangs. - **No secrets in error responses** -- verified across all handlers. - **32-byte cryptographically random session key** -- auto-generated and stored in DB. - **3,383 lines of tests** -- good coverage of delivery engine, middleware, sessions, passwords. - **`docker build .` passes** -- CI gate is green. --- ## Dependency Check Go modules are reasonably current. Key dependency versions: - `golang.org/x/crypto v0.38.0` -- current, no known CVEs - `gorilla/sessions v1.4.0` -- current - `gorilla/securecookie v1.1.2` -- current - `gorm.io/gorm v1.25.5` -- current - `go-chi/chi v1.5.5` -- current - `sentry-go v0.25.0` -- current - `prometheus/client_golang v1.18.0` -- slightly old but no security CVEs No critical CVEs identified in the dependency tree. --- ## Verdict: **NO-GO** webhooker is **not ready** for internet-facing 1.0.0 deployment. The codebase has a solid foundation (good architecture, proper password hashing, template escaping, parameterized queries, non-root container), but it is missing **five critical security features** that the README itself acknowledges as TODO: 1. **Security headers** -- zero production headers 2. **CSRF protection** -- zero forms protected 3. **SSRF prevention** -- delivery engine will POST to any URL including internal networks 4. **Login rate limiting** -- zero brute-force protection 5. **Session fixation** -- sessions not regenerated on login Items 1-4 are explicitly listed as unfinished in the README TODO. Item 5 is a code-level vulnerability. All five are standard expectations for any internet-facing authenticated web application. **Minimum for 1.0:** Fix all five BLOCKER items. The SHOULD-FIX items (especially #6 URL validation, #8 body size limits, #9 password logging) should also be addressed but are lower priority. --- ## Suggested Issues to File 1. **Add production security headers middleware** (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) 2. **Implement CSRF protection for all forms** 3. **Add SSRF prevention for HTTP delivery targets** (private IP blocking, URL validation) 4. **Add rate limiting on login endpoint** 5. **Fix session fixation: regenerate session on login** 6. **Add request body size limits on form endpoints** 7. **Improve admin password bootstrap logging** (don't use structured slog for passwords) <!-- session: agent:sdlc-manager:subagent:9f8f5e11-3608-4eda-bfb3-c0720af17e60 -->
Collaborator

Security Audit Complete — Issues Created

Created 7 tracking issues from the audit findings above:

Blockers (must-fix before internet exposure):

  • #34 — Add production security headers middleware
  • #35 — Implement CSRF protection for all forms
  • #36 — Add SSRF prevention for HTTP delivery targets
  • #37 — Add rate limiting on login endpoint
  • #38 — Fix session fixation: regenerate session on login

Should-fix:

  • #39 — Add request body size limits on form endpoints
  • #40 — Fix admin password bootstrap logging

Verdict: NO-GO for internet-facing 1.0.0. 5 blockers must be resolved first. What's solid: template escaping, parameterized queries, Argon2id passwords, secure session cookies, non-root container, SHA256-pinned images, CORS disabled in prod, good test coverage.

Add bot label to issues you want automated.

## Security Audit Complete — Issues Created Created 7 tracking issues from the [audit findings above](https://git.eeqj.de/sneak/webhooker/issues/33#issuecomment-10915): **Blockers (must-fix before internet exposure):** - [#34](https://git.eeqj.de/sneak/webhooker/issues/34) — Add production security headers middleware - [#35](https://git.eeqj.de/sneak/webhooker/issues/35) — Implement CSRF protection for all forms - [#36](https://git.eeqj.de/sneak/webhooker/issues/36) — Add SSRF prevention for HTTP delivery targets - [#37](https://git.eeqj.de/sneak/webhooker/issues/37) — Add rate limiting on login endpoint - [#38](https://git.eeqj.de/sneak/webhooker/issues/38) — Fix session fixation: regenerate session on login **Should-fix:** - [#39](https://git.eeqj.de/sneak/webhooker/issues/39) — Add request body size limits on form endpoints - [#40](https://git.eeqj.de/sneak/webhooker/issues/40) — Fix admin password bootstrap logging **Verdict: NO-GO for internet-facing 1.0.0.** 5 blockers must be resolved first. What's solid: template escaping, parameterized queries, Argon2id passwords, secure session cookies, non-root container, SHA256-pinned images, CORS disabled in prod, good test coverage. Add `bot` label to issues you want automated.
Sign in to join this conversation.
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: sneak/webhooker#33
No description provided.