Files
weichen 6a3a0dc201 fix(security): harden bypass authz, gate blind spot, command exec, validated_only (review P0/P1)
- FIND-SEC-01: GetUserContext requires session ownership (handler 403 +
  SQL/memory owner filter); in-memory HTTP + isolated-PG regressions
- FIND-ARCH-01: archtest fails closed on untracked table writes (+noise
  exemption) and registers 50 existing writer tables in table_owners
- FIND-SEC-02: Authorizer without pool denies by default; explicit
  NewInMemoryAuthorizer seam opt-in via ALLOW_IN_MEMORY at composition
- FIND-SEC-03: swboot commands run argv-exec (no sh -c) with exact
  basename allowlist match; prefix-spoof and no-shell-eval tests
- FIND-FLOW-01: confirm rejects validated_only plans; ExecuteClaimed
  hard-refuses validated_only steps (new error codes + HTTP mapping)
- FIND-QUAL-01: intentflow.ValidateFlagConsistency fail-fast at Assemble
2026-09-26 02:00:14 +00:00

101 lines
3.2 KiB
Go

package server
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"chat-agent-system/internal/bizdev"
"chat-agent-system/internal/config"
"chat-agent-system/internal/projectworkflow"
)
func TestHandleListProjects_AdminCanSeeProjectsOwnedByOtherUsers(t *testing.T) {
cfg := &config.Config{DataDir: t.TempDir(), AgentBackend: "stub", AllowInMemory: true, Env: "test"}
svc := bizdev.MustNewService(t, cfg)
srv := NewServer(cfg, svc)
ctx := t.Context()
creator, err := srv.AuthService().GetUserByID(ctx, 2)
if err != nil {
t.Fatalf("load creator: %v", err)
}
created, err := srv.pwSvc().CreateProject(ctx, creator, projectworkflow.CreateProjectInput{
Name: "非管理员创建的项目",
})
if err != nil {
t.Fatalf("create project: %v", err)
}
admin, err := srv.AuthService().GetUserByID(ctx, 1)
if err != nil {
t.Fatalf("load admin: %v", err)
}
req := httptest.NewRequest(http.MethodGet, "/api/v1/projects", nil)
req = req.WithContext(withPrincipal(req.Context(), admin))
rec := httptest.NewRecorder()
srv.handleListProjects(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", rec.Code, rec.Body.String())
}
var projects []*projectworkflow.Project
if err := json.NewDecoder(rec.Body).Decode(&projects); err != nil {
t.Fatalf("decode projects: %v", err)
}
for _, project := range projects {
if project.ID == created.ID {
return
}
}
t.Fatalf("admin response should include project %d", created.ID)
}
func TestHandleRunProjectWorkflow_DecodesBodyWithoutContentLength(t *testing.T) {
cfg := &config.Config{
DataDir: t.TempDir(),
AgentBackend: "stub",
// FIND-SEC-02: 业务行为用例走显式内存授权(无库模式项目访问需显式声明)。
AllowInMemory: true,
}
svc := bizdev.MustNewService(t, cfg)
srv := NewServer(cfg, svc)
ctx := t.Context()
srDocID := "doc_proj1001_system_reqs"
_, err := bizdev.Snapshot(svc).Gov.CreateOrGetDocument(
ctx, srDocID, 1001, "system_reqs", "系统需求规格书", "V0.5", "",
2, "张工", 3, "李主管", 4, "王总监",
)
if err != nil {
t.Fatalf("CreateOrGetDocument: %v", err)
}
_ = bizdev.Snapshot(svc).Gov.MarkDocumentContentUpdated(ctx, srDocID, "hash_v1", false)
_, _ = bizdev.Snapshot(svc).Gov.SubmitForReview(ctx, srDocID, 2, "author", "张工")
_, _ = bizdev.Snapshot(svc).Gov.ReviewApprove(ctx, srDocID, 3, "reviewer", "李主管", "同意")
_, err = bizdev.Snapshot(svc).Gov.ApproverApprove(ctx, srDocID, 4, "approver", "王总监", "批准")
if err != nil {
t.Fatalf("ApproverApprove: %v", err)
}
body, _ := json.Marshal(map[string]any{
"task_key": "test_manager",
"project_name": "智能肺功能仪",
"backend": "stub",
})
user, err := srv.AuthService().GetUserByID(ctx, 2)
if err != nil {
t.Fatalf("GetUserByUsername: %v", err)
}
req := httptest.NewRequest(http.MethodPost, "/api/v1/projects/1001/workflows/test_manager/run", bytes.NewReader(body))
req = req.WithContext(withPrincipal(req.Context(), user))
req.SetPathValue("id", "1001")
req.SetPathValue("workflow_key", "test_manager")
rec := httptest.NewRecorder()
srv.handleRunProjectWorkflow(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", rec.Code, rec.Body.String())
}
}