Commit Graph
44 Commits
Author SHA1 Message Date
fen 073d39ccba Fix #116: allow inline styles in CSP (style-src 'self' 'unsafe-inline')
CI / test (pull_request) Successful in 24s
CI / docker (pull_request) Skipped
2026-09-09 21:18:53 -05:00
fen a677e627a8 Fix #114: use presetColors(t.id) for theme card swatches
CI / test (pull_request) Successful in 24s
CI / docker (pull_request) Skipped
t.colors was removed by #113; rendering threw on first iteration and
the theme grid rendered empty. Use the colors returned by
presetColors(t.id) instead. Verified in headless chromium: 5
theme-card elements render on /settings.
2026-09-09 20:55:13 -05:00
fen 6b95994c53 Admin lock on every load, drop settings hint, derive theme swatches from CSS vars (#112)
CI / test (pull_request) Successful in 24s
CI / docker (pull_request) Skipped
2026-09-09 19:52:32 -05:00
fen 7fde147500 Merge pull request 'Fix theme persistence on reload' (#109) from fix-100-theme-persist into dev
CI / test (push) Successful in 23s
CI / docker (push) Successful in 38s
2026-09-10 00:22:04 +00:00
fen df73d56e32 Merge remote-tracking branch 'origin/fix-93-version-label' into dev
CI / test (push) Successful in 28s
CI / docker (push) Successful in 49s
2026-09-09 18:41:26 -05:00
fen 805cc3ff2b web: restore persisted theme on page load (#100)
CI / test (pull_request) Successful in 24s
CI / docker (pull_request) Skipped
2026-09-09 18:40:09 -05:00
poslop f37cc932c4 store: burn_after_reads>0 implies burn-after-read mode (#82)
CI / test (pull_request) Successful in 24s
CI / docker (pull_request) Skipped
2026-09-09 18:28:00 -05:00
agent 65ed0c7043 Show actual version in topbar instead of hardcoded beta (Fixes #93)
CI / test (pull_request) Successful in 26s
CI / docker (pull_request) Skipped
2026-09-09 18:27:57 -05:00
poslop 69da340224 Settings page: theme switcher with live preview (#100)
CI / test (push) Successful in 23s
CI / docker (push) Skipped
2026-09-09 18:21:45 -05:00
poslop 9df6224a27 Result box: color-code by status + friendly error messages from error codes (#105)
CI / test (push) Successful in 23s
CI / docker (push) Skipped
- Backend create/can validation paths emit machine-readable error codes
  (slug_taken, slug_invalid, content_empty, content_too_large,
  expiry_invalid, rate_limited, ...) alongside the human message
- new.html JS maps codes to plain-language guidance with generic fallback
- Result card colored via --ok/--err/--warn left border (result-ok/err/warn)
- docs/API.md error section documents the code field
- Tests assert the code on every validation path
2026-09-09 17:28:25 -05:00
poslop c35531e03e Settings page: small muted Admin link (#102)
CI / test (push) Successful in 22s
CI / docker (push) Skipped
2026-09-09 17:17:00 -05:00
poslop be2b4fb47f Sort arrows: left of label, further spaced, vertically centered, no label shift (#101)
Arrow is absolutely positioned at left:8px in the th, label padding-left
24px; translateY(-50%) keeps it centered regardless of row height, and
being out of flow the label never moves between sorted/unsorted states.
2026-09-09 17:17:00 -05:00
poslop 064f05536a Admin page: validate key before persisting to sessionStorage (#99)
Panel was already display:none-gated until auth; tightened so a wrong
submitted key is never stored in sessionStorage — the typed key is
validated via the settings API first, and only persisted on 200.
2026-09-09 17:17:00 -05:00
poslop 4175ffef3e CSS: inline sort arrow with uniform 8px gap (#103), full-pill slug roundness (#104)
CI / test (push) Successful in 22s
CI / docker (push) Skipped
2026-09-09 17:10:24 -05:00
poslop ea9edc211a UI: consistent sort arrow gap, uniform pill roundness (slug -> radius-sm)
CI / test (push) Successful in 22s
CI / docker (push) Skipped
2026-09-09 16:50:06 -05:00
poslop b21be29daa minor fixes: version label (#93), view dedupe tests (#95) 2026-09-09 16:40:53 -05:00
poslop cd70dd0b0e Merge origin/main (license, compose, #65 leak guard) into cans work
CI / test (push) Failing after 6s
CI / docker (push) Skipped
2026-09-09 16:39:14 -05:00
poslop 5b9be808ed Merge origin/main: keep can listing rows with #65 password-metadata leak guard 2026-09-09 16:36:55 -05:00
poslop 19804d47a3 Cans UI + parity: unlock-cookie flow, /can page, listings badge, custom slug, delete, sweeper; #32 perf notes (#4, #32) 2026-09-09 16:34:51 -05:00
poslop 959a83be1e Merge pull request 'Bound title and language at create time (#86)' (#91) from fix-86-field-bounds into main
CI / test (push) Successful in 24s
CI / docker (push) Successful in 42s
2026-09-09 15:59:32 +00:00
poslop 5b068faf8f Merge pull request 'ratelimit: rightmost untrusted-hop-safe client IP (fixes #85)' (#89) from fix-85-xff into main
CI / test (push) Successful in 27s
CI / docker (push) Skipped
2026-09-09 15:59:29 +00:00
poslop b0360af79c Merge pull request 'Fix #83: create API ignores boolean public field' (#88) from fix-83-public-false into main
CI / test (push) Successful in 26s
CI / docker (push) Skipped
2026-09-09 15:59:26 +00:00
poslop 806798fae6 Merge pull request 'Rate-limit all password verification attempts (#81)' (#90) from fix-81-password-ratelimit into main
CI / test (push) Successful in 26s
CI / docker (push) Skipped
2026-09-09 15:59:24 +00:00
fen 389e4d333f Bound title and language at create time (#86)
CI / test (pull_request) Successful in 21s
CI / docker (pull_request) Skipped
Titles are trimmed and truncated to 200 characters; language must be
blank or match ^[a-zA-Z0-9+#-]{1,40}$ (max 40 chars) or the create is
rejected with a clear 400. Regression tests cover truncation, malformed
and oversized language values, valid identifiers, and blank metadata.
2026-09-09 10:58:34 -05:00
fen 99a044e8a2 Rate-limit all password verification attempts (#81)
CI / test (pull_request) Successful in 22s
CI / docker (pull_request) Skipped
GET ?password= and X-Paste-Password header checks in handleGetPaste were
unlimited (pentest2: 8 wrong = 8x401, no 429). Gate every password
verification on the same per-IP+paste unlock limiter (5/min, 429 with
Retry-After) already used by the POST unlock form path.

Regression tests: repeated wrong passwords via GET ?password= and via
header must eventually 429; correct password within burst still 200.

Fixes #81
2026-09-09 10:58:21 -05:00
agent 3d1d5ee1fd ratelimit: key on rightmost X-Forwarded-For entry (fixes #85)
CI / test (pull_request) Successful in 21s
CI / docker (pull_request) Skipped
2026-09-09 10:57:59 -05:00
Hermes Agent ccb22a2d28 #83: accept boolean public field in create API (false->unlisted, true->public)
CI / test (pull_request) Successful in 23s
CI / docker (pull_request) Skipped
2026-09-09 10:57:56 -05:00
fen 3d415edbdc fix #84: security headers dropped because they were set post-flush; set pre-handler
CI / test (pull_request) Successful in 21s
CI / docker (pull_request) Skipped
2026-09-09 10:54:16 -05:00
poslop 4cb8a3e127 Merge pull request 'Search: loading indicator and performance note (#32)' (#72) from issue-32-search-indicator into main
CI / test (push) Successful in 30s
CI / docker (push) Skipped
2026-09-09 14:26:25 +00:00
poslop 91dbf6f344 Merge pull request 'fix: input validation gaps (#68)' (#78) from issue-68-input-validation into main
CI / test (push) Successful in 23s
CI / docker (push) Skipped
2026-09-09 14:26:19 +00:00
poslop b600cdf51a Merge pull request 'Admin key endpoint: per-IP rate limiting and lockout (#66)' (#73) from issue-66-admin-ratelimit into main
CI / test (push) Successful in 24s
CI / docker (push) Skipped
2026-09-09 14:26:13 +00:00
poslop 47c9fa6386 Merge pull request 'Clamp expires_in at API boundary (#60)' (#74) from issue-60-expiry-clamp into main
CI / test (push) Successful in 23s
CI / docker (push) Skipped
2026-09-09 14:26:07 +00:00
poslop b7d1119d6d Merge pull request 'docs: design for optional client-side E2E encryption (issue #39)' (#75) from issue-39-e2e-design into main
CI / test (push) Successful in 20s
CI / docker (push) Skipped
2026-09-09 14:26:02 +00:00
poslop 44fe3c5772 Merge pull request 'Security headers middleware: CSP, Referrer-Policy, nosniff (#59)' (#76) from issue-59-security-headers into main
CI / test (push) Successful in 21s
CI / docker (push) Skipped
2026-09-09 14:26:01 +00:00
poslop 91568c0598 fix #63: require deletion token on DELETE /api/pastes/{id}
CI / test (pull_request) Successful in 24s
CI / docker (pull_request) Skipped
- DELETE now demands the create-time deletion token (Authorization
  header: Bearer/Token/bare, or ?token= query param), compared with
  the constant-time store.DeletionTokenEqual. 403 otherwise.
- Creator-browser deletes via the /mine button (matching vwr cookie,
  #37) remain allowed; other browsers and plain API clients get 403.
- Regression tests: no token, wrong token (header+query), correct
  token (header+query), creator-cookie path, token extraction.
- Adapted TestSoftDelete to pass the deletion token.
- docs/API.md delete section updated.
- Based on #58's SoftDelete (bool, error) signature.
2026-09-09 09:21:28 -05:00
agent 94a4a3c2ec fix: input validation gaps (#68)
CI / test (pull_request) Successful in 24s
CI / docker (pull_request) Skipped
- enforce server-side body cap via http.MaxBytesReader: oversized JSON
  bodies are rejected with 413 instead of being fully decoded first
- negative burn_after_reads rejected with 400 (zero still = default 1)
- limit=0 explicitly maps to default page size; shared parseLimit clamp
  for /api/public and /api/mine (huge/non-numeric values too)
- negative/non-numeric offset clamped to 0 via parseOffset (was
  pass-through)
- regression tests in issue68_validation_test.go

expires_at/expires_in validation intentionally excluded: covered by #60.
2026-09-09 09:21:25 -05:00
agent 78374b2d49 fix: atomic burn-after-read claim (#58)
CI / test (pull_request) Successful in 20s
CI / docker (pull_request) Skipped
SoftDelete now reports whether it performed the delete (conditional
UPDATE ... WHERE deleted_at IS NULL checked via RowsAffected).
RegisterRead returns an admitted flag: legacy burn pastes admit exactly
one reader (the atomic soft-delete winner), and burn-after-N pastes
increment reads_used via a conditional UPDATE guarded on
reads_used < reads_limit, so concurrent readers cannot both consume the
final read. API, HTML, and raw read paths return 404 when the reader
loses the burn claim; content is never served twice.

OpenStore pins the SQLite pool to one connection: concurrent writes on
separate pooled connections surfaced SQLITE_BUSY as spurious 500s
instead of serializing.

Adds concurrency regression tests: 24 parallel readers of a burn paste
(exactly one receives content, none of the others leak it) and 30
parallel readers vs a 3-read budget (exactly 3 admitted, then 404).
2026-09-09 09:18:42 -05:00
palette-agent e08cafe9c8 security headers middleware: CSP, Referrer-Policy, nosniff on HTML pages (#59)
CI / test (pull_request) Successful in 30s
CI / docker (pull_request) Skipped
- web.SecurityHeaders middleware wired into the chi router
- Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' (page scripts are inline); frame-ancestors 'none'
- Referrer-Policy: no-referrer, X-Content-Type-Options: nosniff
- Applied only to text/html responses; JSON API and /raw pass through unchanged
- Regression test internal/web/securityheaders_test.go
2026-09-09 09:16:42 -05:00
fen bb5c4f0186 fix #60: apply ValidExpiry clamp to cans create handler (was lost in working tree)
CI / test (pull_request) Successful in 27s
CI / docker (pull_request) Skipped
2026-09-09 09:15:39 -05:00
palette-agent 9811191648 admin: per-IP rate limit (5/min) on admin key attempts (#66)
CI / test (pull_request) Successful in 23s
CI / docker (pull_request) Skipped
2026-09-09 09:15:01 -05:00
poslop c288fc73a7 Search: spinner indicator + performance note on client-side filtering (#32)
CI / test (pull_request) Successful in 19s
CI / docker (pull_request) Skipped
- Fix filter fetch to request limit=100 (API max) instead of 500, which
  the API silently clamped, so filtered results actually cover the fetch window.
- Document client-side filtering behavior and limits in README Performance Notes.
2026-09-09 09:13:40 -05:00
Hermes Agent f6c4342468 Exclude password-protected pastes from public listings (#65)
CI / test (pull_request) Successful in 20s
CI / docker (pull_request) Skipped
ListPublic and its COUNT query now filter password_hash IS NULL, so
/api/public (and any page backed by it) no longer leaks metadata
(title, slug, existence) of password-protected pastes. Unlisted
pastes were already excluded. Adds regression test covering both.
2026-09-09 09:13:26 -05:00
poslop f611cc3e9e fix #60: clamp expires_in on cans API (400 on zero/negative, cap 1yr) 2026-09-09 09:13:21 -05:00
poslop 4f1e901f04 Refactor: split monolith into cmd/palette + internal/{store,api,web,lang} (#35)
CI / test (push) Successful in 19s
CI / docker (push) Failing after 2m7s
2026-09-09 01:33:39 -05:00