권한 체계를 사내 모드 기준으로 정리하고 실행 단위 승인 범위를 바로잡음
사내 모드에서 process/build_run/open_external 경로의 외부 접근 차단 범위를 강화했습니다. http_tool과 외부 URI 차단에 더해 curl, Invoke-WebRequest 같은 네트워크성 명령과 build_run custom 실행을 내부 정책으로 막아 실제 동작이 정책 선언과 더 가깝게 맞춰지도록 했습니다. ChatWindow의 '이번 실행 동안 허용' 승인 규칙을 run-scope로 변경했습니다. 탭 실행 시작과 종료 시 승인 캐시를 초기화하고 같은 실행 안에서만 동일 범위 접근을 재질문 없이 재사용하도록 정리해 창 수명 동안 규칙이 남던 문제를 줄였습니다. 권한 건너뛰기 관련 UI/상태 문구를 실제 동작과 맞췄고, OperationModePolicyTests·OperationModeReadinessTests·AgentLoopE2ETests·LlmOperationModeTests를 통해 권한 정책과 사내 모드 차단 회귀를 검증했습니다. dotnet build 경고 0 / 오류 0, 권한 관련 테스트 49건 통과를 확인했습니다.
This commit is contained in:
@@ -3,6 +3,7 @@ using AxCopilot.Services;
|
||||
using AxCopilot.Services.Agent;
|
||||
using FluentAssertions;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Services;
|
||||
@@ -29,9 +30,20 @@ public class OperationModePolicyTests
|
||||
{
|
||||
OperationModePolicy.IsBlockedAgentToolInInternalMode("http_tool", "https://example.com").Should().BeTrue();
|
||||
OperationModePolicy.IsBlockedAgentToolInInternalMode("open_external", "https://example.com").Should().BeTrue();
|
||||
OperationModePolicy.IsBlockedAgentToolInInternalMode("open_external", "mailto:admin@example.com").Should().BeTrue();
|
||||
OperationModePolicy.IsBlockedAgentToolInInternalMode("open_external", @"E:\work\report.html").Should().BeFalse();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("curl https://example.com", true)]
|
||||
[InlineData("powershell Invoke-WebRequest https://example.com", true)]
|
||||
[InlineData("git status", false)]
|
||||
[InlineData("dotnet build", false)]
|
||||
public void IsBlockedShellCommandInInternalMode_DetectsOnlyNetworkLikeCommands(string command, bool expected)
|
||||
{
|
||||
OperationModePolicy.IsBlockedShellCommandInInternalMode(command).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsExternalUrl_DetectsOnlyHttpSchemes()
|
||||
{
|
||||
@@ -267,4 +279,49 @@ public class OperationModePolicyTests
|
||||
writeAllowed.Should().BeFalse();
|
||||
readAllowed.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessTool_ExecuteAsync_InternalMode_BlocksNetworkShellCommand()
|
||||
{
|
||||
var tool = new ProcessTool();
|
||||
using var doc = JsonDocument.Parse("""{"command":"curl https://example.com","shell":"cmd"}""");
|
||||
var context = new AgentContext
|
||||
{
|
||||
OperationMode = OperationModePolicy.InternalMode,
|
||||
Permission = "BypassPermissions",
|
||||
WorkFolder = Path.GetTempPath()
|
||||
};
|
||||
|
||||
var result = await tool.ExecuteAsync(doc.RootElement, context, CancellationToken.None);
|
||||
|
||||
result.Success.Should().BeFalse();
|
||||
result.Output.Should().Contain("사내 모드");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BuildRunTool_ExecuteAsync_InternalMode_BlocksCustomCommand()
|
||||
{
|
||||
var workspaceDir = Path.Combine(Path.GetTempPath(), "axcopilot-buildrun-internal-" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(workspaceDir);
|
||||
try
|
||||
{
|
||||
var tool = new BuildRunTool();
|
||||
using var doc = JsonDocument.Parse("""{"action":"custom","command":"curl https://example.com"}""");
|
||||
var context = new AgentContext
|
||||
{
|
||||
OperationMode = OperationModePolicy.InternalMode,
|
||||
Permission = "BypassPermissions",
|
||||
WorkFolder = workspaceDir
|
||||
};
|
||||
|
||||
var result = await tool.ExecuteAsync(doc.RootElement, context, CancellationToken.None);
|
||||
|
||||
result.Success.Should().BeFalse();
|
||||
result.Output.Should().Contain("사내 모드");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { if (Directory.Exists(workspaceDir)) Directory.Delete(workspaceDir, true); } catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,4 +87,21 @@ public class OperationModeReadinessTests
|
||||
result.Success.Should().BeFalse();
|
||||
result.Output.Should().Contain("사내모드");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OpenExternal_InternalMode_BlocksExternalUriSchemes()
|
||||
{
|
||||
var tool = new OpenExternalTool();
|
||||
using var doc = JsonDocument.Parse("""{"path":"mailto:admin@example.com"}""");
|
||||
var context = new AgentContext
|
||||
{
|
||||
OperationMode = OperationModePolicy.InternalMode,
|
||||
Permission = "Auto"
|
||||
};
|
||||
|
||||
var result = await tool.ExecuteAsync(doc.RootElement, context, CancellationToken.None);
|
||||
|
||||
result.Success.Should().BeFalse();
|
||||
result.Output.Should().Contain("사내모드");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user