Permission 系统提供了灵活的工具执行权限控制,支持三种审批模式和基于规则的智能决策。aster 1.x 版本引入了与 Claude Agent SDK 对齐的增强权限系统。
aster 新增了 EnhancedInspector,提供以下增强功能:
| 模式 | 说明 | 适用场景 |
|---|---|---|
auto_approve | 自动批准所有操作 | 开发测试、可信环境 |
smart_approve | 根据风险级别智能决策 | 推荐默认模式 |
always_ask | 所有操作都需确认 | 高安全性场景 |
| 级别 | 工具示例 | 说明 |
|---|---|---|
| Low | Read, List, Search | 只读操作,无副作用 |
| Medium | Write, Edit | 文件修改,有限范围 |
| High | Bash, Delete, Http | 系统命令,网络访问 |
graph TB
Agent[Agent] --> Middleware[HITL Middleware]
Middleware --> Inspector[Permission Inspector]
Inspector --> Mode{审批模式}
Mode -->|auto_approve| Auto[自动批准]
Mode -->|smart_approve| Smart[智能审批]
Mode -->|always_ask| Ask[总是询问]
Smart --> Risk[风险评估]
Risk --> Rules[规则匹配]
Rules --> Decision{决策}
Decision -->|Allow| Execute[执行工具]
Decision -->|Deny| Reject[拒绝执行]
Decision -->|Ask| Control[Control Channel]
Control --> User[用户确认]
User --> Execute
style Inspector fill:#3b82f6
style Smart fill:#10b981
style Control fill:#f59e0b
import "github.com/astercloud/aster/pkg/permission"
// 使用智能审批模式(推荐)
inspector, err := permission.NewInspector(
permission.WithMode(permission.ModeSmartApprove),
)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
result, err := inspector.Check(ctx, &permission.Request{
ToolName: "Bash",
Arguments: map[string]any{
"command": "rm -rf /tmp/test",
},
})
if result.NeedsApproval {
// 需要用户确认
fmt.Printf("需要审批: %s (风险: %s)\n", result.ToolName, result.RiskLevel)
} else if result.Decision == permission.DecisionAllow {
// 自动批准
fmt.Println("已批准执行")
} else {
// 拒绝执行
fmt.Printf("拒绝: %s\n", result.Reason)
}
inspector, _ := permission.NewInspector(
permission.WithMode(permission.ModeAutoApprove),
)
// 所有工具都自动批准
result, _ := inspector.Check(ctx, &permission.Request{
ToolName: "Bash",
Arguments: map[string]any{"command": "rm -rf /"},
})
// result.Decision == DecisionAllow
// result.NeedsApproval == false
inspector, _ := permission.NewInspector(
permission.WithMode(permission.ModeSmartApprove),
)
// 只读操作 - 自动批准
result, _ := inspector.Check(ctx, &permission.Request{
ToolName: "Read",
Arguments: map[string]any{"path": "main.go"},
})
// result.NeedsApproval == false
// 写操作 - 需要审批
result, _ = inspector.Check(ctx, &permission.Request{
ToolName: "Write",
Arguments: map[string]any{"path": "main.go", "content": "..."},
})
// result.NeedsApproval == true
// 系统命令 - 需要审批
result, _ = inspector.Check(ctx, &permission.Request{
ToolName: "Bash",
Arguments: map[string]any{"command": "echo hello"},
})
// result.NeedsApproval == true
inspector, _ := permission.NewInspector(
permission.WithMode(permission.ModeAlwaysAsk),
)
// 即使只读操作也需要确认
result, _ := inspector.Check(ctx, &permission.Request{
ToolName: "Read",
Arguments: map[string]any{"path": "main.go"},
})
// result.NeedsApproval == true
// 允许所有读取操作
inspector.AddRule(&permission.Rule{
Pattern: "Read",
Decision: permission.DecisionAllowAlways,
RiskLevel: permission.RiskLevelLow,
Note: "允许所有读取操作",
})
// 禁止危险命令
inspector.AddRule(&permission.Rule{
Pattern: "Bash",
Decision: permission.DecisionDenyAlways,
RiskLevel: permission.RiskLevelHigh,
Conditions: []permission.Condition{
{
Field: "command",
Operator: "contains",
Value: "rm -rf",
},
},
Note: "禁止危险的删除命令",
})
// 允许写入特定目录
inspector.AddRule(&permission.Rule{
Pattern: "Write",
Decision: permission.DecisionAllowAlways,
Conditions: []permission.Condition{
{
Field: "path",
Operator: "prefix",
Value: "/tmp/",
},
},
Note: "允许写入临时目录",
})
| 运算符 | 说明 | 示例 |
|---|---|---|
eq | 相等 | command eq "ls" |
ne | 不等 | path ne "/etc/passwd" |
contains | 包含 | command contains "rm" |
prefix | 前缀 | path prefix "/home/" |
suffix | 后缀 | path suffix ".txt" |
regex | 正则 | command regex "^git\s+" |
// 创建临时规则(1小时后过期)
expiry := time.Now().Add(time.Hour)
inspector.AddRule(&permission.Rule{
Pattern: "Bash",
Decision: permission.DecisionAllowAlways,
ExpiresAt: &expiry,
Note: "临时允许 Bash 命令",
})
// 保存规则到文件
err := inspector.SaveRules()
// 规则保存到: ~/.config/aster/permissions.json
// 创建时自动加载规则
inspector, _ := permission.NewInspector(
permission.WithMode(permission.ModeSmartApprove),
permission.WithAutoLoad(true), // 自动加载已保存的规则
)
// 自定义规则文件路径
inspector, _ := permission.NewInspector(
permission.WithPath("/custom/path/permissions.json"),
)
import (
"github.com/astercloud/aster/pkg/middleware"
"github.com/astercloud/aster/pkg/permission"
)
// 创建 Inspector
inspector, _ := permission.NewInspector(
permission.WithMode(permission.ModeSmartApprove),
)
// 创建 HITL 中间件
hitlMiddleware := middleware.NewHumanInTheLoopMiddleware(&middleware.HumanInTheLoopMiddlewareConfig{
Inspector: inspector,
ApprovalHandler: func(ctx context.Context, req *middleware.ReviewRequest) ([]middleware.Decision, error) {
// 自定义审批逻辑
for _, action := range req.ActionRequests {
fmt.Printf("需要审批: %s\n", action.ToolName)
// 显示 UI 或命令行提示...
}
return []middleware.Decision{{Type: middleware.DecisionApprove}}, nil
},
})
// 注册中间件
middleware.DefaultRegistry.Register("hitl", func(config *middleware.MiddlewareFactoryConfig) (middleware.Middleware, error) {
return hitlMiddleware, nil
})
// 在 Agent 中使用
config := &types.AgentConfig{
Middlewares: []string{"hitl"},
}
// 订阅 Control Channel
controlCh := agent.Subscribe([]types.AgentChannel{types.ChannelControl}, nil)
go func() {
for event := range controlCh {
switch e := event.Event.(type) {
case *types.ControlPermissionRequiredEvent:
// 显示审批对话框
approved := showApprovalDialog(e.ToolName, e.Arguments, e.RiskLevel)
// 发送决定
agent.RespondToPermission(e.RequestID, approved, "用户决定")
}
}
}()
// 获取工具风险级别
risk := inspector.AssessRisk(&permission.Request{
ToolName: "Bash",
Arguments: map[string]any{"command": "curl http://..."},
})
// risk == RiskLevelHigh
| 因素 | 低风险 | 高风险 |
|---|---|---|
| 工具类型 | Read, List | Bash, Delete |
| 目标路径 | 工作目录内 | 系统目录 |
| 命令内容 | 查询类 | 修改/删除类 |
| 网络访问 | 无 | 外部请求 |
// 开发环境
if env == "development" {
inspector, _ = permission.NewInspector(
permission.WithMode(permission.ModeAutoApprove),
)
}
// 生产环境
if env == "production" {
inspector, _ = permission.NewInspector(
permission.WithMode(permission.ModeAlwaysAsk),
)
}
// 添加安全工具白名单
safeTools := []string{"Read", "List", "Search", "Glob"}
for _, tool := range safeTools {
inspector.AddRule(&permission.Rule{
Pattern: tool,
Decision: permission.DecisionAllowAlways,
})
}
// 记录所有审批决定
inspector.OnDecision(func(req *permission.Request, result *permission.Result) {
log.Printf("Permission: tool=%s decision=%s reason=%s",
req.ToolName, result.Decision, result.Reason)
})
EnhancedInspector 是增强版权限检查器,整合了 CanUseTool 回调、沙箱配置和规则管理。
import (
"github.com/astercloud/aster/pkg/permission"
"github.com/astercloud/aster/pkg/types"
)
// 配置沙箱设置
sandboxConfig := &types.SandboxConfig{
Kind: types.SandboxKindLocal,
Settings: &types.SandboxSettings{
Enabled: true,
AutoAllowBashIfSandboxed: true,
ExcludedCommands: []string{"git", "docker"},
AllowUnsandboxedCommands: true,
},
PermissionMode: types.SandboxPermissionDefault,
}
// 自定义权限回调
canUseTool := func(
ctx context.Context,
toolName string,
input map[string]any,
opts *types.CanUseToolOptions,
) (*types.PermissionResult, error) {
// 阻止敏感文件访问
if path, ok := input["path"].(string); ok {
if path == "/etc/passwd" {
return &types.PermissionResult{
Behavior: "deny",
Message: "Access denied",
}, nil
}
}
return nil, nil // 让权限系统决策
}
// 创建增强检查器
inspector := permission.NewEnhancedInspector(&permission.EnhancedInspectorConfig{
Mode: permission.ModeSmartApprove,
SandboxConfig: sandboxConfig,
CanUseTool: canUseTool,
PersistPath: ".aster/permissions.json",
AutoLoad: true,
})
type CanUseToolFunc func(
ctx context.Context,
toolName string,
input map[string]any,
opts *CanUseToolOptions,
) (*PermissionResult, error)
type CanUseToolOptions struct {
Signal context.Context // 取消信号
Suggestions []PermissionUpdate // 建议的权限更新
SandboxEnabled bool // 沙箱是否启用
BypassSandboxRequested bool // 是否请求绕过沙箱
}
type PermissionResult struct {
Behavior string // "allow" | "deny"
UpdatedInput map[string]any // 修改后的输入
UpdatedPermissions []PermissionUpdate // 权限更新
Message string // 拒绝原因
Interrupt bool // 是否中断
}
// 执行权限检查
call := &types.ToolCallSnapshot{
ID: "call-1",
Name: "Bash",
Arguments: map[string]any{"command": "ls -la"},
}
result, err := inspector.Check(ctx, call)
if err != nil {
log.Fatal(err)
}
if result.Allowed {
fmt.Printf("✅ Allowed (decided by: %s)\n", result.DecidedBy)
} else if result.NeedsApproval {
fmt.Printf("⏳ Needs approval (decided by: %s)\n", result.DecidedBy)
} else {
fmt.Printf("🚫 Denied: %s\n", result.Message)
}
type CheckResult struct {
Allowed bool // 是否允许
NeedsApproval bool // 是否需要审批
DecidedBy string // 决策来源
Message string // 消息
Interrupt bool // 是否中断
UpdatedInput map[string]any // 修改后的输入
ApprovalRequest *types.ControlPermissionRequiredEvent
}
| 值 | 说明 |
|---|---|
bypass_mode | 权限模式为 bypass |
plan_mode | 权限模式为 plan |
accept_edits_mode | 权限模式为 acceptEdits |
canUseTool | CanUseTool 回调决策 |
excluded_command | 命令在排除列表 |
sandbox_policy | 沙箱策略拒绝 |
auto_allow_bash | AutoAllowBashIfSandboxed |
rule:xxx | 匹配规则 xxx |
low_risk | 低风险自动批准 |
medium_risk | 中风险需审批 |
high_risk | 高风险需审批 |
// 添加会话级规则(会话结束自动清除)
inspector.addSessionRule(permission.Rule{
Pattern: "Write",
Decision: permission.DecisionAllow,
Note: "Session auto-approved",
})
// 清除会话级规则
inspector.ClearSessionRules()
// 通过 CanUseTool 返回权限更新
return &types.PermissionResult{
Behavior: "allow",
UpdatedPermissions: []types.PermissionUpdate{
{
Type: "addRules",
Behavior: "allow",
Destination: "session", // 或 "project", "user"
Rules: []types.PermissionRule{
{ToolName: "Write", RuleContent: "auto-approved"},
},
},
},
}, nil
// 记录违规
inspector.RecordViolation(types.SandboxViolation{
Type: "file",
Path: "/etc/passwd",
Operation: "read",
Blocked: true,
Timestamp: time.Now().Unix(),
Details: "Attempted to read system file",
})
// 获取违规记录
violations := inspector.GetViolations()
for _, v := range violations {
fmt.Printf("Violation: %s %s (%s)\n", v.Type, v.Path, v.Operation)
}
| 模式 | 常量 | 说明 |
|---|---|---|
| 默认 | SandboxPermissionDefault | 标准权限检查流程 |
| 接受编辑 | SandboxPermissionAcceptEdits | 自动接受文件编辑 |
| 绕过权限 | SandboxPermissionBypass | 绕过所有权限检查 |
| 规划模式 | SandboxPermissionPlan | 只记录不执行 |
sandboxConfig := &types.SandboxConfig{
PermissionMode: types.SandboxPermissionAcceptEdits,
}
模型可以在工具输入中请求绕过沙箱:
// 工具调用参数
{
"command": "sudo apt update",
"dangerouslyDisableSandbox": true
}
处理流程:
AllowUnsandboxedCommands 配置# 运行 Permission 示例
go run ./examples/permission/
# 运行 Sandbox Permission 示例 (Claude Agent SDK 风格)
go run ./examples/sandbox-permission/
# 运行 Human-in-the-Loop 示例
go run ./examples/human-in-the-loop/