174 lines
5.0 KiB
Markdown
174 lines
5.0 KiB
Markdown
|
|
# GitHub Copilot SDK 权限控制测试报告
|
|||
|
|
|
|||
|
|
## 测试日期
|
|||
|
|
|
|||
|
|
2026-01-30
|
|||
|
|
|
|||
|
|
## 测试环境
|
|||
|
|
|
|||
|
|
- **Model**: gpt-4.1
|
|||
|
|
- **Python**: 3.12
|
|||
|
|
- **Copilot SDK**: Latest
|
|||
|
|
|
|||
|
|
## 关键发现
|
|||
|
|
|
|||
|
|
### 1. Shell 权限请求结构
|
|||
|
|
|
|||
|
|
Shell 类型的权限请求使用 **`fullCommandText`** 字段,而非 `command` 字段。
|
|||
|
|
|
|||
|
|
**完整请求示例:**
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"kind": "shell",
|
|||
|
|
"toolCallId": "call_JKLi7tz3uSDQWE3LgzCpvSVy",
|
|||
|
|
"fullCommandText": "ls -la",
|
|||
|
|
"intention": "List all files and directories with details in the current directory",
|
|||
|
|
"commands": [
|
|||
|
|
{
|
|||
|
|
"identifier": "ls -la",
|
|||
|
|
"readOnly": false
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"possiblePaths": [],
|
|||
|
|
"possibleUrls": [],
|
|||
|
|
"hasWriteFileRedirection": false,
|
|||
|
|
"canOfferSessionApproval": false
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 正则匹配模式验证
|
|||
|
|
|
|||
|
|
正则白名单模式已验证有效,必须使用 `fullCommandText` 字段:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
command = request.get("fullCommandText", "") or request.get("command", "")
|
|||
|
|
pattern = self.valves.PERMISSIONS_SHELL_ALLOW_PATTERN
|
|||
|
|
if pattern and command:
|
|||
|
|
if re.match(pattern, command):
|
|||
|
|
return {"kind": "approved"}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 测试结果
|
|||
|
|
|
|||
|
|
### 完整测试套件(8/8 通过 ✅)
|
|||
|
|
|
|||
|
|
| # | 测试用例 | 配置 | 提示词 | 预期 | 结果 | 状态 |
|
|||
|
|
|---|---------|------|--------|------|------|------|
|
|||
|
|
| 1 | Default Deny Shell | 默认 | 请执行: ls -la | ❌ Denied | ❌ Denied | ✅ |
|
|||
|
|
| 2 | Allow All | allow_all=True | 请执行: ls -la | ✅ Approved | ✅ Approved | ✅ |
|
|||
|
|
| 3 | Allow Shell | allow_shell=True | 请执行: pwd | ✅ Approved | ✅ Approved | ✅ |
|
|||
|
|
| 4 | Regex Match: ^ls | pattern='^ls' | 请执行: ls -la | ✅ Approved | ✅ Approved | ✅ |
|
|||
|
|
| 5 | Regex No Match | pattern='^ls' | 请执行: pwd | ❌ Denied | ❌ Denied | ✅ |
|
|||
|
|
| 6 | Regex Complex | pattern='^(ls\|pwd\|echo)' | 请执行: pwd | ✅ Approved | ✅ Approved | ✅ |
|
|||
|
|
| 7 | Regex No Match: git | pattern='^(ls\|pwd\|echo)' | 请执行: git status | ❌ Denied | ❌ Denied | ✅ |
|
|||
|
|
| 8 | Read Permission | 默认 | Read: README.md | ✅ Approved | ✅ Approved | ✅ |
|
|||
|
|
|
|||
|
|
**总体通过率: 100%** 🎉
|
|||
|
|
|
|||
|
|
## 推荐配置示例
|
|||
|
|
|
|||
|
|
### 1. 安全模式(推荐生产环境)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
PERMISSIONS_ALLOW_ALL: bool = False
|
|||
|
|
PERMISSIONS_ALLOW_SHELL: bool = False
|
|||
|
|
PERMISSIONS_SHELL_ALLOW_PATTERN: str = "^(ls|pwd|echo|cat).*"
|
|||
|
|
PERMISSIONS_ALLOW_WRITE: bool = False
|
|||
|
|
PERMISSIONS_ALLOW_MCP: bool = True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 开发模式
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
PERMISSIONS_ALLOW_ALL: bool = False
|
|||
|
|
PERMISSIONS_ALLOW_SHELL: bool = False
|
|||
|
|
PERMISSIONS_SHELL_ALLOW_PATTERN: str = "^(ls|pwd|echo|cat|grep|git status|npm test).*"
|
|||
|
|
PERMISSIONS_ALLOW_WRITE: bool = False
|
|||
|
|
PERMISSIONS_ALLOW_MCP: bool = True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 完全信任模式(仅限受控环境)
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
PERMISSIONS_ALLOW_ALL: bool = True
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 实现建议
|
|||
|
|
|
|||
|
|
### 正确的权限处理代码
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
import re
|
|||
|
|
from typing import Any, Dict
|
|||
|
|
|
|||
|
|
async def on_user_permission_request(request: Dict[str, Any], context: Dict[str, str]):
|
|||
|
|
"""
|
|||
|
|
统一权限审批网关
|
|||
|
|
"""
|
|||
|
|
kind = request.get("kind")
|
|||
|
|
# 关键:使用 fullCommandText 而非 command
|
|||
|
|
command = request.get("fullCommandText", "") or request.get("command", "")
|
|||
|
|
|
|||
|
|
# 1. 超级模式
|
|||
|
|
if self.valves.PERMISSIONS_ALLOW_ALL:
|
|||
|
|
return {"kind": "approved"}
|
|||
|
|
|
|||
|
|
# 2. 默认安全(read、url)
|
|||
|
|
if kind in ["read", "url"]:
|
|||
|
|
return {"kind": "approved"}
|
|||
|
|
|
|||
|
|
# 3. Shell 细粒度控制
|
|||
|
|
if kind == "shell":
|
|||
|
|
if self.valves.PERMISSIONS_ALLOW_SHELL:
|
|||
|
|
return {"kind": "approved"}
|
|||
|
|
|
|||
|
|
pattern = self.valves.PERMISSIONS_SHELL_ALLOW_PATTERN
|
|||
|
|
if pattern and command:
|
|||
|
|
try:
|
|||
|
|
if re.match(pattern, command):
|
|||
|
|
return {"kind": "approved"}
|
|||
|
|
except re.error as e:
|
|||
|
|
logger.error(f"Invalid regex: {pattern} - {e}")
|
|||
|
|
|
|||
|
|
# 4. Write 权限
|
|||
|
|
if kind == "write" and self.valves.PERMISSIONS_ALLOW_WRITE:
|
|||
|
|
return {"kind": "approved"}
|
|||
|
|
|
|||
|
|
# 5. MCP 权限
|
|||
|
|
if kind == "mcp" and self.valves.PERMISSIONS_ALLOW_MCP:
|
|||
|
|
return {"kind": "approved"}
|
|||
|
|
|
|||
|
|
# 6. 默认拒绝
|
|||
|
|
logger.warning(f"Permission Denied: {kind} {command}")
|
|||
|
|
return {
|
|||
|
|
"kind": "denied-by-rules",
|
|||
|
|
"rules": [{"kind": "security-policy"}]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 常见正则模式示例
|
|||
|
|
|
|||
|
|
| 用途 | 正则表达式 | 说明 |
|
|||
|
|
|------|-----------|------|
|
|||
|
|
| 只读命令 | `^(ls|pwd|cat|echo|grep).*` | 允许常见只读命令 |
|
|||
|
|
| Git 只读 | `^git (status\|log\|diff\|show).*` | 允许 Git 只读操作 |
|
|||
|
|
| npm/yarn 测试 | `^(npm\|yarn) (test\|run).*` | 允许测试脚本 |
|
|||
|
|
| 完全 shell | `.*` | ⚠️ 危险:允许所有命令 |
|
|||
|
|
|
|||
|
|
## 测试脚本位置
|
|||
|
|
|
|||
|
|
- 基础测试: [test_shell_permission_pattern.py](./test_shell_permission_pattern.py)
|
|||
|
|
- 完整测试套件: [test_permission_comprehensive.py](./test_permission_comprehensive.py)
|
|||
|
|
|
|||
|
|
## 结论
|
|||
|
|
|
|||
|
|
✅ **GitHub Copilot SDK 的权限控制机制完全有效**
|
|||
|
|
✅ **正则白名单模式已验证可用**
|
|||
|
|
⚠️ **必须使用 `fullCommandText` 字段获取命令内容**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**测试执行者**: GitHub Copilot
|
|||
|
|
**审核状态**: ✅ 已验证
|