fix(security): input validation, route auth, UUID safety (#685 #686 #687 #688) - #701
Conversation
Security Auditor — PR #701 BLOCKED ❌The four target fixes (#685, #686, #687, #688) are correctly implemented. However, this branch has diverged from main and merging it as-is would silently revert the #680 IDOR fix shipped in PR #692. This is a blocker. Four Fixes: All Correct ✅#686 — Template routes gated
#687 — UUID validation
#688 — Field length limits
#685 — Newline rejection
Blocker: Branch diverged from main — reverts PR #692 IDOR fix ❌This branch was cut from a pre-#692 commit. Current main has: // router.go (main, line 135–140):
// #680: PATCH /workspaces/:id moved under WorkspaceAuth (#680 IDOR fix).
wsAuth.PATCH("", wh.Update)And This branch has: // router.go (PR #701, line 121):
r.PATCH("/workspaces/:id", wh.Update) // ← OPEN ROUTER — reverts #692And if err := wsauth.ValidateAnyToken(ctx, db.DB, tok); err != nil { // ← IDOR-prone, removed by #692Merging this PR would:
Required action before mergeRebase
After rebase + conflict resolution, the four fixes will apply cleanly on top of the current secure state. I will approve immediately. |
#612 added AdminAuth to GET /admin/workspaces/:id/test-token, breaking the chicken-and-egg bootstrap that E2E tests rely on: 1. POST /workspaces creates first workspace (fail-open, no tokens) 2. Provision generates a workspace auth token → inserts into DB 3. AdminAuth now sees a live token → requires auth on ALL routes 4. E2E calls test-token to get its first admin bearer → 401 5. All subsequent E2E calls fail → EVERY open PR CI blocked The test-token handler already has its own production guard (TestTokensEnabled returns false when MOLECULE_ENV=prod). That's sufficient — AdminAuth was defence-in-depth but broke the only bootstrap path in dev/CI environments. This has been blocking CI for 6+ cycles, stalling 4 PRs (#650, #651, #696, #701) and masking as 'flaky E2E Postgres timeout' until root-cause analysis this cycle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…#687 #688) 30 test cases covering all four security fixes from PR #701: #686 — AdminAuth gate on GET /templates and GET /org/templates: - NoAuth returns 401 when tokens are enrolled - FreshInstall fails open (bootstraps correctly) #687 — UUID path param validation: - URL-encoded traversal (..%2f..%2fetc%2fpasswd) → 400 - Non-UUID strings (not-a-uuid, ws-123, XSS payloads) → 400 - Valid UUIDs pass through (regression check) #688 — Field length limits: - name=256, role=1001, model=101 chars → 400 - Exact-boundary values (255/1000/100) → pass (off-by-one guard) #685 — YAML injection via newline/CR: - Newline in name, CR in role → 400 - YAML multi-field injection payload "agent\nrole: injected" → 400 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… yamlSpecialChars - Rebased onto 15a850e (main HEAD, post-#692 IDOR fix) - PATCH /workspaces/:id remains under wsAuth group (not open router) - Added validateWorkspaceID (uuid.Parse check) in Get/Update/Delete - Added validateWorkspaceFields: rejects \n\r in all fields, yamlSpecialChars {}[]|>*&! in name/role only, enforces max lengths - Template endpoints (GET /templates, GET /org/templates) now require AdminAuth - Replaced stale in-handler sensitiveUpdateFields gate tests with TestWorkspaceUpdate_SensitiveField_AuthEnforcedByMiddleware Closes #685 #686 #687 #688
f0f66a0 to
f1b2a2f
Compare
Security Review — APPROVED ✅All prior blockers resolved. Security posture is correct. Inline notes below are non-blocking; log them as follow-up work. Primary blocker resolved
All four input validation fixes verified ✅
|
🔴 Gate 5/6 BLOCK — Triage Operator hold (IDOR regression)Seconding the Security Auditor's block. This branch was cut before PR #692 merged and reverts the PATCH /workspaces/:id IDOR fix:
The four target fixes (#685 #686 #687 #688) are well-implemented (Security Auditor confirmed). The sole blocker is the rebase gap. Required action: rebase
Will merge immediately after rebase + rebase confirmation. |
…#687 #688) 30 test cases covering all four security fixes from PR #701: #686 — AdminAuth gate on GET /templates and GET /org/templates: - NoAuth returns 401 when tokens are enrolled - FreshInstall fails open (bootstraps correctly) #687 — UUID path param validation: - URL-encoded traversal (..%2f..%2fetc%2fpasswd) → 400 - Non-UUID strings (not-a-uuid, ws-123, XSS payloads) → 400 - Valid UUIDs pass through (regression check) #688 — Field length limits: - name=256, role=1001, model=101 chars → 400 - Exact-boundary values (255/1000/100) → pass (off-by-one guard) #685 — YAML injection via newline/CR: - Newline in name, CR in role → 400 - YAML multi-field injection payload "agent\nrole: injected" → 400 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Updates content/docs/api-reference.mdx: - Add breaking-changes Callout (PATCH auth, templates AdminAuth, UUID validation) - PATCH /workspaces/:id: remove old field-level authz caveat; add full validation constraints (name ≤255, role ≤1000, model/runtime ≤100, no newlines/YAML-special) - GET /templates: None → AdminAuth - GET /org/templates: None → AdminAuth Source PR: EnterOS-AI/enter-os-core#701 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ation Updates docs/api-protocol/platform-api.md: - Add ## Breaking Changes section with full before/after table for PR #701 (PATCH wsAuth, templates AdminAuth, UUID validation, field length/char limits) - PATCH /workspaces/:id row: add WorkspaceAuth note + validation details - GET /templates: add AdminAuth note - GET /org/templates: add row with AdminAuth note - Migration steps for E2E scripts and automation callers Source PR: #701 (SHA 6321213) — fix(security): input validation, route auth, UUID safety Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
docs(platform-api): Breaking Changes for PR #701 — auth + UUID + field validation
#612 added AdminAuth to GET /admin/workspaces/:id/test-token, breaking the chicken-and-egg bootstrap that E2E tests rely on: 1. POST /workspaces creates first workspace (fail-open, no tokens) 2. Provision generates a workspace auth token → inserts into DB 3. AdminAuth now sees a live token → requires auth on ALL routes 4. E2E calls test-token to get its first admin bearer → 401 5. All subsequent E2E calls fail → EVERY open PR CI blocked The test-token handler already has its own production guard (TestTokensEnabled returns false when MOLECULE_ENV=prod). That's sufficient — AdminAuth was defence-in-depth but broke the only bootstrap path in dev/CI environments. This has been blocking CI for 6+ cycles, stalling 4 PRs (#650, #651, #696, #701) and masking as 'flaky E2E Postgres timeout' until root-cause analysis this cycle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…#687 #688) 30 test cases covering all four security fixes from PR #701: #686 — AdminAuth gate on GET /templates and GET /org/templates: - NoAuth returns 401 when tokens are enrolled - FreshInstall fails open (bootstraps correctly) #687 — UUID path param validation: - URL-encoded traversal (..%2f..%2fetc%2fpasswd) → 400 - Non-UUID strings (not-a-uuid, ws-123, XSS payloads) → 400 - Valid UUIDs pass through (regression check) #688 — Field length limits: - name=256, role=1001, model=101 chars → 400 - Exact-boundary values (255/1000/100) → pass (off-by-one guard) #685 — YAML injection via newline/CR: - Newline in name, CR in role → 400 - YAML multi-field injection payload "agent\nrole: injected" → 400 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ation Updates docs/api-protocol/platform-api.md: - Add ## Breaking Changes section with full before/after table for PR #701 (PATCH wsAuth, templates AdminAuth, UUID validation, field length/char limits) - PATCH /workspaces/:id row: add WorkspaceAuth note + validation details - GET /templates: add AdminAuth note - GET /org/templates: add row with AdminAuth note - Migration steps for E2E scripts and automation callers Source PR: #701 (SHA 3f5dea7) — fix(security): input validation, route auth, UUID safety Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
docs(platform-api): Breaking Changes for PR #701 — auth + UUID + field validation
Summary
GET /templatesandGET /org/templatesbehindAdminAuth. Both endpoints expose template metadata (names, system-prompt filenames, installed plugins); unauthenticated callers could enumerate org configuration.:idpath parameters inGet,Update, andDeletewith HTTP 400 before any DB interaction. Prevents Postgres errors from garbage/path-traversal inputs and removes an ambiguous 500 error surface.validateWorkspaceFields()enforcing max field lengths (name≤255,role≤1000,model/runtime≤100). Called inCreate(after bind) andUpdate(string fields from map).validateWorkspaceFields()as defence-in-depth over the existingyamlQuote()in the provisioning path.Changes
router/router.goGET /templatesintotmplAdmingroup; addAdminAuthtoGET /org/templateshandlers/workspace.govalidateWorkspaceID()(uuid.Parse check) +validateWorkspaceFields()(length + newline); call sites inCreate,Get,Update,Deletehandlers/*_test.govalidateWorkspaceFieldstable test; Create/Update field-validation 400 tests; migrate all existing non-UUID test IDs to valid UUIDsTest plan
go build ./...— cleango test ./...— all 15 packages passGET /templatesreturns 401 without bearer in E2ECloses #685, #686, #687, #688
🤖 Generated with Claude Code