#1: server-side regex highlighter (highlight.go) for go/python/js/json/bash/sql; token span classes styled in app.css; per-line so gutter stays aligned. #2: in-memory token-bucket rate limiter (ratelimit.go) on POST /api/pastes, /api/guess-language and unlock POST; 429 + Retry-After + X-RateLimit headers. #26: new-page JS POSTs the password to /{id} with ?next= after creation; the unlock handler honors same-origin ?next= redirect so the creator lands on the unlocked paste. POST /{id} route added. Tests: ratelimit_test.go (burst/429, refill, unlock limit, highlight, auto- unlock e2e); existing tests updated for per-test limiter isolation.
This commit is contained in:
@@ -107,7 +107,7 @@ func (a *apiServer) renderPaste(w http.ResponseWriter, row *PasteRow, justCreate
|
||||
"ID": row.ID,
|
||||
"Title": row.Title.String,
|
||||
"Language": row.Language.String,
|
||||
"ContentHTML": template.HTMLEscapeString(row.Content),
|
||||
"ContentHTML": template.HTML(highlightCode(row.Content, row.Language.String)), // safe: highlightCode escapes all non-span text
|
||||
"ContentAttr": row.Content,
|
||||
"Gutter": strings.TrimSuffix(gutter, "\n"),
|
||||
"LineCount": lines,
|
||||
@@ -143,6 +143,10 @@ func (a *apiServer) handlePasteView(w http.ResponseWriter, r *http.Request) {
|
||||
if row.PasswordHash.Valid {
|
||||
// if a password was submitted via unlock form, verify and set cookie for this paste
|
||||
if r.Method == http.MethodPost {
|
||||
if !rateLimitUnlock(row.ID, r) {
|
||||
writeRateLimited(w, 60)
|
||||
return
|
||||
}
|
||||
r.ParseForm()
|
||||
pw := r.FormValue("password")
|
||||
if pw != "" && checkPassword(row.PasswordHash.String, pw) {
|
||||
@@ -150,7 +154,14 @@ func (a *apiServer) handlePasteView(w http.ResponseWriter, r *http.Request) {
|
||||
Name: "pw_" + row.ID, Value: "1", Path: "/",
|
||||
MaxAge: 3600, HttpOnly: true, SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
// re-render without lock
|
||||
// re-render without lock, or redirect if ?next= was given (#26)
|
||||
if next := r.FormValue("next"); next != "" {
|
||||
// only allow same-origin relative paths
|
||||
if len(next) > 0 && next[0] == '/' && !strings.HasPrefix(next, "//") {
|
||||
http.Redirect(w, r, next, http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
}
|
||||
a.renderPaste(w, row, false, "")
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user