Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions dryrun_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//go:build darwin

package main

import (
"fmt"
"os"
"path/filepath"
"strings"
)

// showDryRun displays the sandbox profile that would be generated for the given configuration
func showDryRun(config *SandboxConfig) error {
fmt.Println("Sandbox Profile (dry-run):")
fmt.Println("========================================")
fmt.Println("Version: macOS Sandbox v1")
fmt.Println("Base profile: system.sb")
fmt.Println()
fmt.Println("Rules:")

if config.AllowAll {
fmt.Println("- Allow all operations (--allow-all flag)")
} else {
fmt.Println("- Allow all operations by default")
fmt.Println("- Deny all file writes")
fmt.Println("- Allow writes to:")
fmt.Println(" * System temporary directories")

if config.AllowKeychain {
fmt.Println(" * Keychain directories (--allow-keychain)")
}

// Process allowed paths
for _, path := range config.AllowedPaths {
absPath, err := filepath.Abs(path)
if err != nil {
absPath = path
}
source := "user specified"
if config.AllowGit && strings.Contains(path, ".git") {
source = "--allow-git"
}
fmt.Printf(" * %s (%s)\n", absPath, source)
}
}

fmt.Println()
fmt.Println("Raw profile:")
fmt.Println("----------------------------------------")

// Generate and display the actual profile
profile, err := generateSandboxProfile(config)
if err != nil {
return fmt.Errorf("generate sandbox profile: %w", err)
}
fmt.Print(profile)
fmt.Println("----------------------------------------")

fmt.Println()
fmt.Printf("Command: %s", config.Command)
if len(config.Args) > 0 {
fmt.Printf(" %s", strings.Join(config.Args, " "))
}
fmt.Println()

return nil
}

// printDryRunAndExit displays the dry-run information and exits
func printDryRunAndExit(config *SandboxConfig) {
if err := showDryRun(config); err != nil {
fmt.Fprintf(os.Stderr, "cage: error showing dry-run: %v\n", err)
os.Exit(1)
}
os.Exit(0)
}
61 changes: 61 additions & 0 deletions dryrun_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//go:build linux

package main

import (
"fmt"
"os"
"path/filepath"
"strings"
)

// showDryRun displays the sandbox configuration that would be applied for the given configuration
func showDryRun(config *SandboxConfig) error {
fmt.Println("Sandbox Profile (dry-run):")
fmt.Println("========================================")
fmt.Println("Platform: Linux")
fmt.Println("Technology: Landlock LSM")
fmt.Println()
fmt.Println("The following restrictions would be applied:")
fmt.Println()
fmt.Println("Rules:")

if config.AllowAll {
fmt.Println("- Allow all operations (--allow-all flag)")
} else {
fmt.Println("- Allow read access to all files")
fmt.Println("- Deny write access except to:")
fmt.Println(" * /dev/null (for discarding output)")

// Process allowed paths
for _, path := range config.AllowedPaths {
absPath, err := filepath.Abs(path)
if err != nil {
absPath = path
}
source := "user specified"
if config.AllowGit && strings.Contains(path, ".git") {
source = "--allow-git"
}
fmt.Printf(" * %s (%s)\n", absPath, source)
}
}

fmt.Println()
fmt.Printf("Command: %s", config.Command)
if len(config.Args) > 0 {
fmt.Printf(" %s", strings.Join(config.Args, " "))
}
fmt.Println()

return nil
}

// printDryRunAndExit displays the dry-run information and exits
func printDryRunAndExit(config *SandboxConfig) {
if err := showDryRun(config); err != nil {
fmt.Fprintf(os.Stderr, "cage: error showing dry-run: %v\n", err)
os.Exit(1)
}
os.Exit(0)
}
23 changes: 23 additions & 0 deletions dryrun_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//go:build !darwin && !linux

package main

import (
"fmt"
"os"
"runtime"
)

// showDryRun displays an error that cage is not supported on this platform
func showDryRun(config *SandboxConfig) error {
return fmt.Errorf("cage is not supported on %s", runtime.GOOS)
}

// printDryRunAndExit displays the dry-run information and exits
func printDryRunAndExit(config *SandboxConfig) {
if err := showDryRun(config); err != nil {
fmt.Fprintf(os.Stderr, "cage: %v\n", err)
os.Exit(1)
}
os.Exit(0)
}
13 changes: 13 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type flags struct {
listPresets bool
configPath string
version bool
dryRun bool
}

func parseFlags() (*flags, []string) {
Expand Down Expand Up @@ -95,6 +96,13 @@ func parseFlags() (*flags, []string) {
"Print version information and exit",
)

flag.BoolVar(
&f.dryRun,
"dry-run",
false,
"Show the generated sandbox profile without executing",
)

flag.Parse()

f.allowPaths = []string(allowFlags)
Expand Down Expand Up @@ -236,6 +244,11 @@ func main() {
Args: args[1:],
}

// Handle dry-run flag
if flags.dryRun {
printDryRunAndExit(sandboxConfig)
}

// Execute in sandbox
if err := RunInSandbox(sandboxConfig); err != nil {
fmt.Fprintf(os.Stderr, "cage: %v\n", err)
Expand Down