Commit Graph
6 Commits
Author SHA1 Message Date
sneak fcee8ae182 Build the middleware test cookie stores with the production constructor (closes #154)
check / check (push) Successful in 3m17s
session.NewStore moves from internal/session/export_test.go into
internal/session/testing.go, so packages outside session can build the
store the application runs with. The two middleware test helpers that
assembled their own store by assigning store.Options now call it, which
puts their securecookie codecs on the same 7-day cap as production
instead of the library's 30-day default.

Model: opus-5-5
2026-10-02 11:20:29 +00:00
clawbot 032f265d69 Derive cookie Secure and CSRF strictness from the request transport (closes #269)
check / check (push) Successful in 2m56s
2026-08-24 03:01:37 +02:00
clawbot c2cd2c440b Add inactivity-based session timeout (closes #66) (#105)
check / check (push) Successful in 4s
Sessions now carry a server-enforced idle deadline (SESSION_IDLE_TIMEOUT,
default 24h) alongside the 7-day absolute cap, refreshed on authenticated
activity. Activity never extends the absolute cap.
2026-08-10 16:12:40 +02:00
clawbotandsneak 734606b7af Update golangci-lint to v2.12.2 with canonical config (#86)
check / check (push) Successful in 3s
Bumps golangci-lint from v2.11.3 to v2.12.2 and adopts the canonical lint config.

## Version pins

- `Dockerfile`: `golangci/golangci-lint:v2.12.2` Debian image, pinned by digest, dated `2026-08-07`
- `script/bootstrap`: `GOLANGCI_LINT_VERSION=2.12.2` with updated sha256 pins for the `linux-amd64` and `linux-arm64` release archives

## Config

`.golangci.yml` replaced with the canonical config. The previous file kept `lll`/`funlen`/`cyclop`/`dupl` settings under the top-level `linters-settings` key, which the v2 schema ignores; the canonical config nests them under `linters.settings`, so those thresholds now actually apply. The unsupported `issues.exclude-use-default` key was dropped.

## Lint fixes (32 findings)

- `lll` (7): wrapped or shortened over-length lines (struct tag comments moved above fields, test logger construction split, `session.NewForTest` signature wrapped, shortened a `#nosec` comment)
- `goconst` (17): replaced repeated `"POST"`/`"PUT"` literals with `http.MethodPost`/`http.MethodPut`, added shared test constants for `webhooker-test`/`test`/`application/json`, and added `tmplKeyError`/`tmplKeyWebhook` constants for template data keys in `internal/handlers`
- `dupl` (8): merged `buildHTTPTargetConfig` and `buildSlackTargetConfig` into a parameterized `buildURLTargetConfig`; removed the duplicate `iWebhookDB` test helper in favor of `testWebhookDB`; extracted shared helpers in middleware and session tests

No `//nolint` directives were added and behavior is unchanged. `make check` (fmt-check, tests, lint) passes.

Note: golangci-lint v2.12 deprecates the `gomodguard` linter in favor of `gomodguard_v2`; the canonical config change for that is left for a future coordinated update.
Co-authored-by: sneak <sneak@sneak.berlin>
Reviewed-on: #86
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
2026-08-07 23:18:49 +02:00
60786c5019 feat: add CSRF protection, SSRF prevention, and login rate limiting (#42)
check / check (push) Successful in 4s
## Security Hardening

This PR implements three security hardening issues:

### CSRF Protection (closes #35)

- Session-based CSRF tokens with cryptographically random 256-bit 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 webhook POSTs) and `/api` (stateless API)

### SSRF Prevention (closes #36)

- `ValidateTargetURL()` blocks private/reserved IP ranges at target creation time
- 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` in the delivery engine for defense-in-depth (prevents DNS rebinding attacks)
- Only `http` and `https` schemes allowed

### Login Rate Limiting (closes #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 every 5 minutes
- `X-Forwarded-For` and `X-Real-IP` header support for reverse proxies

### Files Changed

**New files:**
- `internal/middleware/csrf.go` + tests — CSRF middleware
- `internal/middleware/ratelimit.go` + tests — Login rate limiter
- `internal/delivery/ssrf.go` + tests — SSRF validation + safe transport

**Modified files:**
- `internal/server/routes.go` — Wire CSRF and rate limit middleware
- `internal/handlers/handlers.go` — Inject CSRF token into template data
- `internal/handlers/source_management.go` — SSRF validation on target creation
- `internal/delivery/engine.go` — SSRF-safe HTTP transport for production
- All form templates — Added hidden `csrf_token` fields
- `README.md` — Updated Security section and TODO checklist

`docker build .` passes (lint + tests + build).

Co-authored-by: clawbot <clawbot@noreply.git.eeqj.de>
Co-authored-by: clawbot <clawbot@eeqj.de>
Co-authored-by: Jeffrey Paul <sneak@noreply.example.org>
Reviewed-on: #42
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
2026-03-17 12:38:45 +01:00
clawbotandclawbot 289f479772 test: add tests for delivery, middleware, and session packages (#32)
check / check (push) Has been cancelled
## Summary

Add comprehensive test coverage for three previously-untested packages, addressing [issue #28](#28).

## Coverage Improvements

| Package | Before | After |
|---------|--------|-------|
| `internal/delivery` | 37.1% | 74.5% |
| `internal/middleware` | 0.0% | 70.2% |
| `internal/session` | 0.0% | 51.5% |

## What's Tested

### delivery (37% → 75%)
- `processNewTask` with inline and large (DB-fetched) bodies
- `processRetryTask` success, skip non-retrying, large body fetch
- Worker lifecycle start/stop, retry channel processing
- `processDelivery` unknown target type handling
- `recoverPendingDeliveries`, `recoverWebhookDeliveries`, `recoverInFlight`
- HTTP delivery with custom headers, timeout, invalid config
- `Notify` batching

### middleware (0% → 70%)
- Logging middleware status code capture and pass-through
- `LoggingResponseWriter` delegation
- CORS dev mode (allow-all) and prod mode (no-op)
- `RequireAuth` redirect for unauthenticated, pass-through for authenticated
- `MetricsAuth` basic auth validation
- `ipFromHostPort` helper

### session (0% → 52%)
- `Get`/`Save` round-trip with real cookie store
- `SetUser`, `GetUserID`, `GetUsername`, `IsAuthenticated`
- `ClearUser` removes all keys
- `Destroy` invalidates session (MaxAge -1)
- Session persistence across requests
- Edge cases: overwrite user, wrong type, constants

## Test Helpers Added
- `database.NewTestDatabase` / `NewTestWebhookDBManager` — cross-package test helpers for delivery integration tests
- `session.NewForTest` — creates session manager without fx lifecycle for middleware tests

## Notes
- No production code modified
- All tests use `httptest`, SQLite in-memory, and real cookie stores — no external network calls
- Full test suite completes in ~3.5s within the 30s timeout
- `docker build .` passes (lint + test + build)

closes #28

Co-authored-by: clawbot <clawbot@noreply.git.eeqj.de>
Reviewed-on: #32
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
2026-03-04 12:07:23 +01:00