diff --git a/README.md b/README.md index 9384139..6de1f93 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Fence wraps commands in a sandbox that blocks network access by default and rest - **Violation Monitoring**: Real-time logging of blocked requests and sandbox denials - **Cross-Platform**: macOS (sandbox-exec) and Linux (bubblewrap) - **HTTP/SOCKS5 Proxies**: Built-in filtering proxies for domain control +- **Permission Import**: Using Claude Code? Import your Claude permissions as Fence configs with `fence import --claude -o ~/.fence.json` You can use Fence as a Go package or CLI tool. @@ -96,6 +97,9 @@ Flags: -t, --template Use built-in template (e.g., code, local-dev-server) -v, --version Show version information -h, --help Help for fence + +Subcommands: + import Import settings from other tools (e.g., --claude for Claude Code) ``` ### Examples @@ -131,6 +135,9 @@ fence -m npm install # Expose a port for inbound connections fence -p 3000 -c "npm run dev" + +# Import settings from Claude Code +fence import --claude -o .fence.json ``` ## Library Usage diff --git a/cmd/fence/main.go b/cmd/fence/main.go index 71151a5..c806bb3 100644 --- a/cmd/fence/main.go +++ b/cmd/fence/main.go @@ -13,6 +13,7 @@ import ( "syscall" "github.com/Use-Tusk/fence/internal/config" + "github.com/Use-Tusk/fence/internal/importer" "github.com/Use-Tusk/fence/internal/platform" "github.com/Use-Tusk/fence/internal/sandbox" "github.com/Use-Tusk/fence/internal/templates" @@ -100,6 +101,8 @@ Configuration file format (~/.fence.json): rootCmd.Flags().SetInterspersed(true) + rootCmd.AddCommand(newImportCmd()) + if err := rootCmd.Execute(); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) exitCode = 1 @@ -293,6 +296,101 @@ func runCommand(cmd *cobra.Command, args []string) error { return nil } +// newImportCmd creates the import subcommand. +func newImportCmd() *cobra.Command { + var ( + claudeMode bool + inputFile string + outputFile string + extendTmpl string + noExtend bool + ) + + cmd := &cobra.Command{ + Use: "import", + Short: "Import settings from other tools", + Long: `Import permission settings from other tools and convert them to fence config. + +Currently supported sources: + --claude Import from Claude Code settings + +By default, imports extend the "code" template which provides sensible defaults +for network access (npm, GitHub, LLM providers) and filesystem protections. +Use --no-extend for a minimal config, or --extend to choose a different template. + +Examples: + # Import from default Claude Code settings (~/.claude/settings.json) + fence import --claude + + # Import from a specific Claude Code settings file + fence import --claude -f ~/.claude/settings.json + + # Import and write to a specific output file + fence import --claude -o .fence.json + + # Import without extending any template (minimal config) + fence import --claude --no-extend + + # Import and extend a different template + fence import --claude --extend local-dev-server + + # Import from project-level Claude settings + fence import --claude -f .claude/settings.local.json -o .fence.json`, + RunE: func(cmd *cobra.Command, args []string) error { + if !claudeMode { + return fmt.Errorf("no import source specified. Use --claude to import from Claude Code") + } + + opts := importer.DefaultImportOptions() + if noExtend { + opts.Extends = "" + } else if extendTmpl != "" { + opts.Extends = extendTmpl + } + + result, err := importer.ImportFromClaude(inputFile, opts) + if err != nil { + return fmt.Errorf("failed to import Claude settings: %w", err) + } + + for _, warning := range result.Warnings { + fmt.Fprintf(os.Stderr, "Warning: %s\n", warning) + } + + if outputFile != "" { + if err := importer.WriteConfig(result.Config, outputFile); err != nil { + return err + } + fmt.Printf("Imported %d rules from %s\n", result.RulesImported, result.SourcePath) + fmt.Printf("Written to %s\n", outputFile) + } else { + // Print clean JSON to stdout, helpful info to stderr (don't interfere with piping) + data, err := importer.MarshalConfigJSON(result.Config) + if err != nil { + return fmt.Errorf("failed to marshal config: %w", err) + } + fmt.Println(string(data)) + if result.Config.Extends != "" { + fmt.Fprintf(os.Stderr, "\n# Extends %q - inherited rules not shown\n", result.Config.Extends) + } + fmt.Fprintf(os.Stderr, "# Imported %d rules from %s\n", result.RulesImported, result.SourcePath) + fmt.Fprintf(os.Stderr, "# Use -o to write to a file (includes comments)\n") + } + + return nil + }, + } + + cmd.Flags().BoolVar(&claudeMode, "claude", false, "Import from Claude Code settings") + cmd.Flags().StringVarP(&inputFile, "file", "f", "", "Path to settings file (default: ~/.claude/settings.json for --claude)") + cmd.Flags().StringVarP(&outputFile, "output", "o", "", "Output file path (default: stdout)") + cmd.Flags().StringVar(&extendTmpl, "extend", "", "Template to extend (default: code)") + cmd.Flags().BoolVar(&noExtend, "no-extend", false, "Don't extend any template (minimal config)") + cmd.MarkFlagsMutuallyExclusive("extend", "no-extend") + + return cmd +} + // printTemplates prints all available templates to stdout. func printTemplates() { fmt.Println("Available templates:") diff --git a/docs/configuration.md b/docs/configuration.md index 9427ddb..c736dec 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -164,6 +164,54 @@ Fence detects blocked commands in: |-------|-------------| | `allowPty` | Allow pseudo-terminal (PTY) allocation in the sandbox (for MacOS) | +## Importing from Claude Code + +If you've been using Claude Code and have already built up permission rules, you can import them into fence: + +```bash +# Import from default Claude Code settings (~/.claude/settings.json) +fence import --claude + +# Import from a specific file +fence import --claude -f ~/.claude/settings.json + +# Import and write to a specific output file +fence import --claude -o .fence.json + +# Import without extending any template (minimal config) +fence import --claude --no-extend + +# Import and extend a different template +fence import --claude --extend local-dev-server + +# Import from project-level Claude settings +fence import --claude -f .claude/settings.local.json -o .fence.json +``` + +### Default Template + +By default, imports extend the `code` template which provides sensible defaults: + +- Network access for npm, GitHub, LLM providers, etc. +- Filesystem protections for secrets and sensitive paths +- Command restrictions for dangerous operations + +Use `--no-extend` if you want a minimal config without these defaults, or `--extend