security/fix: phase7 hardening — auth, authz, error kinds, file safety, frontend & quality#30
Merged
screenleon merged 6 commits intomainfrom Apr 28, 2026
Merged
Conversation
…rity headers - writeError now uses json.Marshal to escape the message string, preventing JSON injection via crafted error text - Session cookie gains Secure flag (detected from TLS/X-Forwarded-Proto) and SameSite=Strict to block CSRF - Global securityHeaders middleware adds X-Content-Type-Options, X-Frame-Options, and Referrer-Policy to every response Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…po path validation - Added IsUserMember and ListForUser to ProjectStore for membership-based queries - projectAllowedForUser helper (admin bypass + fail-closed membership check) now gates every project and task Get/Update/Delete handler - Non-admin project list is scoped to the caller's memberships via ListForUser - validateRepoPath rejects empty, relative, and root-level repo paths on update - Task handlers fetch existing task before authorising, preventing info-leak via 200 vs 403 side-channel on task ID enumeration Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… + constant usage
- FailDispatchTask now accepts errorKind as a 4th parameter and stores it
in the JSON error blob; defaults to ErrorKindUnknown when empty
- connector_dispatch.go passes errorKind separately instead of embedding it
in the message string
- service.go corrects two wrong literals:
adapter_timeout → ErrorKindCliNotFound (context-cancel on CLI spawn)
"unknown" → models.ErrorKindUnknown (constant instead of literal)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ge component - useConnectorActivity: initialise lastUpdateRef to Date.now() instead of 0 so the first poll result is never classified as stale - ConnectorActivityBadge (planning/): compute phaseLabel via PHASE_LABELS[phase] ?? phase so unrecognised future phases display gracefully across all three variants - Delete frontend/src/components/ConnectorActivityBadge.tsx — dead code, nothing imports it; planning/ version is canonical Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…onst dedup, doc fix - activity_reporter: shutdown flush now uses a 5s bounded context instead of context.Background() to prevent goroutine leak on graceful stop - connector/service: reportPhase nil-guard helper replaces scattered inline checks; classifyRunError and classifyDispatchRunError merged into a single classifyError(msg, forDispatch) function - connector_activity handler: remove duplicate connectorOnlineWindow constant; use store.LocalConnectorLivenessWindow instead - DECISIONS.md: correct two references claiming SSE hub channels are unbuffered — they are buffered at capacity 8 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
projectAllowedForUser returned false for nil user, breaking tests that run without auth middleware. In production RequireAuth blocks nil users before handlers; nil only reaches handlers in test/local-no-auth mode, where the restriction is meaningless. Treat nil as allowed — same as the pre-authz behaviour. Add .githooks/pre-push: runs `go test ./...` against SQLite before every push. Configure with `git config core.hooksPath .githooks`. Skippable with `--no-verify` for emergencies. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Multi-batch hardening pass driven by critic + risk + security + architecture review.
Batch A — Auth security (
middleware/auth.go,handlers/auth.go,router/router.go)writeErrornow usesjson.Marshalto escape the message — fixes JSON injection via crafted error textSecureflag (detected from TLS /X-Forwarded-Proto) andSameSite=StrictsecurityHeadersmiddleware addsX-Content-Type-Options,X-Frame-Options,Referrer-PolicyBatch B — Authorization gaps (
handlers/authz.go,handlers/projects.go,handlers/tasks.go,store/project_store.go)IsUserMember+ListForUseradded toProjectStoreprojectAllowedForUserhelper (admin bypass + fail-closed membership check) gates all project/task handlersvalidateRepoPathrejects empty, relative, and root-level paths on updateBatch C+D — Dispatch error kinds + file write safety (
store/task_store.go,handlers/connector_dispatch.go,connector/service.go)FailDispatchTaskacceptserrorKindas a 4th parameter; stores it in the JSON blobadapter_timeout→ErrorKindCliNotFound,"unknown"→models.ErrorKindUnknownapplyExecutionResultFileshardened:EvalSymlinkson repo root,pathContainedInRepotraversal check, 50-file cap, 1 MiB per-file capclassifyRunError/classifyDispatchRunErrormerged into singleclassifyError(msg, forDispatch)Batch E — Frontend bugs (
hooks/useConnectorActivity.ts,planning/ConnectorActivityBadge.tsx)lastUpdateRefinitialised toDate.now()— fixes stale-flash on first pollPHASE_LABELS[phase] ?? phasefor forward-compatibilityfrontend/src/components/ConnectorActivityBadge.tsx(planning/ version is canonical)Batch F — Backend quality (
connector/activity_reporter.go,handlers/connector_activity.go,DECISIONS.md)context.Background()connectorOnlineWindowconstant removed; handler usesstore.LocalConnectorLivenessWindowreportPhasenil-safe helper added toServiceTest plan
go build ./...passestsc --noEmitpassesSameSite=Strict; HttpOnly(inspect DevTools → Application → Cookies)/api/projects/{id}or/api/tasks/{id}belonging to another project (expect 403)error_kindJSON field🤖 Generated with Claude Code