diff --git a/.doc/audit/2026-09-20-bug-hunt-permissions-uploads.md b/.doc/audit/2026-09-20-bug-hunt-permissions-uploads.md new file mode 100644 index 00000000..7bb2c87c --- /dev/null +++ b/.doc/audit/2026-09-20-bug-hunt-permissions-uploads.md @@ -0,0 +1,48 @@ +--- +title: "系统权限设置与项目协作多项缺陷排查" +status: done +created: 2026-09-20 +updated: 2026-09-20 +severity: high +--- + +# 系统权限设置与项目协作多项缺陷排查 + +## 现象 + +用户报告:成员管理勾选无效、系统设置「允许编制人兼任审核批准」400、PDF 预览被拦截、任务 runs 403 刷屏、发起编制被标 NETWORK_ERROR、注册/建号 rollback、改密后刷新再强制改密、退出重登出现同名重复项目、无项目仍可对话、右侧工具栏默认展开且不记忆。 + +## 根因分析 + +| # | 根因 | 关键路径 | +|---|------|----------| +| 1 | CSP 缺 `frame-src`,blob iframe fallback 到 `default-src 'self'` | `internal/server/cors.go` | +| 2 | PdfViewer 同源 iframe 直连,触发 `X-Frame-Options: DENY` 原生拦截页 | `web/src/components/doc/preview/renderers/PdfViewer.tsx` | +| 3 | 前端未传 `expected_revision`(该 key 后端强制 CAS) | `web/src/api/systemAdmin.ts` / SettingsSection | +| 4 | `ProjectSettingsApiError` 字段名与消费方 `httpStatus`/`errorCode` 不一致 | `web/src/api/projectSettingsError.ts` | +| 5 | task-run view 仅放行创建者/owner;前端 403 无停止轮询 | `task_run_auth.go` / `App.tsx` | +| 6 | `CanManageProjectMembers` 未读细粒度权限位 | `internal/authz/service.go` | +| 7 | memStore `ListProjectsForUser` 忽略成员表 | `internal/projectworkflow/store.go` | +| 8 | `user_role_bindings` 插入吞错 → commit rollback;handler 一律 400 | `internal/auth/pg_store.go` | +| 9 | 嵌套 `user.mustChangePassword` OR 合并复活 + fetchMe 未回写 session | `authUserMap.ts` / WorkbenchView / useAuthenticatedWorkspaceSync | +| 10 | `projects.name` 无唯一约束 + Create 无幂等键;logout 不清 projects 缓存 | `service.go` / migration 00056 / App logout | +| 11 | `Project()<=0` 时跳过项目访问校验 | `handlers.go` / `sse.go` / `stage_access_helpers.go` | +| 12 | `rightCollapsed` 默认 false 且无持久化 | `AppShell.tsx` / `storage.ts` | + +## 严重度 + +`high` + +## 关联整改 + +- Short-term: `.doc/ops/short-term/2026-09-20-bug-hunt-permissions-uploads.md` +- 分支: `fix/bug-hunt-2026-09-20-permissions-and-uploads` + +## 证据 + +- 产品决策:新增 `project:manage_members`;无项目禁止对话并置灰发送按钮;同创建人内项目名唯一 + `client_key` 幂等。 +- 迁移:`00055_...project-manage-members-permission.sql`、`00056_...project-create-idempotency.sql` + +## 开放问题 + +- 无(本批范围内) diff --git a/.doc/audit/authorization-matrix-v3-full.md b/.doc/audit/authorization-matrix-v3-full.md index 646c6097..539df729 100644 --- a/.doc/audit/authorization-matrix-v3-full.md +++ b/.doc/audit/authorization-matrix-v3-full.md @@ -43,7 +43,7 @@ AuthN column is **heuristic**; negative-test pointers filled where known. | POST | `/api/v1/auth/register` | `s.handleAuthRegister` | `server.go` | handler | see suite | | GET | `/api/v1/auth/sessions` | `s.handleAuthSessions` | `server.go` | handler | see suite | | DELETE | `/api/v1/auth/sessions/{id}` | `s.handleAuthSessionByID` | `server.go` | handler | see suite | -| POST | `/api/v1/chat` | `s.handleChat` | `server.go` | session+project | see suite | +| POST | `/api/v1/chat` | `s.handleChat` | `server.go` | session+project (ERR_NO_PROJECT if unbound + zero memberships) | see suite | | GET | `/api/v1/chat/categories` | `s.handleListChatCategories` | `server.go` | session+project | see suite | | GET | `/api/v1/chat/dates` | `s.handleListChatDates` | `server.go` | session+project | see suite | | GET | `/api/v1/chat/digests` | `s.handleListDailyDigests` | `server.go` | session+project | see suite | @@ -60,8 +60,8 @@ AuthN column is **heuristic**; negative-test pointers filled where known. | GET | `/api/v1/chat/search` | `s.handleChatSearch` | `server.go` | session+project | see suite | | GET | `/api/v1/chat/sessions` | `s.handleListChatSessions` | `server.go` | session+project | see suite | | POST | `/api/v1/chat/sessions` | `s.handleUpsertChatSession` | `server.go` | session+project | see suite | -| GET | `/api/v1/chat/stream` | `s.handleChatStream` | `server.go` | session+project | see suite | -| POST | `/api/v1/chat/stream` | `s.handleChatStream` | `server.go` | session+project | see suite | +| GET | `/api/v1/chat/stream` | `s.handleChatStream` | `server.go` | session+project (ERR_NO_PROJECT if unbound + zero memberships) | see suite | +| POST | `/api/v1/chat/stream` | `s.handleChatStream` | `server.go` | session+project (ERR_NO_PROJECT if unbound + zero memberships) | see suite | | GET | `/api/v1/chat/turns/{client_turn_id}` | `s.handleGetChatTurn` | `server.go` | session+project | see suite | | GET | `/api/v1/chat/unread` | `s.handleChatUnread` | `server.go` | session+project | see suite | | GET | `/api/v1/context/cases/recommend` | `s.handleRecommendCases` | `server.go` | handler | see suite | @@ -131,10 +131,10 @@ AuthN column is **heuristic**; negative-test pointers filled where known. | GET | `/api/v1/tasks/run/stream` | `s.handleRunTaskStream` | `server.go` | session+project | see suite | | POST | `/api/v1/tasks/run/stream` | `s.handleRunTaskStream` | `server.go` | session+project | see suite | | POST | `/api/v1/tasks/runs` | `s.handleCreateTaskRun` | `server.go` | session+project | see suite | -| GET | `/api/v1/tasks/runs/{run_id}` | `s.handleGetTaskRun` | `server.go` | session+project | see suite | -| POST | `/api/v1/tasks/runs/{run_id}/cancel` | `s.handleCancelTaskRun` | `server.go` | session+project | see suite | -| GET | `/api/v1/tasks/runs/{run_id}/events` | `s.handleTaskRunEvents` | `server.go` | session+project | see suite | -| POST | `/api/v1/tasks/runs/{run_id}/resume` | `s.handleResumeTaskRun` | `server.go` | session+project | see suite | +| GET | `/api/v1/tasks/runs/{run_id}` | `s.handleGetTaskRun` | `server.go` | session+project member (view) | see suite | +| POST | `/api/v1/tasks/runs/{run_id}/cancel` | `s.handleCancelTaskRun` | `server.go` | session+run owner or project owner | see suite | +| GET | `/api/v1/tasks/runs/{run_id}/events` | `s.handleTaskRunEvents` | `server.go` | session+project member (view) | see suite | +| POST | `/api/v1/tasks/runs/{run_id}/resume` | `s.handleResumeTaskRun` | `server.go` | session+run owner or project owner | see suite | | GET | `/api/v1/workflow-instances/{id}` | `s.handleGetWorkflowInstance` | `server.go` | session+project | see suite | | POST | `/api/v1/workflow-instances/{id}/stages/{stage}/advance` | `s.handleAdvanceStage` | `server.go` | session+project | see suite | | GET | `/api/v1/workflow-instances/{id}/stages/{stage}/readiness` | `s.handleGetStageReadiness` | `server.go` | session+project | see suite | diff --git a/.doc/ops/CURRENT.md b/.doc/ops/CURRENT.md index 92cdb2e2..ed82f4b3 100644 --- a/.doc/ops/CURRENT.md +++ b/.doc/ops/CURRENT.md @@ -15,6 +15,7 @@ | owner | task | branch | worktree | mutex | status | |-------|------|--------|----------|-------|--------| +| Cursor-Auto | 2026-09-20-bug-hunt-permissions-uploads | fix/bug-hunt-2026-09-20-permissions-and-uploads | - | authz+web+projectworkflow | in_progress | | Cursor | audit-b1-data-consistency | fix/audit-b1-data-consistency | - | docgov+workflow+chat | done | | Cursor | audit-b2-authz-hardening | fix/audit-b2-authz-hardening | - | authz | done | | Cursor | audit-b3-sse-events | fix/audit-b3-sse-events | - | chat+events | done | diff --git a/.doc/ops/short-term/2026-09-20-bug-hunt-permissions-uploads.md b/.doc/ops/short-term/2026-09-20-bug-hunt-permissions-uploads.md new file mode 100644 index 00000000..2c7e8aa4 --- /dev/null +++ b/.doc/ops/short-term/2026-09-20-bug-hunt-permissions-uploads.md @@ -0,0 +1,58 @@ +--- +title: "修复系统权限设置与项目协作多项缺陷" +status: in_progress +created: 2026-09-20 +updated: 2026-09-20 +owner: Cursor-Auto +branch: fix/bug-hunt-2026-09-20-permissions-and-uploads +worktree: "-" +mutex: authz+web+projectworkflow +--- + +# 修复系统权限设置与项目协作多项缺陷 + +## 目标 + +修复两轮排查确认的 12 处后端/前端缺陷(CSP/PDF、系统设置 CAS、任务错误误分类、task-run 403、成员管理 RBAC、注册吞错、强制改密复发、项目重名、无项目可对话、右侧栏不记忆等)。 + +## 范围 + +- CSP `frame-src` + PDF blob 预览 +- 系统设置 `expected_revision` 全链路 +- `ProjectSettingsApiError` httpStatus/errorCode 别名 +- task-run 查看放宽为项目成员;前端 403/404 停轮询 +- `project:manage_members` 权限位(迁移 00055) +- memStore 成员可见性 +- CreateUser 吞错 / 400·500 分类 / LooksInternal +- 强制改密 session 嵌套字段 OR 复活 +- CreateProject `client_key` 幂等 + `(creator, name)` 唯一(迁移 00056) +- 无项目禁止对话 + 发送按钮置灰 +- 右侧工具栏默认收起并持久化 + +## 非目标 + +- 全局项目名唯一(仅同创建人内唯一) +- 放宽 `frame-ancestors` / `X-Frame-Options` + +## 步骤 + +1. [x] 后端 CSP / authz / auth / projectworkflow / chat 入口 +2. [x] 前端 PDF / settings / 错误别名 / 轮询 / RBAC UI / 改密 / 建项幂等 / InputArea / AppShell +3. [x] 迁移 00055 / 00056 + 单测 +4. [ ] `go vet` / `go test -short` / `npm test` / `npm run build` +5. [ ] 占集成槽位合回 `dev` + +## 验收标准 + +- [x] 代码与契约补齐(见 audit 笔记) +- [ ] 短测与前端构建通过 +- [ ] 合入 `dev` + +## 相关链接 + +- Audit: `.doc/audit/2026-09-20-bug-hunt-permissions-uploads.md` +- Plan: `.cursor/plans/修复系统权限设置与项目协作多项缺陷_ff3242f7.plan.md` + +## 证据 / 备注 + +分支:`fix/bug-hunt-2026-09-20-permissions-and-uploads`