feat: add defaultDenyRead mode for strict filesystem isolation (#24)

This commit is contained in:
JY Tan
2026-02-01 15:11:40 -08:00
committed by GitHub
parent cef3576076
commit 7679fecf06
9 changed files with 430 additions and 11 deletions

View File

@@ -0,0 +1,29 @@
{
"extends": "code",
"filesystem": {
// Deny reads by default, only system paths and allowRead are accessible
"defaultDenyRead": true,
"allowRead": [
// Current working directory
".",
// macOS preferences (needed by many apps)
"~/Library/Preferences",
// AI coding tool configs (need to read their own settings)
"~/.claude",
"~/.claude.json",
"~/.codex",
"~/.cursor",
"~/.opencode",
"~/.gemini",
"~/.factory",
// XDG config directory
"~/.config",
// Cache directories (some tools read from cache)
"~/.cache"
]
}
}

View File

@@ -43,6 +43,7 @@ var templateDescriptions = map[string]string{
"git-readonly": "Blocks destructive commands like git push, rm -rf, etc.",
"code": "Production-ready config for AI coding agents (Claude Code, Codex, Copilot, etc.)",
"code-relaxed": "Like 'code' but allows direct network for apps that ignore HTTP_PROXY (cursor-agent, opencode)",
"code-strict": "Like 'code' but denies reads by default; only allows reading the current project directory and essential system paths",
}
// List returns all available template names sorted alphabetically.

View File

@@ -126,6 +126,63 @@ func TestCodeTemplate(t *testing.T) {
}
}
func TestCodeStrictTemplate(t *testing.T) {
cfg, err := Load("code-strict")
if err != nil {
t.Fatalf("failed to load code-strict template: %v", err)
}
// Should inherit AllowPty from code template
if !cfg.AllowPty {
t.Error("code-strict should inherit AllowPty=true from code")
}
// Should have defaultDenyRead enabled
if !cfg.Filesystem.DefaultDenyRead {
t.Error("code-strict should have DefaultDenyRead=true")
}
// Should have allowRead with current directory
if len(cfg.Filesystem.AllowRead) == 0 {
t.Error("code-strict should have allowRead paths")
}
hasCurrentDir := false
for _, path := range cfg.Filesystem.AllowRead {
if path == "." {
hasCurrentDir = true
break
}
}
if !hasCurrentDir {
t.Error("code-strict should allow reading current directory")
}
// Should inherit allowWrite from code
if len(cfg.Filesystem.AllowWrite) == 0 {
t.Error("code-strict should inherit allowWrite from code")
}
// Should inherit denyWrite from code
if len(cfg.Filesystem.DenyWrite) == 0 {
t.Error("code-strict should inherit denyWrite from code")
}
// Should inherit allowed domains from code
if len(cfg.Network.AllowedDomains) == 0 {
t.Error("code-strict should inherit allowed domains from code")
}
// Should inherit denied commands from code
if len(cfg.Command.Deny) == 0 {
t.Error("code-strict should inherit denied commands from code")
}
// Extends should be cleared after resolution
if cfg.Extends != "" {
t.Error("extends should be cleared after loading")
}
}
func TestCodeRelaxedTemplate(t *testing.T) {
cfg, err := Load("code-relaxed")
if err != nil {