Code Reader
首页
帮助
设计文档
首页
帮助
设计文档
  • Permission (PermissionNext)

Permission (PermissionNext)

PermissionNext 是 OpenCode 的权限控制系统,管理工具和操作的访问控制。

概述

PermissionNext 使用基于规则的权限系统,支持通配符匹配和精细的访问控制。每个操作(如 write、bash、read)都需要权限检查,可以设置为 allow(允许)、deny(拒绝)或 ask(询问用户)。

定义位置

packages/opencode/src/permission/next.ts:12-95

主要对象

Action

权限动作类型。

type Action = "allow" | "deny" | "ask"
值说明
allow允许操作,无需确认
deny拒绝操作
ask询问用户确认后再执行

Rule

单条权限规则。

属性名类型必填说明
permissionstring是权限名称
patternstring是匹配模式
actionAction是允许的动作

Ruleset

规则集合,按优先级排序。

type Ruleset = Rule[]

Request

权限请求对象。

| 属性名 | 类型 | 必填 | 说明 | | ---------------- | ------------------- | ---- | --------------------------- | ------ | --- | -------- | | id | string | 是 | 请求唯一标识符 | | sessionID | string | 是 | 会话 ID | | permission | string | 是 | 请求的权限名称 | | patterns | string[] | 是 | 匹配模式列表 | | metadata | Record<string, any> | 是 | 元数据 | | always | string[] | 是 | 总是要询问的模式列表 tool | object | 否 | 工具信息 | | tool.messageID | string | - | 工具所在的消息 ID | | tool.callID | string | - | 工具调用 ID |

Reply

用户对权限请求的回复。

type Reply = "once" | "always" | "reject"
值说明
once本次允许,下次继续询问
always总是允许(添加到批准列表)
reject拒绝操作

TypeScript 类型定义

export type Action = "allow" | "deny" | "ask"

export type Rule = {
  permission: string
  pattern: string
  action: Action
}

export type Ruleset = Rule[]

export type Request = {
  id: string
  sessionID: string
  permission: string
  patterns: string[]
  metadata: Record<string, any>
  always: string[]
  tool?: {
    messageID: string
    callID: string
  }
}

export type Reply = "once" | "always" | "reject"

权限事件

事件名类型说明
permission.askedrequest: Request询问权限
permission.replied{sessionID, requestID, reply}收到回复

典型使用场景

1. 从配置创建规则集

const permission = {
  "*": "allow",
  write: "ask",
  bash: {
    "rm -rf": "deny",
    "npm install": "ask",
  },
}

const ruleset = PermissionNext.fromConfig(permission)

转换为:

;[
  { permission: "*", action: "allow", pattern: "*" },
  { permission: "write", action: "ask", pattern: "*" },
  { permission: "bash", action: "deny", pattern: "rm -rf" },
  { permission: "bash", action: "ask", pattern: "npm install" },
]

2. 检查权限

// 创建权限请求
const request: PermissionNext.Request = {
  id: Identifier.ascending("permission"),
  sessionID: session.id,
  permission: "write",
  patterns: ["/path/to/file.js"],
  metadata: {},
  always: [],
}

// 发送权限检查
await PermissionNext.ask({
  ...request,
  ruleset: agent.permission,
})

// 如果需要用户确认,会发布 permission.asked 事件
// UI 应该显示对话框并等待用户回复

3. 回复权限请求

// 用户选择 "允许一次"
await PermissionNext.reply({
  requestID: request.id,
  reply: "once",
})

// 用户选择 "总是允许"(添加到批准列表)
await PermissionNext.reply({
  requestID: request.id,
  reply: "always",
})

// 用户选择 "拒绝"
await PermissionNext.reply({
  requestID: request.id,
  reply: "reject",
})

4. 合并多个规则集

const defaults = PermissionNext.fromConfig({
  "*": "allow",
  write: "ask",
})

const agentRules = PermissionNext.fromConfig({
  bash: "deny",
})

const userRules = PermissionNext.fromConfig({
  read: "ask",
})

// 合并(优先级:defaults < agent < user)
const merged = PermissionNext.merge(defaults, agentRules, userRules)

5. 添加永久批准

// 当用户选择 "always" 时,添加到批准列表
await PermissionNext.addApproval({
  projectID: project.id,
  patterns: ["/path/to/file.js"], // 总是允许的模式
})

6. 检查是否批准

const approvals = await PermissionNext.listApproval(project.id)

for (const approval of approvals) {
  console.log(`Approved patterns: ${approval.patterns.join(", ")}`)
}

权限匹配规则

通配符匹配

支持通配符模式:

"*" // 匹配所有路径
"*.js" // 匹配所有 .js 文件
"/path/*" // 匹配 /path/ 下所有内容
"/path/*.js" // 匹配 /path/ 下所有 .js 文件

模式匹配优先级

  1. 精确匹配优先于通配符
  2. 更具体的模式优先于更通用的模式

示例:

const rules = [
  { permission: "write", pattern: "*.js", action: "allow" },
  { permission: "write", pattern: "*", action: "deny" },
]

// "/path/to/file.js" 匹配规则 1 (允许)
// "/path/to/file.txt" 匹配规则 2 (拒绝)

权限系统架构

内置权限规则

默认权限

const defaults = {
  "*": "allow", // 默认允许所有操作
  doom_loop: "ask", // 防止无限循环
  external_directory: {
    "*": "ask", // 外部目录需要确认
    [Truncate.DIR]: "allow", // 允许截断目录
    [Truncate.GLOB]: "allow", //允许使用 glob
  },
  question: "deny", // 禁用 question 工具
  plan_enter: "deny", // 禁用 plan_enter
  plan_exit: "deny", // 禁用 plan_exit
  read: {
    "*": "allow", // 默认允许读取
    "*.env": "ask", // 环境变量文件需要确认
    "*.env.*": "ask", // 环境变量文件需要确认
    "*.env.example": "allow", // 环境变量示例文件允许
  },
}

权限存储

待定请求

;["permission_pending", projectID, sessionID, requestID] = Request

批准列表

["permission_approval", projectID] = {
  patterns: string[]
}[]

错误处理

DeniedError

当权限被拒绝时抛出:

class DeniedError extends Error {
  constructor(rules: Rule[]) {
    super("Permission denied")
    this.rules = rules
  }
}

权限配置示例

完整示例

{
  "permission": {
    // 全局设置
    "*": "allow",

    // 文件写入需要确认
    "write": "ask",

    // 危险命令拒绝
    "bash": {
      "rm -rf": "deny",
      "dd": "deny",
      ":(){ :|:& };": "deny", // 防止 fork 炸弹
    },

    // 特定文件路径
    "edit": {
      "/etc/*": "deny",
      "/root/*": "deny",
      "node_modules/**": "allow",
    },

    // 网络操作
    "webfetch": "ask",
    "websearch": "ask",

    // 测试工具
    "test": {
      "--coverage": "allow",
      "*": "ask",
    },
  },
}

对象关系

变更历史

版本变更内容日期
v1初始 PermissionNext 架构-
v1.1添加 approval 持久化-
v1.2改进通配符匹配-

相关文档

  • Agent - Agent 配置
  • Config - 配置系统