Skip to content

Latest commit

 

History

History
801 lines (635 loc) · 17.9 KB

File metadata and controls

801 lines (635 loc) · 17.9 KB
title 5.2c Agent 权限与安全
subtitle 精确控制 Agent 能做什么
course OpenCode 中文实战课
stage 第五阶段
lesson 5.2c
duration 25 分钟
practice 20 分钟
level 进阶
description 精确控制 Agent 可以做什么、不可以做什么,确保 AI 操作的安全性。
tags
Agent
权限
安全
TaskTool
prerequisite
5.2a Agent 快速入门

5.2c Agent 权限与安全

精确控制 Agent 可以做什么、不可以做什么。

📝 课程笔记

本课核心知识点整理:

Agent权限与安全学霸笔记


学完你能做什么

  • 理解权限系统架构
  • 配置 bash/edit/task/skill 权限
  • 设计安全的 Agent 系统
  • 实现最小权限原则

权限系统架构

三种权限动作

动作 说明 效果
allow 允许 直接执行,无需确认
ask 询问 弹出确认框,用户决定
deny 禁止 拒绝执行,Agent 收到错误

权限配置层级

默认权限(源码定义)
    ↓ 覆盖
全局配置 permission
    ↓ 覆盖
Agent 级别 permission

后面的覆盖前面的

来源:config.ts:418-447agent.ts:194

规则优先级:最后匹配获胜

这是最重要的规则!当多个规则都匹配时,最后一个匹配的规则生效

{
  "permission": {
    "bash": {
      "*": "ask",           // 规则 1:所有命令需确认
      "git *": "allow",     // 规则 2:git 命令允许
      "git push*": "deny"   // 规则 3:git push 禁止
    }
  }
}

执行 git push origin main

  1. 匹配规则 1(*)→ ask
  2. 匹配规则 2(git *)→ allow
  3. 匹配规则 3(git push*)→ deny
  4. 最终结果:deny(规则 3 在最后)

来源:agents.mdx:473permissions.mdx:70


可配置的权限类型

权限 匹配对象 说明
read 文件路径 读取文件
edit 文件路径 所有文件修改(edit/write/patch/multiedit)
glob glob 模式 文件搜索
grep 正则表达式 内容搜索
list 目录路径 列出目录内容
bash 命令字符串 执行 shell 命令
task subagent 名称 调用子 Agent
skill skill 名称 加载技能
lsp - LSP 查询(目前不支持细粒度)
todoread - 读取 Todo 列表
todowrite - 更新 Todo 列表
webfetch URL 获取网页内容
websearch 查询字符串 网页搜索
codesearch 查询字符串 代码搜索
external_directory - 访问项目目录之外的路径
doom_loop - 检测重复调用(同一工具连续调用 3 次相同输入)
question - 向用户提问(默认 deny,防止 subagent 打扰用户)
plan_enter - 进入计划模式(代码已弃用)
plan_exit - 退出计划模式,切换到 build agent

来源:config.ts:614-645


权限配置语法

简单语法:单一动作

{
  "permission": {
    "edit": "allow",      // 所有文件编辑允许
    "bash": "ask",        // 所有命令需确认
    "webfetch": "deny"    // 禁止获取网页
  }
}

全局设置

{
  "permission": "allow"   // 所有权限都允许
}

对象语法:细粒度控制

{
  "permission": {
    "bash": {
      "*": "ask",              // 默认需确认
      "git status": "allow",   // git status 允许
      "git log*": "allow",     // git log 开头的允许
      "rm -rf*": "deny"        // rm -rf 禁止
    }
  }
}

通配符

符号 含义 示例
* 匹配任意字符(0个或多个) git * 匹配 git statusgit log
? 匹配单个字符 file?.txt 匹配 file1.txt

bash 权限详解

bash 权限匹配的是解析后的命令字符串

常见配置

{
  "permission": {
    "bash": {
      "*": "ask",                    // 默认需确认

      // Git 命令
      "git status": "allow",
      "git log*": "allow",
      "git diff*": "allow",
      "git branch*": "allow",
      "git checkout*": "ask",        // 切换分支要确认
      "git push*": "ask",            // 推送要确认
      "git reset --hard*": "deny",   // 硬重置禁止

      // 包管理
      "npm install*": "allow",
      "npm run*": "allow",
      "npm publish*": "deny",        // 发布禁止

      // 危险命令
      "rm -rf*": "deny",
      "sudo*": "deny",
      "chmod 777*": "deny"
    }
  }
}

Plan Agent 的最佳实践

{
  "agent": {
    "plan": {
      "permission": {
        "bash": {
          "*": "deny",               // 默认禁止
          "git log*": "allow",       // 只读命令允许
          "git diff*": "allow",
          "git status": "allow",
          "ls*": "allow",
          "cat*": "allow",
          "head*": "allow",
          "tail*": "allow"
        }
      }
    }
  }
}

edit 权限详解

edit 权限控制所有文件修改操作,包括:

  • edit 工具
  • write 工具
  • patch 工具
  • multiedit 工具

常见配置

{
  "permission": {
    "edit": {
      "*": "allow",                    // 默认允许

      // 敏感文件
      "*.env": "deny",
      "*.env.*": "deny",
      "*.env.example": "allow",        // 示例文件允许
      ".env.local": "deny",

      // 系统文件
      "package-lock.json": "deny",     // 锁文件不要改
      "pnpm-lock.yaml": "deny",
      "yarn.lock": "deny",

      // 目录
      "node_modules/*": "deny",
      ".git/*": "deny",
      "dist/*": "deny"
    }
  }
}

只读 Agent 配置

{
  "agent": {
    "readonly-auditor": {
      "description": "只读代码审计,不修改任何文件",
      "mode": "subagent",
      "permission": {
        "edit": "deny"                 // 禁止所有编辑
      }
    }
  }
}

task 权限:控制 subagent 调用

task 权限控制 Agent 可以调用哪些 subagent

工作原理

当设置 task: deny 时:

  1. 该 subagent 从 Task tool 的描述中完全移除
  2. 模型不会尝试调用它(因为看不到)

注意:用户仍可通过 @agent-name 手动调用任何 subagent。task 权限只影响 Agent 自动调用。

来源:agents.mdx:557-565

配置示例

{
  "agent": {
    "safe-orchestrator": {
      "description": "安全编排器,只能调用指定的 subagent",
      "mode": "primary",
      "permission": {
        "task": {
          "*": "deny",                   // 禁止所有
          "docs-writer": "allow",        // 允许文档
          "code-reviewer": "allow",      // 允许审查
          "dangerous-agent": "deny"      // 显式禁止
        }
      }
    }
  }
}

通配符使用

{
  "agent": {
    "orchestrator": {
      "permission": {
        "task": {
          "*": "deny",
          "safe-*": "allow",            // 所有 safe- 开头的允许
          "internal/*": "allow",        // 嵌套目录的允许
          "code-reviewer": "ask"        // 需要确认
        }
      }
    }
  }
}

TaskTool 参数详解

Task 工具的完整参数定义如下:

参数 类型 必需 说明
description string 任务描述(3-5 个词),用作子会话标题
prompt string 子代理要执行的任务提示
subagent_type string 要调用的子代理名称(必须是非 primary agent)
session_id string 继续已存在的子会话
command string 触发此任务的命令(用于调试)

执行流程

TaskTool 的工作流程如下:

主 Agent (Build)
    ↓
    1. 权限检查
       - 检查调用者是否有 task 权限
       - 过滤可访问的 subagent
       ↓
    2. 创建子会话
       - 在主会话下创建独立 session
       - 标题:描述 + (@subagent subagent)
       - 应用限制权限(todowrite/todoread/task)
       ↓
    3. 调用子代理
       - 子代理在独立 session 中执行
       - 上下文仅包含传入的 prompt
       - 监听 PartUpdated 事件获取进度
       ↓
    4. 返回结果
       - 收集所有工具调用摘要
       - 生成对话摘要
       - 返回给主 Agent

关键点:子代理运行在独立的 Session 中,看不到主 Agent 的对话历史。调用时必须提供完整上下文。

子代理的默认限制

为了防止无限递归,子代理(无论通过 task 调用还是 @ 手动调用)受到以下硬编码限制

限制 说明 原因
todowrite: deny 禁止写入待办列表 防止子代理干扰主 Agent 任务管理
todoread: deny 禁止读取待办列表 同上
task: deny 禁止再调用子代理 防止无限递归

实际使用示例

配置允许调用特定子代理

{
  "agent": {
    "orchestrator": {
      "description": "任务编排 Agent,可调用专门子代理",
      "mode": "primary",
      "permission": {
        "task": {
          "docs-writer": "allow",      // 允许文档写作
          "code-reviewer": "allow",    // 允许代码审查
          "general": "allow",           // 允许通用任务
          "*": "deny"                 // 其他禁止
        }
      }
    }
  }
}

Agent 内部调用 TaskTool

# 伪代码示例
主 Agent 收到:帮我写 API 文档

1. 分析任务类型 → 确定需要 docs-writer 子代理
2. 调用 TaskTool:
   - description: "编写 API 文档"
   - prompt: "为以下函数编写文档..."
   - subagent_type: "docs-writer"
3. 子代理执行 → 返回文档内容
4. 主 Agent 接收结果 → 继续对话

继续子会话

当子代理需要分步执行时,可以传递 session_id 继续之前的工作:

TaskTool(
  description: "完善文档",
  prompt: "检查文档完整性并补充缺失内容",
  subagent_type: "docs-writer",
  session_id: "abc123"  // 继续之前的会话
)

来源packages/opencode/src/tool/task.ts:23-193


skill 权限:控制技能加载

skill 权限控制 Agent 可以加载哪些技能。

配置示例

{
  "agent": {
    "restricted-agent": {
      "description": "受限 Agent,只能使用指定技能",
      "mode": "subagent",
      "permission": {
        "skill": {
          "*": "deny",                   // 禁止所有技能
          "docs-writer": "allow",        // 只允许文档技能
          "translator": "allow"
        }
      }
    }
  }
}

来源:skill.ts:15-21


内置安全规则

OpenCode 默认配置了一些安全规则:

.env 文件保护

// 内置默认配置
{
  "permission": {
    "read": {
      "*": "allow",
      "*.env": "ask",           // .env 文件需确认(安全考虑)
      "*.env.*": "ask",         // .env.xxx 也需确认
      "*.env.example": "allow"  // 示例文件允许
    }
  }
}

来源:agent.ts:67-72

doom_loop 检测

当同一工具被连续调用 3 次,且输入完全相同时,触发 doom_loop 检测。

{
  "permission": {
    "doom_loop": "ask"    // 默认值:提示用户确认
  }
}

question 权限

控制 Agent 是否能使用 question 工具向用户提问。

默认值 说明
subagent: deny 防止 subagent 随意打扰用户
build agent: allow 主 Agent 可以提问

使用场景:当你需要 subagent 在遇到不确定时向你确认,可以设为 allow

{
  "agent": {
    "interactive-helper": {
      "permission": {
        "question": "allow"    // 允许这个 subagent 提问
      }
    }
  }
}

来源:agent.ts:56-64, question.ts

external_directory 保护

当 Agent 尝试访问项目目录之外的路径时:

{
  "permission": {
    "external_directory": "ask"    // 默认值:提示用户确认
  }
}

plan_enter / plan_exit 权限

控制 Agent 是否能切换计划模式:

  • plan_enter:进入计划模式(代码已实现但被注释弃用,计划模式通过 /plan 命令触发)
  • plan_exit:退出计划模式,切换到 build agent

默认值均为 deny,只有 build agent 和 plan agent 允许调用。

{
  "agent": {
    "build": {
      "permission": {
        "plan_enter": "allow",   // build agent 允许切换到 plan
        "plan_exit": "deny"      // 不允许退出(只有 plan agent 可以)
      }
    }
  }
}

来源:agent.ts:56-65, plan.ts

废弃字段:tools

⚠️ tools 字段已废弃,请使用 permission 字段代替。

旧写法(已废弃)

{
  "agent": {
    "my-agent": {
      "tools": {
        "bash": false,      // 禁用 bash
        "edit": true        // 允许编辑
      }
    }
  }
}

新写法

{
  "agent": {
    "my-agent": {
      "permission": {
        "bash": "deny",     // 禁用 bash
        "edit": "allow"     // 允许编辑
      }
    }
  }
}

迁移说明

  • tools 中的 write/edit/patch/multiedit 都映射到 edit 权限
  • true"allow"false"deny"
  • 系统会自动转换旧配置,但建议手动更新

来源:config.ts:675-737

子代理的隐式限制

除了配置的权限外,子代理(无论 subagent 还是被调用的 all 模式)还受到以下硬编码限制

  1. Todo 工具禁用

    • 子代理永远无法使用 todowritetodoread
    • 这是为了防止子代理干扰主 Agent 的任务列表管理。
  2. 主代理专属工具禁用

    • 配置在 primary_tools 中的工具,子代理无法使用。
  3. Task 嵌套限制

    • 子代理默认不能再调用其他子代理(除非显式授予 task 权限)。
    • 例如:explore 无法调用 general,因为它的默认权限是 *: deny

    为什么这样设计?

    • 防止无限递归:避免子 agent 之间形成循环调用链,导致任务永远无法结束
    • 控制复杂度:让任务执行路径更可预测、更易调试
    • 资源管控:每次调用子 agent 都会创建新 session,消耗 token 和计算资源
    • 职责分离:子 agent 应专注做一件事,编排工作交给主 agent

Agent 级别权限覆盖

在 Agent 配置中设置的权限会覆盖全局权限。

JSON 配置

{
  "permission": {
    "bash": {
      "*": "ask",
      "git status": "allow"
    }
  },
  "agent": {
    "build": {
      "permission": {
        "bash": {
          "git push": "allow"       // build agent 额外允许 push
        }
      }
    },
    "plan": {
      "permission": {
        "bash": {
          "*": "deny",              // plan agent 禁止所有命令
          "git log*": "allow"       // 除了查看日志
        }
      }
    }
  }
}

Markdown 配置

---
description: 只读审计 Agent
mode: subagent
permission:
  edit: deny
  bash:
    "*": deny
    "git log*": allow
    "git diff*": allow
  webfetch: deny
---

只分析代码,不做任何修改。

安全最佳实践

1. 最小权限原则

只授予 Agent 完成任务所需的最小权限。

// ❌ 不好:过于宽松
{
  "agent": {
    "my-agent": {
      "permission": "allow"
    }
  }
}

// ✅ 好:明确列出需要的权限
{
  "agent": {
    "my-agent": {
      "permission": {
        "read": "allow",
        "edit": {
          "docs/*": "allow"
        },
        "bash": "deny"
      }
    }
  }
}

2. 显式列出允许的命令

// ❌ 不好:允许所有,然后禁止危险的
{
  "permission": {
    "bash": {
      "*": "allow",
      "rm -rf*": "deny"
    }
  }
}

// ✅ 好:禁止所有,然后允许需要的
{
  "permission": {
    "bash": {
      "*": "deny",
      "git status": "allow",
      "npm test": "allow"
    }
  }
}

3. 敏感操作设为 ask

{
  "permission": {
    "bash": {
      "*": "allow",
      "git push*": "ask",        // 推送需确认
      "npm publish*": "ask",     // 发布需确认
      "docker *": "ask"          // Docker 操作需确认
    }
  }
}

4. 定期审查权限配置

检查清单:

  • 是否有不再需要的权限?
  • 敏感操作是否都设为 ask?
  • 新增的 Agent 权限是否合理?

踩坑提醒

现象 原因 解决
权限不生效 规则顺序错误 * 放最前面,具体规则放后面
subagent 仍能被调用 用户 @ 调用不受限 task 权限只影响 Task tool
bash 命令匹配失败 匹配的是解析后的命令 检查实际命令格式(含参数)
.env 无需确认就读取 自定义规则覆盖了默认 如需保护,设为 .env ask
权限太严格 设了 *: deny 忘了允许必要的 逐条添加允许规则

与 5.5 权限管控的关系

本章专注于 Agent 级别的权限配置

全局权限配置和更多细节,请参考 5.5 权限管控


本课小结

你学会了:

  1. 权限系统架构:三种动作、配置层级、最后匹配获胜
  2. 15+ 权限类型:bash、edit、task、skill、question、plan_enter、plan_exit 等
  3. 细粒度控制:使用对象语法和通配符
  4. TaskTool 机制:子代理调用、参数定义、执行流程
  5. 子代理限制:todowrite/todoread/task 禁用,防止无限递归
  6. 内置安全规则:.env 保护、doom_loop、external_directory
  7. 安全最佳实践:最小权限、显式允许、敏感操作 ask

下一课预告

配置好权限,还有更多高级技巧:工具接口设计、透传参数、调试方法。

下一课5.2d Agent 高级技巧