
AI系统的安全性是企业级应用的核心关注点。Claude Code 的权限系统采用多层次、可配置的设计,既保证了灵活性又确保了安全性。本篇将深入剖析其实现细节。

Claude Code 的权限系统围绕以下核心原则设计:
原则 | 描述 | 实现方式 |
|---|---|---|
默认拒绝 | 未明确允许的操作默认拒绝 | 白名单机制 |
最小权限 | 只授予必要的最小权限 | 精细规则匹配 |
分级控制 | 不同操作需要不同级别的确认 | 权限模式系统 |
可审计 | 所有权限决策可追溯 | 审计日志 |
可覆盖 | 支持企业策略强制覆盖 | Policy优先级 |

权限层次
├── 全局权限模式 (Permission Mode)
│ ├── default - 交互式确认
│ ├── acceptEdits - 自动接受编辑
│ ├── plan - 计划模式(受限)
│ ├── auto - 自动批准
│ └── bypassPermissions - 跳过所有检查
│
├── 工具权限规则 (Tool Permission Rules)
│ ├── allow - 允许规则
│ ├── deny - 拒绝规则
│ └── ask - 需要确认
│
├── 路径权限 (Path Permissions)
│ ├── 允许的目录
│ └── 禁止的目录
│
└── 操作权限 (Operation Permissions)
├── 读操作
├── 写操作
└── 执行操作
// utils/permissions/PermissionMode.ts
export const PERMISSION_MODES = [
'default', // 默认:交互式确认
'acceptEdits', // 自动接受编辑
'plan', // 计划模式
'auto', // 自动批准
'bypassPermissions', // 跳过所有检查
] as const
export type PermissionMode = (typeof PERMISSION_MODES)[number]// 交互式确认模式
const defaultMode: PermissionModeHandler = {
name: 'default',
checkPermission: async (request, context) => {
// 1. 检查是否有匹配的规则
const rule = findMatchingRule(request, context.rules)
if (rule?.type === 'allow') {
return { allowed: true }
}
if (rule?.type === 'deny') {
return { allowed: false, reason: rule.reason }
}
// 2. 没有规则,请求用户确认
return {
allowed: false,
needsConfirmation: true,
message: generateConfirmationMessage(request),
}
},
}// 自动接受编辑模式
const acceptEditsMode: PermissionModeHandler = {
name: 'acceptEdits',
checkPermission: async (request, context) => {
// 编辑操作自动批准
if (isEditOperation(request)) {
return { allowed: true }
}
// 其他操作走默认流程
return defaultMode.checkPermission(request, context)
},
}// 计划模式(受限)
const planMode: PermissionModeHandler = {
name: 'plan',
checkPermission: async (request, context) => {
// 只允许读操作
const allowedInPlan = ['Read', 'Glob', 'Grep', 'LSP']
if (allowedInPlan.includes(request.tool)) {
return defaultMode.checkPermission(request, context)
}
// 写操作需要特殊确认
return {
allowed: false,
needsConfirmation: true,
message: 'Write operations require confirmation in plan mode',
}
},
}// 自动批准模式
const autoMode: PermissionModeHandler = {
name: 'auto',
checkPermission: async (request, context) => {
// 检查deny规则
const denyRule = findDenyRule(request, context.rules)
if (denyRule) {
return { allowed: false, reason: denyRule.reason }
}
// 其他自动批准
return { allowed: true }
},
}// 跳过所有检查(需要安全环境)
const bypassMode: PermissionModeHandler = {
name: 'bypassPermissions',
checkPermission: async (request, context) => {
// 验证环境安全性
if (!isSecureEnvironment(context)) {
throw new Error(
'bypassPermissions can only be used in sandboxed environment'
)
}
// 允许所有操作
return { allowed: true }
},
}
function isSecureEnvironment(context: ToolUseContext): boolean {
// 检查是否在Docker/沙箱中
const isDocker = process.env.IS_DOCKER === '1'
const isSandbox = process.env.IS_SANDBOX === '1'
const isBubblewrap = process.env.CLAUDE_CODE_BUBBLEWRAP === '1'
// 检查是否有网络访问
const hasInternet = checkInternetAccess()
// 只允许无网络的沙箱环境
return (isDocker || isSandbox || isBubblewrap) && !hasInternet
}// 权限规则语法
type PermissionRuleString =
| `${ToolName}` // "Bash"
| `${ToolName}(${Pattern})` // "Bash(npm:*)"
| `${ToolName}(${Pattern}:${Pattern})` // "Read(**):Write(src/**)"
// 规则对象
interface PermissionRuleObject {
rule: string
source?: string // 规则来源
conditions?: PermissionCondition[]
}
// 完整规则类型
type PermissionRule = string | PermissionRuleObject// utils/permissions/ruleMatcher.ts
export function matchRule(
request: PermissionRequest,
rule: PermissionRule
): PermissionMatchResult {
const ruleStr = typeof rule === 'string' ? rule : rule.rule
// 解析规则
const parsed = parseRuleString(ruleStr)
// 1. 检查工具名称
if (parsed.tool !== request.tool && parsed.tool !== '*') {
return { matched: false }
}
// 2. 检查参数模式
if (parsed.pattern) {
const paramMatch = matchPattern(
getRelevantParam(request, parsed.paramType),
parsed.pattern
)
if (!paramMatch) {
return { matched: false }
}
}
// 3. 检查条件
if (typeof rule === 'object' && rule.conditions) {
const conditionsMet = rule.conditions.every(cond =>
checkCondition(cond, request)
)
if (!conditionsMet) {
return { matched: false }
}
}
return { matched: true, rule }
}// 模式匹配实现
function matchPattern(value: string, pattern: string): boolean {
// 通配符模式
if (pattern.includes('*')) {
const regex = patternToRegex(pattern)
return regex.test(value)
}
// 精确匹配
return value === pattern
}
// 模式转正则
function patternToRegex(pattern: string): RegExp {
const escaped = pattern
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
.replace(/\*/g, '.*')
.replace(/\?/g, '.')
return new RegExp(`^${escaped}$`)
}
// 示例
matchPattern('npm install', 'npm:*') // true
matchPattern('npm run build', 'npm:*') // true
matchPattern('yarn install', 'npm:*') // false
matchPattern('/src/file.ts', '**/*.ts') // true// 规则优先级处理
function resolvePermission(
request: PermissionRequest,
rules: PermissionRules
): PermissionDecision {
// 1. deny 规则最高优先级
for (const rule of rules.deny ?? []) {
if (matchRule(request, rule).matched) {
return {
allowed: false,
reason: `Denied by rule: ${rule}`,
source: getRuleSource(rule),
}
}
}
// 2. ask 规则需要确认
for (const rule of rules.ask ?? []) {
if (matchRule(request, rule).matched) {
return {
allowed: false,
needsConfirmation: true,
message: `Requires confirmation: ${rule}`,
source: getRuleSource(rule),
}
}
}
// 3. allow 规则允许
for (const rule of rules.allow ?? []) {
if (matchRule(request, rule).matched) {
return {
allowed: true,
source: getRuleSource(rule),
}
}
}
// 4. 无匹配规则,走默认流程
return {
allowed: false,
needsConfirmation: true,
message: 'No matching permission rule',
}
}
// services/classifier/transcriptClassifier.ts
export class DangerousOperationClassifier {
// 分类操作的危险级别
async classify(request: PermissionRequest): Promise<DangerLevel> {
const prompt = this.buildClassificationPrompt(request)
const response = await this.llm.classify(prompt)
return {
level: response.level, // 'safe' | 'caution' | 'dangerous'
reason: response.reason,
confidence: response.confidence,
}
}
private buildClassificationPrompt(request: PermissionRequest): string {
return `
Analyze this operation and classify its danger level:
Tool: ${request.tool}
Input: ${JSON.stringify(request.input, null, 2)}
Working Directory: ${request.cwd}
Classify as:
- safe: Read-only, no side effects
- caution: Moderate risk, reversible
- dangerous: High risk, potentially irreversible
Provide:
1. Danger level
2. Reasoning
3. Confidence (0-1)
`
}
}// 内置分类规则
const CLASSIFICATION_RULES: ClassificationRule[] = [
// 文件操作
{
pattern: /rm\s+-rf/,
level: 'dangerous',
reason: 'Recursive force delete is irreversible',
},
{
pattern: /sudo/,
level: 'dangerous',
reason: 'Elevated privileges required',
},
{
pattern: />\s*\/dev\/sd/,
level: 'dangerous',
reason: 'Direct disk write',
},
// 网络操作
{
pattern: /curl.*\|\s*bash/,
level: 'dangerous',
reason: 'Remote code execution',
},
{
pattern: /wget.*\|\s*sh/,
level: 'dangerous',
reason: 'Remote code execution',
},
// 数据库操作
{
pattern: /DROP\s+(DATABASE|TABLE)/i,
level: 'dangerous',
reason: 'Irreversible data loss',
},
]
// components/PermissionDialog.tsx
export function PermissionDialog({
request,
onDecision,
}: PermissionDialogProps) {
return (
<Box flexDirection="column" borderStyle="round" borderColor="yellow">
<Text bold color="yellow">
Permission Required
</Text>
<Box marginTop={1}>
<Text>Tool: </Text>
<Text bold>{request.tool}</Text>
</Box>
<Box>
<Text>Operation: </Text>
<Text>{formatOperation(request.input)}</Text>
</Box>
{request.riskAnalysis && (
<Box marginTop={1}>
<Text color="red">
Risk: {request.riskAnalysis.reason}
</Text>
</Box>
)}
<Box marginTop={1}>
<Text dimColor>Options:</Text>
</Box>
<Text> [y] Yes, allow this operation</Text>
<Text> [n] No, deny this operation</Text>
<Text> [a] Always allow this type of operation</Text>
<Text> [e] Edit the operation first</Text>
<Text> [Esc] Cancel</Text>
<InputHandler onKey={handleKey} />
</Box>
)
}// 处理用户决策
async function handlePermissionDecision(
decision: PermissionDecision,
request: PermissionRequest
): Promise<void> {
switch (decision) {
case 'allow-once':
// 仅本次允许
await executeWithPermission(request)
break
case 'allow-always':
// 添加到允许规则
await addPermissionRule({
type: 'allow',
rule: generateRuleFromRequest(request),
source: 'user',
})
await executeWithPermission(request)
break
case 'deny':
// 拒绝
logPermissionDenied(request)
break
case 'edit':
// 编辑操作
const editedInput = await editOperation(request.input)
request.input = editedInput
// 重新检查权限
await checkAndExecute(request)
break
}
}
// utils/git/permissions.ts
export function checkGitOperation(
operation: GitOperation,
context: ToolUseContext
): PermissionResult {
// 危险的Git操作
const dangerousOperations = [
'push --force',
'reset --hard',
'clean -fd',
'checkout -- .',
]
if (dangerousOperations.some(op => operation.command.includes(op))) {
return {
allowed: false,
needsConfirmation: true,
message: `Dangerous Git operation: ${operation.command}`,
riskLevel: 'high',
}
}
// 检查是否在受保护分支
const protectedBranches = context.settings.protectedBranches ?? ['main', 'master']
const currentBranch = getCurrentBranch()
if (protectedBranches.includes(currentBranch)) {
if (operation.type === 'force-push' || operation.type === 'reset') {
return {
allowed: false,
needsConfirmation: true,
message: `Operation on protected branch: ${currentBranch}`,
riskLevel: 'high',
}
}
}
return { allowed: true }
}// services/audit/permissionAudit.ts
export function logPermissionDecision(
request: PermissionRequest,
decision: PermissionDecision
): void {
const auditEntry: AuditEntry = {
timestamp: new Date().toISOString(),
sessionId: request.sessionId,
// 请求信息
tool: request.tool,
input: sanitizeInput(request.input),
// 决策信息
decision: decision.type,
reason: decision.reason,
source: decision.source, // 'user' | 'rule' | 'auto'
// 上下文
cwd: request.cwd,
userId: request.userId,
}
// 写入审计日志
appendAuditLog(auditEntry)
// 发送到分析服务
logEvent('permission_decision', auditEntry)
}// 生成权限审计报告
export function generateAuditReport(
sessionId: SessionId
): PermissionAuditReport {
const entries = getAuditEntries(sessionId)
return {
totalOperations: entries.length,
byDecision: {
allowed: entries.filter(e => e.decision === 'allow').length,
denied: entries.filter(e => e.decision === 'deny').length,
confirmed: entries.filter(e => e.decision === 'confirmed').length,
},
byTool: groupBy(entries, 'tool'),
highRiskOperations: entries.filter(e => e.riskLevel === 'high'),
// 时间分布
timeline: generateTimeline(entries),
}
}
安全边界层次
├── 输入验证层
│ ├── Schema验证
│ ├── 参数清洗
│ └── 路径规范化
│
├── 权限检查层
│ ├── 模式检查
│ ├── 规则匹配
│ └── AI分类
│
├── 执行隔离层
│ ├── 沙箱执行
│ ├── 资源限制
│ └── 超时控制
│
└── 审计追踪层
├── 操作日志
├── 变更追踪
└── 异常告警// sandbox/executor.ts
export async function executeInSandbox<T>(
operation: () => Promise<T>,
options: SandboxOptions
): Promise<T> {
// 1. 创建隔离环境
const sandbox = await createSandbox(options)
try {
// 2. 设置资源限制
sandbox.setMemoryLimit(options.memoryLimit ?? '512M')
sandbox.setCpuLimit(options.cpuLimit ?? '50%')
sandbox.setTimeout(options.timeout ?? 30000)
// 3. 执行操作
const result = await sandbox.run(operation)
return result
} finally {
// 4. 清理环境
await sandbox.cleanup()
}
}第6篇:LLM集成 - API客户端与流式响应 - 深入理解 Claude Code 如何与 Claude API 高效交互。