0%

Claude Code 源码解析 (9):多层权限决策引擎设计

Claude Code 源码解析 (9):多层权限决策引擎设计

导读: 这是 Claude Code 20 个功能特性源码解析系列的第 9 篇,深入分析权限系统的架构设计。


📋 目录

  1. 问题引入:权限控制的挑战
  2. 技术原理:多层权限决策架构
  3. 设计思想:为什么这样设计
  4. 解决方案:完整实现详解
  5. OpenClaw 最佳实践
  6. 总结

问题引入:权限控制的挑战

痛点场景

场景 1:权限粒度太粗

1
2
3
4
5
6
7
8
用户:"给 AI 完全访问权限"

AI 可以:
- 读取 /etc/passwd ✅
- 删除整个项目 ❌
- 发送数据到外部 ❌

→ 权限过大,风险不可控

场景 2:每次都要确认

1
2
3
4
5
6
7
8
9
10
11
12
用户:"运行测试"

AI: 确认执行 pytest?
用户:确认

AI: 确认执行 flake8?
用户:确认

AI: 确认执行 mypy?
用户:确认

→ 繁琐,用户体验差

场景 3:规则无法覆盖所有场景

1
2
3
4
5
6
7
规则:允许执行 npm install

但:
- npm install 正常包 → 应该允许
- npm install 恶意包 → 应该拒绝

→ 静态规则无法判断意图

核心问题

设计 AI 助手的权限系统时,面临以下挑战:

  1. 粒度问题

    • 如何平衡灵活性与安全性?
    • 如何支持细粒度权限控制?
  2. 效率问题

    • 如何减少不必要的确认?
    • 如何让常用操作更流畅?
  3. 智能问题

    • 如何判断操作的真实意图?
    • 如何处理规则未覆盖的场景?
  4. 审计问题

    • 如何记录权限决策?
    • 如何追溯权限滥用?

Claude Code 用多层权限决策引擎解决了这些问题。


技术原理:多层权限决策架构

整体架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
┌─────────────────────────────────────────────────────────────┐
│ 权限请求 │
│ "执行命令:rm -rf node_modules" │
└─────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ 第 1 层:全局规则 (Global Rules) │
│ - 系统级白名单/黑名单 │
│ - 管理员配置 │
│ - 决策:Allow / Deny / Continue │
└─────────────┬───────────────────────────────────────────────┘
│ Continue

┌─────────────────────────────────────────────────────────────┐
│ 第 2 层:工作区规则 (Workspace Rules) │
│ - 项目级配置 │
│ - 团队约定 │
│ - 决策:Allow / Deny / Continue │
└─────────────┬───────────────────────────────────────────────┘
│ Continue

┌─────────────────────────────────────────────────────────────┐
│ 第 3 层:会话规则 (Session Rules) │
│ - 临时授权 │
│ - 用户偏好 │
│ - 决策:Allow / Deny / Continue │
└─────────────┬───────────────────────────────────────────────┘
│ Continue

┌─────────────────────────────────────────────────────────────┐
│ 第 4 层:AI 分类器 (AI Classifier) │
│ - 意图识别 │
│ - 风险评估 │
│ - 决策:Allow / Deny / Ask (带置信度) │
└─────────────┬───────────────────────────────────────────────┘
│ Ask / Low Confidence

┌─────────────────────────────────────────────────────────────┐
│ 第 5 层:用户确认 (User Confirmation) │
│ - 显示风险信息 │
│ - 用户决策 │
│ - 可选:记住选择 │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ 最终决策:Allow / Deny │
│ - 记录审计日志 │
│ - 更新规则 (如果用户选择记住) │
└─────────────────────────────────────────────────────────────┘

权限规则定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
interface PermissionRule {
// 基本信息
id: string;
name: string;
description?: string;

// 规则范围
scope: 'global' | 'workspace' | 'session';

// 匹配条件
match: {
tool: string; // 工具名称
pattern: string; // 正则或 glob 模式
conditions?: { // 附加条件
agent?: string;
cwd?: string;
timeRange?: string;
user?: string;
};
};

// 决策
action: 'allow' | 'deny' | 'ask';

// 元数据
priority: number; // 优先级 (高优先级先匹配)
createdAt: Date;
createdBy: string;
expiresAt?: Date; // 过期时间

// 审计
audit: {
enabled: boolean;
level: 'none' | 'basic' | 'detailed';
};
}

规则引擎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
class PermissionRuleEngine {
private rules: PermissionRule[] = [];
private ruleIndex: Map<string, PermissionRule[]> = new Map();

// 添加规则
addRule(rule: PermissionRule): void {
this.rules.push(rule);
this.rebuildIndex();
}

// 移除规则
removeRule(ruleId: string): void {
const index = this.rules.findIndex(r => r.id === ruleId);
if (index !== -1) {
this.rules.splice(index, 1);
this.rebuildIndex();
}
}

// 匹配规则
match(request: PermissionRequest): PermissionRule | null {
// 按优先级排序
const sortedRules = this.rules.sort((a, b) => b.priority - a.priority);

for (const rule of sortedRules) {
if (this.matchesRule(request, rule)) {
return rule;
}
}

return null;
}

private matchesRule(
request: PermissionRequest,
rule: PermissionRule
): boolean {
// 检查工具匹配
if (rule.match.tool !== '*' && rule.match.tool !== request.tool) {
return false;
}

// 检查模式匹配
const pattern = new RegExp(rule.match.pattern);
if (!pattern.test(request.input)) {
return false;
}

// 检查附加条件
if (rule.match.conditions) {
const conditions = rule.match.conditions;

if (conditions.agent && conditions.agent !== request.agent) {
return false;
}

if (conditions.cwd && !request.cwd?.includes(conditions.cwd)) {
return false;
}

if (conditions.timeRange) {
const now = new Date();
const [start, end] = conditions.timeRange.split('-');
const currentHour = now.getHours();

if (currentHour < parseInt(start) || currentHour >= parseInt(end)) {
return false;
}
}
}

return true;
}

private rebuildIndex(): void {
// 按工具建立索引,加速查找
this.ruleIndex.clear();

for (const rule of this.rules) {
const tool = rule.match.tool;
if (!this.ruleIndex.has(tool)) {
this.ruleIndex.set(tool, []);
}
this.ruleIndex.get(tool)!.push(rule);
}
}
}

AI 分类器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
class AIPermissionClassifier {
private llm: LLMClient;
private cache: Map<string, ClassificationResult> = new Map();

constructor(llm: LLMClient) {
this.llm = llm;
}

async classify(request: PermissionRequest): Promise<ClassificationResult> {
// 检查缓存
const cacheKey = this.getCacheKey(request);
const cached = this.cache.get(cacheKey);
if (cached && !this.isExpired(cached)) {
return cached;
}

// 构建 Prompt
const prompt = this.buildClassificationPrompt(request);

// 调用 LLM
const response = await this.llm.generate(prompt, {
temperature: 0, // 确定性输出
maxTokens: 500,
});

// 解析结果
const result = this.parseResponse(response.content);

// 缓存结果
this.cache.set(cacheKey, result);

return result;
}

private buildClassificationPrompt(request: PermissionRequest): string {
return `
你是一个 AI 助手权限分类器。请分析以下权限请求的安全性。

请求详情:
- 工具:${request.tool}
- 输入:${request.input}
- 执行者:${request.agent}
- 当前目录:${request.cwd}
- 用户:${request.user}

请考虑以下因素:
1. 这个操作是否有破坏性风险?
2. 是否访问敏感位置 (/etc, /root, 云元数据等)?
3. 是否执行远程代码?
4. 是否可逆?
5. 是否符合常见工作模式?

返回 JSON 格式:
{
"safe": boolean, // 是否安全
"riskLevel": "low|medium|high|critical", // 风险级别
"reason": string, // 判断理由
"confidence": number, // 置信度 (0-1)
"suggestion": "allow|deny|ask" // 建议决策
}
`.trim();
}

private parseResponse(content: string): ClassificationResult {
try {
// 提取 JSON
const jsonMatch = content.match(/\{[\s\S]*\}/);
if (!jsonMatch) {
throw new Error('No JSON found');
}

const result = JSON.parse(jsonMatch[0]);

// 验证字段
if (typeof result.safe !== 'boolean') {
throw new Error('Invalid safe field');
}
if (typeof result.confidence !== 'number') {
throw new Error('Invalid confidence field');
}

return result;

} catch (error) {
// 解析失败,返回保守结果
return {
safe: false,
riskLevel: 'medium',
reason: 'Failed to parse classifier response',
confidence: 0.5,
suggestion: 'ask',
};
}
}

private getCacheKey(request: PermissionRequest): string {
return `${request.tool}:${request.input}:${request.agent}`;
}

private isExpired(result: ClassificationResult): boolean {
// 缓存 5 分钟
return Date.now() - result.cachedAt! > 5 * 60 * 1000;
}
}

interface ClassificationResult {
safe: boolean;
riskLevel: 'low' | 'medium' | 'high' | 'critical';
reason: string;
confidence: number;
suggestion: 'allow' | 'deny' | 'ask';
cachedAt?: number;
}

权限决策器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
class PermissionDecider {
private ruleEngine: PermissionRuleEngine;
private classifier: AIPermissionClassifier;
private auditLogger: AuditLogger;

constructor(config: PermissionDeciderConfig) {
this.ruleEngine = new PermissionRuleEngine();
this.classifier = new AIPermissionClassifier(config.llm);
this.auditLogger = new AuditLogger(config.auditPath);
}

async decide(request: PermissionRequest): Promise<PermissionDecision> {
const startTime = Date.now();

try {
// ========== 第 1 层:全局规则 ==========
const globalRule = await this.matchGlobalRule(request);
if (globalRule) {
const decision = this.createDecision(globalRule.action, 'global_rule', globalRule);
await this.auditLogger.log(request, decision);
return decision;
}

// ========== 第 2 层:工作区规则 ==========
const workspaceRule = await this.matchWorkspaceRule(request);
if (workspaceRule) {
const decision = this.createDecision(workspaceRule.action, 'workspace_rule', workspaceRule);
await this.auditLogger.log(request, decision);
return decision;
}

// ========== 第 3 层:会话规则 ==========
const sessionRule = await this.matchSessionRule(request);
if (sessionRule) {
const decision = this.createDecision(sessionRule.action, 'session_rule', sessionRule);
await this.auditLogger.log(request, decision);
return decision;
}

// ========== 第 4 层:AI 分类器 ==========
const classification = await this.classifier.classify(request);

if (classification.confidence > 0.85) {
// 高置信度,自动决策
const action = classification.suggestion === 'allow' ? 'allow' : 'deny';
const decision = this.createDecision(action, 'classifier', null, classification);
await this.auditLogger.log(request, decision);
return decision;
}

// ========== 第 5 层:用户确认 ==========
const userDecision = await this.requestUserConfirmation(request, classification);

const decision = this.createDecision(
userDecision.approved ? 'allow' : 'deny',
'user',
null,
classification,
userDecision
);

// 如果用户选择记住,添加规则
if (userDecision.remember) {
await this.createRuleFromDecision(request, userDecision);
}

await this.auditLogger.log(request, decision);
return decision;

} catch (error) {
// 错误情况下,默认拒绝
const decision: PermissionDecision = {
decision: 'deny',
source: 'error',
reason: `Permission system error: ${error.message}`,
timestamp: new Date(),
};

await this.auditLogger.log(request, decision);
return decision;
}
}

private createDecision(
action: 'allow' | 'deny',
source: string,
rule: PermissionRule | null,
classification?: ClassificationResult,
userDecision?: UserDecision
): PermissionDecision {
return {
decision: action,
source,
reason: rule?.description || classification?.reason || userDecision?.reason,
confidence: classification?.confidence,
riskLevel: classification?.riskLevel,
timestamp: new Date(),
ruleId: rule?.id,
};
}

private async requestUserConfirmation(
request: PermissionRequest,
classification: ClassificationResult
): Promise<UserDecision> {
// 显示确认对话框
const prompt = `
权限请求确认

操作:${request.tool}
详情:${request.input}
执行者:${request.agent}

风险评估:
- 风险级别:${classification.riskLevel}
- 判断理由:${classification.reason}
- 置信度:${Math.round(classification.confidence * 100)}%

是否允许此操作?
[允许] [拒绝] [允许并记住] [拒绝并记住]
`.trim();

// 等待用户响应
const response = await this.waitForUserResponse(prompt);

return {
approved: response.action === 'allow',
remember: response.remember,
reason: response.reason,
};
}

private async createRuleFromDecision(
request: PermissionRequest,
userDecision: UserDecision
): Promise<void> {
const rule: PermissionRule = {
id: generateRuleId(),
name: `Auto-created from user decision`,
scope: userDecision.scope || 'workspace',
match: {
tool: request.tool,
pattern: this.extractPattern(request.input),
conditions: {
agent: request.agent,
},
},
action: userDecision.approved ? 'allow' : 'deny',
priority: 50,
createdAt: new Date(),
createdBy: request.user,
audit: {
enabled: true,
level: 'basic',
},
};

if (userDecision.scope === 'global') {
await this.ruleEngine.addRule(rule);
} else {
await this.saveWorkspaceRule(rule);
}
}
}

interface PermissionDecision {
decision: 'allow' | 'deny';
source: 'global_rule' | 'workspace_rule' | 'session_rule' | 'classifier' | 'user' | 'error';
reason?: string;
confidence?: number;
riskLevel?: string;
timestamp: Date;
ruleId?: string;
}

interface UserDecision {
approved: boolean;
remember: boolean;
scope?: 'global' | 'workspace' | 'session';
reason?: string;
}

审计日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
class AuditLogger {
private db: Database;
private logPath: string;

constructor(logPath: string) {
this.logPath = logPath;
this.db = new Database(path.join(logPath, 'audit.db'));
this.initSchema();
}

private initSchema(): void {
this.db.exec(`
CREATE TABLE IF NOT EXISTS permission_logs (
id TEXT PRIMARY KEY,
timestamp TEXT,
tool TEXT,
input TEXT,
agent TEXT,
user TEXT,
cwd TEXT,
decision TEXT,
source TEXT,
reason TEXT,
confidence REAL,
risk_level TEXT,
rule_id TEXT,
duration_ms INTEGER
)
`);
}

async log(request: PermissionRequest, decision: PermissionDecision): Promise<void> {
await this.db.run(`
INSERT INTO permission_logs (
id, timestamp, tool, input, agent, user, cwd,
decision, source, reason, confidence, risk_level, rule_id, duration_ms
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`, [
generateId(),
new Date().toISOString(),
request.tool,
request.input,
request.agent,
request.user,
request.cwd,
decision.decision,
decision.source,
decision.reason,
decision.confidence,
decision.riskLevel,
decision.ruleId,
Date.now() - decision.timestamp.getTime(),
]);
}

async query(filter: AuditFilter): Promise<AuditLog[]> {
let query = 'SELECT * FROM permission_logs';
const params: any[] = [];
const conditions: string[] = [];

if (filter.tool) {
conditions.push('tool = ?');
params.push(filter.tool);
}

if (filter.decision) {
conditions.push('decision = ?');
params.push(filter.decision);
}

if (filter.source) {
conditions.push('source = ?');
params.push(filter.source);
}

if (filter.startTime) {
conditions.push('timestamp >= ?');
params.push(filter.startTime.toISOString());
}

if (filter.endTime) {
conditions.push('timestamp <= ?');
params.push(filter.endTime.toISOString());
}

if (conditions.length > 0) {
query += ' WHERE ' + conditions.join(' AND ');
}

query += ' ORDER BY timestamp DESC';

if (filter.limit) {
query += ` LIMIT ${filter.limit}`;
}

const rows = await this.db.all(query, params);
return rows.map(row => this.rowToLog(row));
}

async getStatistics(timeRange: string): Promise<AuditStatistics> {
const startTime = this.parseTimeRange(timeRange);

const stats = await this.db.get(`
SELECT
COUNT(*) as total,
SUM(CASE WHEN decision = 'allow' THEN 1 ELSE 0 END) as allowed,
SUM(CASE WHEN decision = 'deny' THEN 1 ELSE 0 END) as denied,
AVG(confidence) as avg_confidence
FROM permission_logs
WHERE timestamp >= ?
`, [startTime.toISOString()]);

// 按来源统计
const bySource = await this.db.all(`
SELECT source, COUNT(*) as count
FROM permission_logs
WHERE timestamp >= ?
GROUP BY source
`, [startTime.toISOString()]);

// 按工具统计
const byTool = await this.db.all(`
SELECT tool, COUNT(*) as count
FROM permission_logs
WHERE timestamp >= ?
GROUP BY tool
ORDER BY count DESC
LIMIT 10
`, [startTime.toISOString()]);

return {
total: stats.total,
allowed: stats.allowed,
denied: stats.denied,
allowedRate: stats.allowed / stats.total,
avgConfidence: stats.avg_confidence,
bySource: Object.fromEntries(bySource.map(r => [r.source, r.count])),
byTool: byTool.map(r => ({ tool: r.tool, count: r.count })),
};
}

private rowToLog(row: any): AuditLog {
return {
id: row.id,
timestamp: new Date(row.timestamp),
tool: row.tool,
input: row.input,
agent: row.agent,
user: row.user,
cwd: row.cwd,
decision: row.decision,
source: row.source,
reason: row.reason,
confidence: row.confidence,
riskLevel: row.risk_level,
ruleId: row.rule_id,
durationMs: row.duration_ms,
};
}
}

设计思想:为什么这样设计

思想 1:多层防御

问题: 单层规则容易被绕过或误判。

解决: 多层决策。

1
2
3
全局规则 → 工作区规则 → 会话规则 → AI 分类器 → 用户确认
↓ ↓ ↓ ↓ ↓
系统级 项目级 临时 智能 最终

设计智慧:

每层都有不同的视角,组合起来更可靠。

思想 2:智能降级

问题: AI 分类器可能出错。

解决: 置信度阈值 + 人工兜底。

1
2
3
4
5
6
7
if (classification.confidence > 0.85) {
// 高置信度:自动决策
return classification.suggestion;
} else {
// 低置信度:用户确认
return await requestUserConfirmation();
}

阈值选择:

置信度 决策 说明
> 0.85 自动 AI 很有把握
0.5-0.85 用户确认 AI 不太确定
< 0.5 拒绝 AI 认为危险

思想 3:规则可学习

问题: 规则配置繁琐。

解决: 从用户决策学习。

1
2
3
4
5
6
7
8
// 用户选择"允许并记住"
if (userDecision.remember) {
// 自动创建规则
const rule = createRuleFromDecision(request, userDecision);
await ruleEngine.addRule(rule);
}

// 下次同样请求直接匹配规则

学习效果:

1
2
3
4
第 1 次:npm install → 用户确认 → 选择"允许并记住"
第 2 次:npm install → 匹配规则 → 自动允许
第 3 次:npm install → 匹配规则 → 自动允许
...

思想 4:审计可追溯

问题: 权限滥用无法追溯。

解决: 完整审计日志。

1
2
3
4
5
6
7
8
9
10
11
12
interface AuditLog {
timestamp: Date;
tool: string;
input: string;
agent: string;
user: string;
decision: 'allow' | 'deny';
source: string;
reason: string;
confidence: number;
durationMs: number;
}

审计查询:

1
2
3
4
5
6
7
8
# 查看所有拒绝的请求
openclaw audit query --decision deny

# 查看特定工具的决策
openclaw audit query --tool bash --limit 50

# 查看统计
openclaw audit stats --range 7d

思想 5:上下文感知

问题: 同样命令在不同上下文风险不同。

解决: 上下文感知决策。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 同样命令:rm -rf node_modules

上下文 1
- cwd: /home/johnzok/project
- agent: devops
→ 允许 (常见操作)

上下文 2
- cwd: /
- agent: unknown
→ 拒绝 (危险位置)

上下文 3
- cwd: /etc
- agent: devops
→ 询问 (敏感位置)

解决方案:完整实现详解

PermissionTool 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
export class PermissionTool extends Tool {
name = 'permission';
description = '管理权限规则和决策';

inputSchema = {
type: 'object',
properties: {
action: {
type: 'string',
enum: ['list', 'add', 'remove', 'decide', 'audit'],
description: '操作类型',
},
rule: {
type: 'object',
description: '规则定义',
},
request: {
type: 'object',
description: '权限请求',
},
},
required: ['action'],
};

private decider: PermissionDecider;
private auditLogger: AuditLogger;

async execute(input: PermissionInput, context: ToolContext): Promise<ToolResult> {
switch (input.action) {
case 'list':
return this.listRules();

case 'add':
return this.addRule(input.rule!);

case 'remove':
return this.removeRule(input.rule!.id);

case 'decide':
return this.decide(input.request!);

case 'audit':
return this.queryAudit(input.filter);

default:
return {
success: false,
error: `Unknown action: ${input.action}`,
};
}
}

private async decide(request: PermissionRequest): Promise<ToolResult> {
const decision = await this.decider.decide(request);

return {
success: true,
decision: decision.decision,
source: decision.source,
reason: decision.reason,
confidence: decision.confidence,
riskLevel: decision.riskLevel,
};
}
}

权限规则配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# ~/.openclaw/config/permissions.yaml

# 全局规则
global_rules:
# 永远拒绝的危险操作
- id: deny-rm-root
name: 禁止删除根目录
match:
tool: bash
pattern: "rm\\s+(-[rf]+\\s+)*/"
action: deny
priority: 100
audit:
enabled: true
level: detailed

- id: deny-dd
name: 禁止 dd 命令
match:
tool: bash
pattern: "dd\\s+.*"
action: deny
priority: 100

- id: deny-curl-pipe
name: 禁止下载执行
match:
tool: bash
pattern: "(curl|wget)\\s+[^|]*\\|\\s*(ba)?sh"
action: deny
priority: 100

# 工作区规则
workspace_rules:
# 开发相关允许
- id: allow-npm-install
name: 允许 npm install
match:
tool: bash
pattern: "npm\\s+install.*"
action: allow
priority: 50
conditions:
cwd: "**/project/**"

- id: allow-git-status
name: 允许 git 只读操作
match:
tool: bash
pattern: "git\\s+(status|log|diff|show).*"
action: allow
priority: 50

# AI 分类器配置
classifier:
enabled: true
confidence_threshold: 0.85
cache_ttl: 300 # 5 分钟

# 审计配置
audit:
enabled: true
path: ~/.openclaw/logs/audit
retention_days: 90

OpenClaw 最佳实践

实践 1:查看权限规则

1
2
3
4
5
6
7
8
# 查看所有规则
openclaw run permission --action list

# 查看全局规则
openclaw run permission --action list --scope global

# 查看工作区规则
openclaw run permission --action list --scope workspace

实践 2:添加权限规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 添加允许规则
openclaw run permission --action add --rule '{
"id": "allow-npm-test",
"name": "允许 npm test",
"match": {
"tool": "bash",
"pattern": "npm\\s+test.*"
},
"action": "allow",
"priority": 50
}'

# 添加拒绝规则
openclaw run permission --action add --rule '{
"id": "deny-prod-delete",
"name": "禁止删除生产环境",
"match": {
"tool": "bash",
"pattern": ".*production.*delete.*"
},
"action": "deny",
"priority": 100
}'

实践 3:查询审计日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 查看所有权限决策
openclaw run permission --action audit --limit 50

# 查看拒绝的决策
openclaw run permission --action audit --decision deny

# 查看统计
openclaw run permission --action audit --stats --range 7d

# 输出:
权限决策统计 (最近 7 天):
─────────────────────────────────────
总决策数:1250
允许:1180 (94.4%)
拒绝:70 (5.6%)

平均置信度:0.87

决策来源:
- 规则匹配:850 (68%)
- AI 分类器:320 (25.6%)
- 用户确认:80 (6.4%)

Top 工具:
- bash: 800
- file_edit: 250
- file_read: 150
- web_fetch: 50

实践 4:测试权限决策

1
2
3
4
5
6
7
8
9
10
11
12
13
# 测试特定请求的决策
openclaw run permission --action decide --request '{
"tool": "bash",
"input": "npm install",
"agent": "devops",
"cwd": "/home/johnzok/project"
}'

# 输出:
决策:allow
来源:workspace_rule
规则:allow-npm-install
置信度:1.0

实践 5:导出审计报告

1
2
3
4
5
6
7
8
# 导出 CSV
openclaw run permission --action audit --export csv --output audit-report.csv

# 导出 JSON
openclaw run permission --action audit --export json --output audit-report.json

# 生成周报
openclaw run permission --action audit --report weekly --email admin@company.com

总结

核心要点

  1. 多层决策 - 全局 → 工作区 → 会话 → AI → 用户
  2. 智能降级 - 置信度阈值 + 人工兜底
  3. 规则学习 - 从用户决策自动创建规则
  4. 审计追溯 - 完整记录所有决策
  5. 上下文感知 - 根据上下文调整决策

设计智慧

好的权限系统让用户”无感安全”。

Claude Code 的权限系统设计告诉我们:

  • 多层决策比单层更可靠
  • AI 辅助可以减少用户确认
  • 规则可以从用户行为学习
  • 审计日志是安全的基础

决策流程

1
2
3
请求 → 规则匹配 → AI 分类 → 用户确认 → 决策
↓ ↓ ↓
68% 25.6% 6.4%

下一步

  • 配置权限规则
  • 启用 AI 分类器
  • 设置审计日志
  • 定期审查规则

系列文章:

  • [1] Bash 命令执行的安全艺术 (已发布)
  • [2] 差异编辑的设计艺术 (已发布)
  • [3] 文件搜索的底层原理 (已发布)
  • [4] 多 Agent 协作的架构设计 (已发布)
  • [5] 技能系统的设计哲学 (已发布)
  • [6] MCP 协议集成的完整指南 (已发布)
  • [7] 后台任务管理的完整方案 (已发布)
  • [8] Web 抓取的 SSRF 防护设计 (已发布)
  • [9] 多层权限决策引擎设计 (本文)
  • [10] 插件生命周期的设计智慧 (待发布)

上一篇: Claude Code 源码解析 (8):Web 抓取的 SSRF 防护设计


关于作者: John,OpenClaw 平台开发者,专注 AI 助手架构设计与实现。