feat(security): 代审口径 A 落地(仅子代理发起的敏感调用做实质判定 + fail-closed + 理由进审计与小字)
This commit is contained in:
1 parent
fa850f9599
commit
20160c66d8
13 files changed
+1479
-83
No files matched your search
@@ -0,0 +1,56 @@
|
||||
package orchestration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/loop"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
|
||||
)
|
||||
|
||||
// dispatchTaskRunner 记录每个子代理运行的 context(含其中挂的派发任务标记)。
|
||||
type dispatchTaskRunner struct {
|
||||
mu sync.Mutex
|
||||
ctx []context.Context
|
||||
}
|
||||
|
||||
func (r *dispatchTaskRunner) Run(ctx context.Context, _ string, _ []model.Message) (*loop.Result, error) {
|
||||
r.mu.Lock()
|
||||
r.ctx = append(r.ctx, ctx)
|
||||
r.mu.Unlock()
|
||||
return &loop.Result{Text: "ok", Steps: 1, StopReason: "stop"}, nil
|
||||
}
|
||||
|
||||
func (r *dispatchTaskRunner) last() context.Context {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
return r.ctx[len(r.ctx)-1]
|
||||
}
|
||||
|
||||
// TestSubagentTaskRidesRunContext 锁定口径 A 的结构化来源:编排层在驱动子代理 Run
|
||||
// 之前把**派给它的任务目标**挂到运行 context 上,代审判定器据此识别「这次调用由
|
||||
// 子代理发起」并拿到判越界所需的任务。缺失这条挂载 ⇒ 子代理的敏感调用会静默绕过
|
||||
// 实质判定(本用例就是那个缺口的第一道闸)。
|
||||
func TestSubagentTaskRidesRunContext(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
s := newScheduler(t, Options{MaxParallel: 1})
|
||||
m := &dispatchTaskRunner{}
|
||||
s.runner = m
|
||||
|
||||
sub, err := s.Spawn(context.Background(), AgentSpec{Goal: "整理 data/ 下的导出文件"})
|
||||
if err != nil {
|
||||
t.Fatalf("Spawn: %v", err)
|
||||
}
|
||||
if _, werr := s.Wait(context.Background(), []string{sub.ID}); werr != nil {
|
||||
t.Fatalf("Wait: %v", werr)
|
||||
}
|
||||
got, ok := loop.SubagentTaskFrom(m.last())
|
||||
if !ok {
|
||||
t.Fatal("子代理的运行 context 上没有派发任务标记(代审的越界判定将无从触发)")
|
||||
}
|
||||
if got != "整理 data/ 下的导出文件" {
|
||||
t.Fatalf("派发任务 = %q, want 派给该子代理的任务目标", got)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user