Skip to main content

sbox/
cli.rs

1use std::path::PathBuf;
2
3use clap::{Args, Parser, Subcommand, ValueEnum};
4
5#[derive(Debug, Clone, Parser)]
6#[command(
7    name = "sbox",
8    version,
9    about = "Policy-driven sandboxed command runner"
10)]
11pub struct Cli {
12    #[arg(long, global = true)]
13    pub config: Option<PathBuf>,
14
15    #[arg(long, global = true)]
16    pub workspace: Option<PathBuf>,
17
18    #[arg(long, global = true, value_enum)]
19    pub backend: Option<CliBackendKind>,
20
21    #[arg(long, global = true)]
22    pub image: Option<String>,
23
24    #[arg(long, global = true)]
25    pub profile: Option<String>,
26
27    #[arg(long, global = true, value_enum)]
28    pub mode: Option<CliExecutionMode>,
29
30    #[arg(short = 'v', long, global = true, action = clap::ArgAction::Count)]
31    pub verbose: u8,
32
33    #[arg(long, global = true)]
34    pub quiet: bool,
35
36    #[arg(long, global = true)]
37    pub strict_security: bool,
38
39    #[command(subcommand)]
40    pub command: Commands,
41}
42
43#[derive(Debug, Clone, Subcommand)]
44pub enum Commands {
45    Init(InitCommand),
46    Run(RunCommand),
47    Exec(ExecCommand),
48    Shell(ShellCommand),
49    Plan(PlanCommand),
50    Doctor(DoctorCommand),
51    Clean(CleanCommand),
52    Shim(ShimCommand),
53    Bootstrap(BootstrapCommand),
54    Audit(AuditCommand),
55}
56
57#[derive(Debug, Clone, Args)]
58pub struct InitCommand {
59    #[arg(long)]
60    pub force: bool,
61
62    #[arg(long)]
63    pub preset: Option<String>,
64
65    #[arg(long)]
66    pub output: Option<PathBuf>,
67
68    /// Launch an interactive wizard to generate sbox.yaml
69    #[arg(long, short = 'i')]
70    pub interactive: bool,
71}
72
73#[derive(Debug, Clone, Args)]
74#[command(trailing_var_arg = true)]
75pub struct RunCommand {
76    /// Print the resolved plan and backend command without executing
77    #[arg(long)]
78    pub dry_run: bool,
79
80    /// Pass an extra environment variable into the sandbox, e.g. -e FOO=bar (repeatable)
81    #[arg(short = 'e', long = "env", value_name = "NAME=VALUE")]
82    pub env: Vec<String>,
83
84    #[arg(required = true, num_args = 1.., allow_hyphen_values = true)]
85    pub command: Vec<String>,
86}
87
88#[derive(Debug, Clone, Args)]
89#[command(trailing_var_arg = true)]
90pub struct ExecCommand {
91    pub profile: String,
92
93    #[arg(required = true, num_args = 1.., allow_hyphen_values = true)]
94    pub command: Vec<String>,
95}
96
97#[derive(Debug, Clone, Args, Default)]
98pub struct ShellCommand {
99    #[arg(long)]
100    pub shell: Option<String>,
101}
102
103#[derive(Debug, Clone, Args)]
104#[command(trailing_var_arg = true)]
105pub struct PlanCommand {
106    #[arg(long)]
107    pub show_command: bool,
108
109    /// Omit to show the policy for the profile selected by --profile without a specific command.
110    #[arg(num_args = 0.., allow_hyphen_values = true)]
111    pub command: Vec<String>,
112}
113
114#[derive(Debug, Clone, Args, Default)]
115pub struct DoctorCommand {
116    #[arg(long)]
117    pub strict: bool,
118}
119
120#[derive(Debug, Clone, Args, Default)]
121pub struct CleanCommand {
122    #[arg(long)]
123    pub sessions: bool,
124
125    #[arg(long)]
126    pub images: bool,
127
128    #[arg(long)]
129    pub caches: bool,
130
131    #[arg(long)]
132    pub all: bool,
133
134    #[arg(long = "global")]
135    pub global_scope: bool,
136}
137
138#[derive(Debug, Clone, Copy, ValueEnum)]
139pub enum CliBackendKind {
140    Podman,
141    Docker,
142}
143
144#[derive(Debug, Clone, Copy, ValueEnum)]
145pub enum CliExecutionMode {
146    Host,
147    Sandbox,
148}
149
150/// Scan the project's lockfile for known-malicious or vulnerable package versions.
151/// Delegates to the ecosystem's native audit tool (npm audit, cargo audit, etc.)
152/// and runs on the host (not in a sandbox) so it can reach advisory databases.
153#[derive(Debug, Clone, Args, Default)]
154pub struct AuditCommand {
155    /// Extra arguments forwarded to the underlying audit tool.
156    #[arg(num_args = 0.., allow_hyphen_values = true)]
157    pub extra_args: Vec<String>,
158}
159
160/// Generate the package lockfile inside the sandbox without running install scripts.
161/// Requires `package_manager:` to be configured in sbox.yaml.
162/// After bootstrap, run `sbox run -- <rebuild-command>` to execute scripts with network off.
163#[derive(Debug, Clone, Args, Default)]
164pub struct BootstrapCommand {}
165
166#[derive(Debug, Clone, Args)]
167pub struct ShimCommand {
168    /// Directory to write shim scripts into (default: ~/.local/bin)
169    #[arg(long)]
170    pub dir: Option<PathBuf>,
171
172    /// Overwrite existing shim files
173    #[arg(long)]
174    pub force: bool,
175
176    /// Print what would be created without writing anything
177    #[arg(long)]
178    pub dry_run: bool,
179}