Merge pull request #3 from dj-oyu/test-ahead
fix: include rule details in safety guard error messages
This commit is contained in:
commit
78dc0b79fc
4 changed files with 93 additions and 4 deletions
|
|
@ -36,7 +36,7 @@ func validatePath(path, workspace string, restrict bool) (string, error) {
|
||||||
|
|
||||||
if restrict {
|
if restrict {
|
||||||
if !isWithinWorkspace(absPath, absWorkspace) {
|
if !isWithinWorkspace(absPath, absWorkspace) {
|
||||||
return "", fmt.Errorf("access denied: path is outside the workspace")
|
return "", fmt.Errorf("access denied: path outside workspace %s", absWorkspace)
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolved string
|
var resolved string
|
||||||
|
|
|
||||||
|
|
@ -486,3 +486,15 @@ func TestSandboxFs_Write(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, newData, content)
|
assert.Equal(t, newData, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestValidatePath_OutsideWorkspace_IncludesPath verifies that the access
|
||||||
|
// denied error includes the workspace path so the caller knows the boundary.
|
||||||
|
func TestValidatePath_OutsideWorkspace_IncludesPath(t *testing.T) {
|
||||||
|
workspace := t.TempDir()
|
||||||
|
outsidePath := filepath.Join(t.TempDir(), "secret.txt")
|
||||||
|
|
||||||
|
_, err := validatePath(outsidePath, workspace, true)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "access denied")
|
||||||
|
assert.Contains(t, err.Error(), workspace)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -701,7 +701,7 @@ func (t *ExecTool) guardCommand(command, cwd string) string {
|
||||||
|
|
||||||
for _, pattern := range t.denyPatterns {
|
for _, pattern := range t.denyPatterns {
|
||||||
if pattern.MatchString(lower) {
|
if pattern.MatchString(lower) {
|
||||||
return "Command blocked by safety guard (dangerous pattern detected)"
|
return fmt.Sprintf("Command blocked: deny pattern %s", pattern.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -714,7 +714,16 @@ func (t *ExecTool) guardCommand(command, cwd string) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !allowed {
|
if !allowed {
|
||||||
return "Command blocked by safety guard (not in allowlist)"
|
var b strings.Builder
|
||||||
|
b.WriteString("Command blocked: not in allowlist [")
|
||||||
|
for i, p := range t.allowPatterns {
|
||||||
|
if i > 0 {
|
||||||
|
b.WriteByte(',')
|
||||||
|
}
|
||||||
|
b.WriteString(p.String())
|
||||||
|
}
|
||||||
|
b.WriteByte(']')
|
||||||
|
return b.String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -764,7 +773,7 @@ func (t *ExecTool) guardCommand(command, cwd string) string {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "Command blocked by safety guard (path outside working dir)"
|
return fmt.Sprintf("Command blocked: path outside working dir %s", p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -553,6 +553,74 @@ func TestGuardCommand_AgentCLISlashCommand(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestGuardCommand_DenyPattern_IncludesPattern verifies that deny-match
|
||||||
|
// error messages include the matched pattern string.
|
||||||
|
func TestGuardCommand_DenyPattern_IncludesPattern(t *testing.T) {
|
||||||
|
workspace := t.TempDir()
|
||||||
|
tool, _ := NewExecTool(workspace, true)
|
||||||
|
// Also add a custom deny pattern for precise matching.
|
||||||
|
tool.denyPatterns = append(tool.denyPatterns, regexp.MustCompile(`\bdangerous_cmd\b`))
|
||||||
|
|
||||||
|
result := tool.guardCommand("dangerous_cmd --force", workspace)
|
||||||
|
if result == "" {
|
||||||
|
t.Fatal("expected deny pattern to block the command")
|
||||||
|
}
|
||||||
|
if !strings.Contains(result, "deny pattern") {
|
||||||
|
t.Errorf("expected 'deny pattern' in message, got: %s", result)
|
||||||
|
}
|
||||||
|
if !strings.Contains(result, `\bdangerous_cmd\b`) {
|
||||||
|
t.Errorf("expected pattern string in message, got: %s", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGuardCommand_Allowlist_ShowsPatterns verifies that allowlist violation
|
||||||
|
// messages include all configured patterns.
|
||||||
|
func TestGuardCommand_Allowlist_ShowsPatterns(t *testing.T) {
|
||||||
|
workspace := t.TempDir()
|
||||||
|
tool, _ := NewExecTool(workspace, true)
|
||||||
|
err := tool.SetAllowPatterns([]string{`^go\b`, `^git\b`})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SetAllowPatterns failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := tool.guardCommand("curl http://example.com", workspace)
|
||||||
|
if result == "" {
|
||||||
|
t.Fatal("expected allowlist to block the command")
|
||||||
|
}
|
||||||
|
if !strings.Contains(result, "not in allowlist") {
|
||||||
|
t.Errorf("expected 'not in allowlist' in message, got: %s", result)
|
||||||
|
}
|
||||||
|
if !strings.Contains(result, `^go\b`) || !strings.Contains(result, `^git\b`) {
|
||||||
|
t.Errorf("expected allowlist patterns in message, got: %s", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGuardCommand_PathOutside_IncludesPath verifies that workspace-escape
|
||||||
|
// messages include the offending path token.
|
||||||
|
func TestGuardCommand_PathOutside_IncludesPath(t *testing.T) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("Unix absolute path test not applicable on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
workspace := t.TempDir()
|
||||||
|
externalDir := t.TempDir()
|
||||||
|
dataFile := filepath.Join(externalDir, "secret.txt")
|
||||||
|
os.WriteFile(dataFile, []byte("secret"), 0o644)
|
||||||
|
|
||||||
|
tool, _ := NewExecTool(workspace, true)
|
||||||
|
|
||||||
|
result := tool.guardCommand("cat "+dataFile, workspace)
|
||||||
|
if result == "" {
|
||||||
|
t.Fatal("expected path outside workspace to be blocked")
|
||||||
|
}
|
||||||
|
if !strings.Contains(result, "path outside working dir") {
|
||||||
|
t.Errorf("expected 'path outside working dir' in message, got: %s", result)
|
||||||
|
}
|
||||||
|
if !strings.Contains(result, dataFile) {
|
||||||
|
t.Errorf("expected offending path %q in message, got: %s", dataFile, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Background process tests ---
|
// --- Background process tests ---
|
||||||
|
|
||||||
func TestExecTool_Bg_StartAndOutput(t *testing.T) {
|
func TestExecTool_Bg_StartAndOutput(t *testing.T) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue