fix: set Secure/HttpOnly/SameSite on session cookies (closes #47) #48
Reference in New Issue
Block a user
Delete Branch "fix/gosec-findings"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
closes #47
Fixes the two remaining
gosecfindings onmain, bothG124(http.Cookie missing or has insecure
Secure,HttpOnly, orSameSiteattribute):internal/session/session.go:84(CreateSession, the loginset-cookie path)
internal/session/session.go:128(ClearSession, the logoutdelete-cookie path)
What changed
Secure: true,HttpOnly: true, andSameSite: http.SameSiteStrictMode.securefield (previously wired to!config.Debug) and thesameSitefield are removed fromsession.Manager, and the deadsecure-toggle parameter is removed from
session.NewManager, whichnow takes only the signing key (reviewer-directed; the mechanical
call-shape updates in
session_test.goleave every assertionuntouched).
TestSessionCookieAttributesAlwaysSecure(failing), asserting thatevery cookie emitted by the session manager carries
HttpOnly,Secure, andSameSiteof Lax or stricter, for both write paths.The second commit makes it pass.
TODO.mdupdated per its Workflow section (Next Step completed,next Future Step promoted, stale "10 open findings" Status text
corrected).
Attribute choices and reasoning
Secure: truealways: theG124analyzer only accepts a constanttruestore, and there is no legitimate configuration in which theauthentication cookie should be sent over plaintext HTTP. The old
behavior disabled
Securewheneverdebugwas on. Local developmentover
http://localhostkeeps working: browsers treatlocalhostasa trustworthy origin and accept
Securecookies there. Anyplain-HTTP flow on a non-localhost host will no longer keep a
session, which is the point of the fix.
SameSite: Strict(unchanged from current production behavior, andstricter than the Lax minimum): the login form is a same-origin POST
to
/followed by a same-site redirect, soStrictbreaks nothing.HttpOnly: true(unchanged).Verification
make check(tests, golangci-lint, fmt-check) is fully green on thebranch head
cb9e14e: all tests pass and the linter reports 0 issues,independently confirmed by the reviewer in a fresh worktree. Commit
history:
ca15f52(failing test) →02ca16a(fix + TODO.md, closes#47) →
cb9e14e(drop the deadNewManagerparameter).Scope-guard enumeration (fresh
make checkonmainatb6e9ac2before any changes, golangci-lint viascript/lint):Total gosec findings: 2 — well under the ~30 stop threshold from issue #47, and matching the 2026-08-07 scope update on that issue. No other linters reported issues; all tests were green.
G124—internal/session/session.go:84— "http.Cookie missing or has insecure Secure, HttpOnly, or SameSite attribute" (CreateSession:SecureandSameSitewere non-constant fields, withSecurewired to!config.Debug)G124—internal/session/session.go:128— same message (ClearSession: identical pattern on the delete-cookie path)Both are genuine findings and are fixed in this PR by hardcoding
Secure: true,HttpOnly: true,SameSite: http.SameSiteStrictMode; no//nolintsuppressions and no linter config changes.make checkis fully green on the branch head (02ca16a).Review: FAIL — one blocking item
Independently verified at head
02ca16ain a fresh worktree:What passes:
make checkfully green (all tests, golangci-lint 0 issues, fmt-check clean). For the record: the first two verification runs failed spuriously because the shared golangci-lint analysis cache replayed results recorded under a since-deleted sibling worktree path;golangci-lint cache cleanresolved it. Nothing in this PR was at fault.CreateSession,ClearSession) now carry constantSecure: true,HttpOnly: true,SameSite: Strict— constants are exactly what theG124analyzer requires, so no//nolintwas needed and none was used.SameSite: Strictpreserves the pre-existing production value; the only behavior change isSecureno longer being disabled in debug mode, which is the point of the fix, andhttp://localhostdevelopment is unaffected since browsers treat localhost as a trustworthy origin.TestSessionCookieAttributesAlwaysSecure), covering both write paths × both constructor arguments, asserting all three attributes with diagnostic logging, and it failed for the right reason before the fix. Existing tests are untouched. The linter config is untouched.TODO.mdupdated correctly per its Workflow section; commit messages are clean, the finishing commit title ends with(closes #47), and there are no forbidden trailers.Blocking:
NewManager(signingKey string, _ bool)keeps a dead, ignored parameter, with a doc comment promising later removal and a deferred P1 entry inTODO.md. That is a half-measure of exactly the kind sneak rejected on PR #46 — pre-1.0 there is no installed base to keep compiling against, so update everything in one pass:NewManager(signingKey string).internal/handlers/handlers.gocall site.NewManagercall sites ininternal/session/session_test.goand the one in the new attributes test — call shape only; no assertion may change. (This call-site edit is reviewer-directed under the one-pass precedent; if sneak prefers the strict reading of the "no modifying existing tests without approval" rule, say so here and the parameter stays with the P1 follow-up instead.)TODO.mdand the "retained only so call sites keep compiling" language from the godoc.constructorBoolArgdimension in the process — collapsing that loop is expected, its assertions stay.make checkgreen, push, and summarize here for re-review.Rework for the blocking item is pushed:
cb9e14e.session.NewManagernow takes only the signing key:NewManager(signingKey string). The ignored_ boolparameter and the "retained only so call sites keep compiling" godoc language are gone.internal/handlers/handlers.gocall site updated, along with its stale comment about the ignored argument.NewManagercall sites ininternal/session/session_test.goupdated — call shape only, per the review's direction; no assertion was changed (one stale// Test with secure=truecomment removed).constructorBoolArgloop insession_cookie_attributes_test.gocollapsed as anticipated in the review; its assertions (HttpOnly, Secure, SameSite Lax-or-stricter, both write paths, with diagnostic logging) are identical.TODO.md(make fmtrun).make checkis fully green atcb9e14e(all tests pass, golangci-lint 0 issues after agolangci-lint cache cleanto flush the stale-worktree-path cache issue noted in the review, fmt-check clean). Cookie behavior is unchanged from02ca16a. Ready for re-review.Re-review: PASS
Verified at head
cb9e14ein a fresh worktree with an isolated lint cache:make checkfully green (all tests, golangci-lint 0 issues, fmt-check clean) — independently run, not taken from the implementor's report.The rework addressed the blocking item exactly as directed:
NewManager(signingKey string)— dead parameter gone, godoc no longer promises a follow-up.internal/handlers/handlers.gocall site and its stale "second argument is ignored" comment fixed.session_test.gocall sites updated to the one-argument shape — I diffed them individually: call shape only, every assertion untouched. The one removed comment (// Test with secure=true) described the deleted argument, so its removal is correct.constructorBoolArgloop as anticipated; its assertions (HttpOnly, Secure, SameSite Lax-or-stricter, bothCreateSessionandClearSessionpaths) are identical, and diagnostic logging is retained.TODO.md.Branch history is clean and tells the right story:
ca15f52(failing test) →02ca16a(fix + TODO,closes #47) →cb9e14e(reviewer-directed parameter removal). No forbidden trailers anywhere. Cookie behavior is unchanged from the reviewed fix: constantSecure/HttpOnly/SameSite=Stricton both write paths, which is what closes the twoG124findings and makesmake checkgreen onmainonce merged.Marking
merge-readyand assigning sneak for merge into protectedmain. Merging this closes #47.