claw-code permissionSetup 비교 반영: 위험 자동허용 가드 + 회귀 테스트 추가
Some checks failed
Release Gate / gate (push) Has been cancelled

- 전역 권한이 Auto일 때 고위험 도구(process, spawn_agent, snippet_runner)는 자동 허용을 ask로 강등

- AgentContext 권한 계산 경로에 dangerous auto guard를 통합하여 우발적 무승인 실행 방지

- OperationModePolicyTests에 guard 동작 회귀 테스트 2건 추가

- 패리티 문서에 permissionSetup 기반 보강 항목 추가

- 로드맵/패리티 문서 테스트 수치 동기화: 374/374

- 검증: dotnet build 경고0 오류0, ParityBenchmark 12/12, ReplayStability 12/12, 전체 테스트 374/374
This commit is contained in:
2026-04-03 21:07:15 +09:00
parent b30c5f124e
commit 72f307631d
5 changed files with 74 additions and 11 deletions

View File

@@ -102,4 +102,40 @@ public class OperationModePolicyTests
allowed.Should().BeTrue();
askCalled.Should().BeFalse();
}
[Fact]
public void AgentContext_GetEffectiveToolPermission_DowngradesDangerousAutoToolWhenGlobalAuto()
{
var context = new AgentContext
{
Permission = "Auto",
ToolPermissions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["process"] = "auto",
}
};
context.GetEffectiveToolPermission("process", "git status").Should().Be("ask");
context.GetEffectiveToolPermission("file_read", @"E:\work\README.md").Should().Be("Auto");
}
[Fact]
public async Task AgentContext_CheckToolPermissionAsync_DangerousAutoToolRequiresPromptInGlobalAuto()
{
var askCalled = false;
var context = new AgentContext
{
OperationMode = OperationModePolicy.ExternalMode,
Permission = "Auto",
AskPermission = (_, _) =>
{
askCalled = true;
return Task.FromResult(false);
}
};
var allowed = await context.CheckToolPermissionAsync("process", "git status");
allowed.Should().BeFalse();
askCalled.Should().BeTrue();
}
}

View File

@@ -92,6 +92,12 @@ public class AgentContext
"process", "build_run", "git_tool", "http_tool", "open_external", "snippet_runner",
"spawn_agent", "test_loop",
};
private static readonly HashSet<string> DangerousAutoTools = new(StringComparer.OrdinalIgnoreCase)
{
"process",
"spawn_agent",
"snippet_runner",
};
private readonly object _permissionLock = new();
private readonly HashSet<string> _approvedPermissionCache = new(StringComparer.OrdinalIgnoreCase);
@@ -191,22 +197,26 @@ public class AgentContext
public string GetEffectiveToolPermission(string toolName, string? target)
{
toolName ??= "";
var normalizedToolName = toolName.Trim();
if (TryResolvePatternPermission(toolName, target, out var patternPermission))
return PermissionModeCatalog.NormalizeToolOverride(patternPermission);
return ApplyDangerousAutoGuard(normalizedToolName, PermissionModeCatalog.NormalizeToolOverride(patternPermission));
if (ToolPermissions.TryGetValue(toolName, out var toolPerm) &&
!string.IsNullOrWhiteSpace(toolPerm))
return PermissionModeCatalog.NormalizeToolOverride(toolPerm);
return ApplyDangerousAutoGuard(normalizedToolName, PermissionModeCatalog.NormalizeToolOverride(toolPerm));
if (ToolPermissions.TryGetValue("*", out var wildcardPerm) &&
!string.IsNullOrWhiteSpace(wildcardPerm))
return PermissionModeCatalog.NormalizeToolOverride(wildcardPerm);
return ApplyDangerousAutoGuard(normalizedToolName, PermissionModeCatalog.NormalizeToolOverride(wildcardPerm));
if (ToolPermissions.TryGetValue("default", out var defaultPerm) &&
!string.IsNullOrWhiteSpace(defaultPerm))
return PermissionModeCatalog.NormalizeToolOverride(defaultPerm);
return ApplyDangerousAutoGuard(normalizedToolName, PermissionModeCatalog.NormalizeToolOverride(defaultPerm));
return SensitiveTools.Contains(toolName)
var fallback = SensitiveTools.Contains(toolName)
? PermissionModeCatalog.NormalizeGlobalMode(Permission)
: PermissionModeCatalog.Auto;
return ApplyDangerousAutoGuard(normalizedToolName, fallback);
}
public async Task<bool> CheckToolPermissionAsync(string toolName, string target)
@@ -297,6 +307,19 @@ public class AgentContext
.Replace("\\?", ".") + "$";
return Regex.IsMatch(input, regex, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
}
private string ApplyDangerousAutoGuard(string toolName, string permission)
{
if (string.IsNullOrWhiteSpace(toolName))
return permission;
if (PermissionModeCatalog.IsAuto(permission)
&& PermissionModeCatalog.IsAuto(Permission)
&& DangerousAutoTools.Contains(toolName))
return "ask";
return permission;
}
}
/// <summary>에이전트 이벤트 (UI 표시용).</summary>