Documentation
¶
Overview ¶
Package permgate 工具调用许可判定链:谁在什么模式下对什么输入得到 allow/ask/deny。对标 ZCode permission/service.ts 的优先级状态机——顺序是 踩坑固化的,别调换:
requiresUserInteraction → ask(交互型工具天然要用户) alwaysAsk 硬阻断段 → disallowed/project-deny 仍压过「问」(被硬禁的 工具不能退化成「弹窗一点就能跑」) alwaysAsk 会话授权段 → 命中即放行(钥匙是本会话的授权,不看模式) alwaysAsk 只读拦截段 → 只读档的非只读工具直接拒(不给「问」的机会) disallowed → deny project deny → deny 配置白名单 → allow(break-glass 位次,issue #58:运维显式加白是「比模式 更强的信任」,压过只读档与 project ask;硬禁仍在它前面——白名单不是 绕禁的后门) 只读拦截 → deny(只读档的非只读工具直接拒——「禁」压过「问」) yolo → 放行(alwaysAsk 与一切「禁」都在它前面——yolo 跳过的是「问」不是「禁」) project ask → ask 只读模式 → 只读工具放行、其余拦下 project allow → allow 预批钩子 → allow(WebFetch URL 白名单类) 兜底 → 会话授权、只读工具放行,其余 ask
「只读拦截」段是「只读模式」段的前置影子:只读档对非只读工具是「禁」不是 「问」——它必须压过 alwaysAsk 与 project ask 两处「问」,否则只读退化成 「换个姿势问」(issue #24)。它与后段同 RuleID/Reason,审计口径不变; mode 之间互斥,故它与 yolo 段无交集。唯一的例外是配置白名单(#58 的 break-glass):只读档默认禁写,但运维对单个工具的显式加白压得过模式档 (下游不再需要在 permgate 之外私设一层本地 override,授权真相只有一份)。
与 agentkit policy(操作审计门:四模式裁决矩阵)正交:policy 裁「业务操作 该不该做」,permgate 裁「这次工具调用要不要人确认」。
规则内容匹配(对标 matchesRuleContent):精确 / `prefix:*` 前缀(prefix 或 prefix+空白开头——空白含换行,换行同属命令分隔)/ `*` 通配(跨行透明)。 匹配对象从工具输入提取(command/url/file_path/path/pattern/patch_text 字段 或字符串输入本身);类型化输入(map[string]string、struct 及其字段/json tag) 同样参与提取(#65/#72:此前这些形态静默落空,内容级 deny 对类型化调用方 全部失配——fail-open)。
复合命令(#62/#71):command 形态的匹配对象先按 shell 操作符(`&&` `||` `;` `|` 换行,引号内不拆)拆段,段级命中的量词按规则极性取 fail-closed 侧 ——allow/会话授权要求**每段**都命中(`git status && curl evil` 不得搭 `git status:*` 的车),deny/ask 只要**任一段**命中(复合命令里混进一段 危险命令即拦)。不追求完整 shell 语法:命令替换(`$(…)`、反引号)等真 AST 形态归 tools/bashguard,本包只做 stdlib 操作符切分,拆不动的形态宁可 让 allow 失配(落 ask),不得让操作符后的命令搭车放行。
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Capability ¶
type Capability struct {
// ReadOnly 只读工具(读后无副作用)。
ReadOnly bool
// AlwaysAsk 工具自报必须逐次确认(不可被模式/白名单绕过,只可被硬阻断
// 与会话授权短路)。
AlwaysAsk bool
// RequiresUserInteraction 用户交互型工具(AskUserQuestion 类)。
RequiresUserInteraction bool
}
Capability 工具能力自报(注册期声明)。
type Rule ¶
type Rule struct {
// ToolName 工具名;Edit 规则同时匹配 Write(写面等价,对标 ZCode 特例)。
ToolName string
// Content 输入内容模式(command/url/路径):空 = 该工具全部调用;
// "git status:*" 前缀(空白边界,命令形态专用);含 * 通配;否则精确等值。
// ⚠ 路径规则用 wildcard 形态("/repo/data/*")而非前缀形态——前缀边界是
// 空白/制表符,对路径子树形同虚设;路径 subject 匹配前会 filepath.Clean
//(`..` 穿越不命中),符号链接围栏归执行器。
Content string
}
Rule 单条许可规则。
type Service ¶
type Service struct {
// AllowedTools 配置级白名单(工具名集合)。break-glass 位次(#58):
// 运维显式加白压过只读档与 project ask,但压不过 disallowed/project deny。
AllowedTools map[string]bool
// DisallowedTools 配置级黑名单(压过 yolo、alwaysAsk 的确认段与白名单)。
DisallowedTools map[string]bool
// ProjectRules 项目/产品级规则集。
ProjectRules Ruleset
// Preapprove 预批钩子(WebFetch URL 白名单类):返回非空 ruleID 即放行。
// 判定位次在项目 allow 之后、配置白名单之后(plan 拦截压得过预批)。
Preapprove func(ctx context.Context, tool string, input any) (ruleID string, ok bool)
// contains filtered or unexported fields
}
Service 许可判定器。零值可用(全部走兜底档)。
并发契约(issue #66):Check/AllowForSession/ResetSession/Update/SessionAllows 之间并发安全——Check 全程只读锁内快照,Update 在锁内整体换配置。导出的 配置字段(AllowedTools/DisallowedTools/ProjectRules/Preapprove)视为**装配期 字段**:直接赋值只允许发生在并发使用开始前;运行期热更新一律经 Update 换**新值**(不得原地改切片元素——快照共享底层数组,原地改与并发读仍构成 竞争),也不得在 Update 回调里再调本包方法(非重入锁,会死锁)。
func (*Service) AllowForSession ¶
AllowForSession 会话级授权登记(用户对 ask 选择「本会话总是允许」时调用)。 同 (tool, content) 重复登记折叠(#70):规则表=授权真相——N 次「总是允许」 不该积累 N 条相同规则(线性膨胀稀释审计面)。
func (*Service) Check ¶
func (s *Service) Check(ctx context.Context, tool string, input any, mode Mode, cap Capability) Decision
Check 判定一次工具调用。input 为工具入参(string、map[string]any、 map[string]string 或 struct 等类型化形态——内容字段按 subjectKeys 名单提取)。 分段优先级见包注释——顺序是踩坑固化的,别调换。
func (*Service) ResetSession ¶
func (s *Service) ResetSession()
ResetSession 会话结束清空会话授权(实例消亡语义的显式形态)。
func (*Service) SessionAllows ¶ added in v0.20.0
SessionAllows 本会话已授权清单快照(#70 审计面):锁内深拷贝,调用方可安全 遍历/导出;空清单返回 nil。