Creating a paste with burn_after_reads (verified with 1, 2, 3 and even 999999999) does not burn. Content is served on every read: /raw/{id}, /{id} HTML, and /api/pastes/{id} all return the full content on unlimited repeated reads with distinct sessions (fresh vwr cookies).
Verified repro:
create {"content":"BURN-EXPIRY-CONFIRM","burn_after_reads":1}
GET /raw/<id> -> full content (3+ times, distinct cookies)
GET /api/pastes/<id> -> "reads_remaining": null (never populated)
20 concurrent first-reads all returned the content (not just a race — the counter never decrements). view_count increments normally; no burn state is tracked. Round-1's "burn-after-read race" fix is moot because the feature never fires at all.
Also: 20 parallel readers + repeated sequential readers all get content — the guarantee "one read" is completely broken for any security-sensitive sharing.
**Severity:** High
**Target:** palette-dev v0.2.1
Creating a paste with `burn_after_reads` (verified with 1, 2, 3 and even 999999999) does not burn. Content is served on every read: `/raw/{id}`, `/{id}` HTML, and `/api/pastes/{id}` all return the full content on unlimited repeated reads with distinct sessions (fresh `vwr` cookies).
Verified repro:
```
create {"content":"BURN-EXPIRY-CONFIRM","burn_after_reads":1}
GET /raw/<id> -> full content (3+ times, distinct cookies)
GET /api/pastes/<id> -> "reads_remaining": null (never populated)
```
20 concurrent first-reads all returned the content (not just a race — the counter never decrements). `view_count` increments normally; no burn state is tracked. Round-1's "burn-after-read race" fix is moot because the feature never fires at all.
Also: 20 parallel readers + repeated sequential readers all get content — the guarantee "one read" is completely broken for any security-sensitive sharing.
Not a bug as reported. burn_after_reads (plural) is the budget for burn-after-N and only takes effect when burn_after_read: true is also set (store.CreatePaste: if p.BurnAfterRead { ... readsLimit }). Sending only burn_after_reads without the flag is a no-op by design.
Live-verified against v0.2.1 dev: create with {"burn_after_read":true} -> read1=200, read2=404. 20 concurrent readers claim verified by PR #77 regression tests (exactly one admitted).
Legit UX takeaway: the API silently ignoring burn_after_reads without the flag is confusing. Reclosing as works-as-intended but recommending the API reject or warn when burn_after_reads is set without burn_after_read.
Not a bug as reported. `burn_after_reads` (plural) is the budget for burn-after-N and only takes effect when `burn_after_read: true` is also set (store.CreatePaste: `if p.BurnAfterRead { ... readsLimit }`). Sending only `burn_after_reads` without the flag is a no-op by design.
Live-verified against v0.2.1 dev: create with `{"burn_after_read":true}` -> read1=200, read2=404. 20 concurrent readers claim verified by PR #77 regression tests (exactly one admitted).
Legit UX takeaway: the API silently ignoring `burn_after_reads` without the flag is confusing. Reclosing as works-as-intended but recommending the API reject or warn when `burn_after_reads` is set without `burn_after_read`.
Reopening: still reproducible on dev server. POST with only burn_after_reads:1 (no burn_after_read:true) creates a normal immortal paste - reads_limit stays NULL because store.go only resolves the budget inside 'if p.BurnAfterRead'. Documented field is silently ignored.
Reopening: still reproducible on dev server. POST with only burn_after_reads:1 (no burn_after_read:true) creates a normal immortal paste - reads_limit stays NULL because store.go only resolves the budget inside 'if p.BurnAfterRead'. Documented field is silently ignored.
poslop
added spent time 1 minute2026-09-09 23:26:53 +00:00
Diff review: minimal fix in store.CreatePaste — burn_after_reads > 0 alone now sets readsLimit and implies burn_after_read. Logic verified for all three cases (burn_after_reads alone, burn_after_read:true + reads:N, neither).
go vet clean; full go test ./... green, incl. new TestBurnReadsImpliedByBurnAfterReads.
Merged to dev; CI test run on dev: success.
Deployed to palette-dev (rollout complete). Live verify at https://palette-dev.archfox.org: POST {content, burn_after_reads:1} -> read1=200, read2=404, read3=404. Burn enforced.
Note: first live check hit a pod still running the pre-merge image; after rollout restart picked up the CI dev image it behaves correctly.
Closing issue and deleting fix-82-burn-implies-budget.
QA passed (PR #107).
- Diff review: minimal fix in store.CreatePaste — burn_after_reads > 0 alone now sets readsLimit and implies burn_after_read. Logic verified for all three cases (burn_after_reads alone, burn_after_read:true + reads:N, neither).
- go vet clean; full go test ./... green, incl. new TestBurnReadsImpliedByBurnAfterReads.
- Merged to dev; CI test run on dev: success.
- Deployed to palette-dev (rollout complete). Live verify at https://palette-dev.archfox.org: POST {content, burn_after_reads:1} -> read1=200, read2=404, read3=404. Burn enforced.
Note: first live check hit a pod still running the pre-merge image; after rollout restart picked up the CI dev image it behaves correctly.
Closing issue and deleting fix-82-burn-implies-budget.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Severity: High
Target: palette-dev v0.2.1
Creating a paste with
burn_after_reads(verified with 1, 2, 3 and even 999999999) does not burn. Content is served on every read:/raw/{id},/{id}HTML, and/api/pastes/{id}all return the full content on unlimited repeated reads with distinct sessions (freshvwrcookies).Verified repro:
20 concurrent first-reads all returned the content (not just a race — the counter never decrements).
view_countincrements normally; no burn state is tracked. Round-1's "burn-after-read race" fix is moot because the feature never fires at all.Also: 20 parallel readers + repeated sequential readers all get content — the guarantee "one read" is completely broken for any security-sensitive sharing.
Not a bug as reported.
burn_after_reads(plural) is the budget for burn-after-N and only takes effect whenburn_after_read: trueis also set (store.CreatePaste:if p.BurnAfterRead { ... readsLimit }). Sending onlyburn_after_readswithout the flag is a no-op by design.Live-verified against v0.2.1 dev: create with
{"burn_after_read":true}-> read1=200, read2=404. 20 concurrent readers claim verified by PR #77 regression tests (exactly one admitted).Legit UX takeaway: the API silently ignoring
burn_after_readswithout the flag is confusing. Reclosing as works-as-intended but recommending the API reject or warn whenburn_after_readsis set withoutburn_after_read.Reopening: still reproducible on dev server. POST with only burn_after_reads:1 (no burn_after_read:true) creates a normal immortal paste - reads_limit stays NULL because store.go only resolves the budget inside 'if p.BurnAfterRead'. Documented field is silently ignored.
QA passed (PR #107).
Note: first live check hit a pod still running the pre-merge image; after rollout restart picked up the CI dev image it behaves correctly.
Closing issue and deleting fix-82-burn-implies-budget.