Skip to content

Close the admin takeover through the delete-me link - #2262

Merged
umputun merged 6 commits into
masterfrom
fix-deleteme-token
Oct 11, 2026
Merged

umputun merged 6 commits into
masterfrom
fix-deleteme-token

Conversation

@paskal

@paskal paskal commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Previously, the delete-me approval page rendered values from the server response as HTML, and some of them come from the user who requested deletion, so markup in them could run as script in the admin's session. The delete-me token was also accepted as a session, the site in a session token was not checked against the configured sites, and the admin deletion endpoint processed a token for any site.

After this change:

  • The page renders the response and errors as text.
  • Delete-me tokens are rejected as sessions; the admin deletion endpoint still accepts them, but only for the site the admin's session belongs to. The basic-auth admin may omit the site.
  • With the bolt store, tokens are issued only for the sites given in --site, and sessions are accepted only when the site matches exactly, since the auth package compares case-insensitively at issue. Remote-store deployments keep accepting any audience, as the remote store decides which sites exist.
  • Deletion links include the requesting site, the signed-out message tells the admin where to sign in, and the API documentation lists the site parameter of the admin endpoint.
  • golangci-lint in backend CI moves to v2.14.0, as v2.13.2 cannot read Go 1.27.2 export data.

Regression tests cover the page rendering, delete-me token rejection, the site binding of the deletion endpoint, and audience restriction at issue, parse and login; each fails with its fix reverted. On a build serving only --site=myblog, an admin processed a deletion through the shipped page in a browser.

The admin approval page wrote the server's response and error into
innerHTML, and both can carry values controlled by the user who asked
to be deleted, so markup in them ran as script in the admin's session.

The page now builds the heading and the details as elements and sets
their text, so nothing the server returns is parsed as HTML.
A delete-me token names the user, is signed like a session token and
lives for three months. The comment beside its delete_me attribute said
it could not be used to log in, but nothing checked it, so whoever held
the link could act as that user.

The auth validator now refuses claims carrying delete_me. The admin
deletion handler parses the token itself and keeps accepting it.
The site in a token comes from the login request, and nothing compared
it with the configured sites, so any value got a signed session and was
echoed back in responses and error messages.

With the bolt store the token service now issues and accepts tokens
only for the sites given in --site. A remote store decides on its own
side which sites exist, so with one any audience is still accepted.

The delete-me page fell back to the default site, remark, which an
instance not serving it now refuses. Deletion links carry site_id, the
signed-out message sends the admin to the requesting site's comments,
and the API docs list the site parameter the admin endpoint requires.
@paskal
paskal requested a review from umputun as a code owner October 11, 2026 06:06
Copilot AI balanced review requested due to automatic review settings October 11, 2026 06:06
@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

size-limit report 📦

Path Size
public/embed.mjs 2.05 KB (0%)
public/remark.mjs 53.82 KB (0%)
public/remark.css 7 KB (-0.02% 🔽)
public/last-comments.mjs 17.37 KB (0%)
public/last-comments.css 3.22 KB (0%)
public/deleteme.mjs 2.98 KB (+0.07% 🔺)
public/counter.mjs 731 B (0%)

@codecov

codecov Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.56%. Comparing base (d9254d2) to head (398ac41).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2262      +/-   ##
==========================================
+ Coverage   74.01%   74.56%   +0.54%     
==========================================
  Files         129      129              
  Lines        3745     3751       +6     
  Branches      830      865      +35     
==========================================
+ Hits         2772     2797      +25     
+ Misses        967      948      -19     
  Partials        6        6              
Flag Coverage Δ
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Strict TypeScript compilation fails, and site/audience authorization remains insufficiently bound.

3 open findings
What changed in this PR

Hardens the delete-me workflow against script injection and token misuse.

Changes:

  • Renders deletion responses safely as text.
  • Restricts delete-me tokens and Bolt-store audiences.
  • Includes the requesting site in deletion links and adds regression tests.
File Description
site/​content/​docs/​contributing/​api/​index.md Documents the required site parameter.
frontend/​apps/​remark42/​app/​deleteme.ts Safely renders deletion-page content.
frontend/​apps/​remark42/​app/​deleteme.test.ts Tests injection-resistant rendering.
backend/​app/​rest/​api/​rest_private.go Adds the site to deletion links.
backend/​app/​rest/​api/​rest_private_test.go Updates link expectations.
backend/​app/​cmd/​server.go Rejects deletion tokens as sessions and restricts audiences.
backend/​app/​cmd/​server_test.go Tests token and audience restrictions.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread backend/app/rest/api/rest_private.go
Comment thread frontend/apps/remark42/app/deleteme.ts
Comment thread backend/app/cmd/server.go
CI installs the newest Go 1.27 patch release, now 1.27.2, and
golangci-lint v2.13.2 cannot read its export data, so the lint step
failed with an export data decoding error. v2.14.0, built with Go
1.27.1, reports no issues in backend/app and
backend/_example/memory_store under Go 1.27.2.
Admin rights hold for the site the session belongs to, but the admin
deletion endpoint deleted from whichever site the token named, so an
admin of one site could process a deletion request for another site
served by the same instance. The endpoint now refuses a token whose
site differs from the request's site, which the site check has already
matched against the session. The basic-auth admin, who administers
every site, may still omit it.

The handler test server now takes query sessions from the jwt
parameter, as the server does, leaving token to the delete-me token.
The auth package matches audiences ignoring case, so with the bolt
store and --site=remark a token for REMARK was issued and accepted,
while store operations with it failed, as the store keys sites by their
exact name. With the bolt store, sessions whose audience is not exactly
a configured site are now rejected.

@umputun umputun left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@umputun
umputun merged commit 529621e into master Oct 11, 2026
21 checks passed
@umputun
umputun deleted the fix-deleteme-token branch October 11, 2026 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants