loopd, bxctl, inferproxy: read args_os instead of panicking
M3a review finding 7, for the other three roles. loopd keeps its config path as a path; bxctl and inferproxy take text arguments and answer one that is not UTF-8 with their usage. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,19 @@ use bxctl::escape::escape_model_text;
|
|||||||
use proto::{ErrorCode, SessionId, Timestamp, TurnDone};
|
use proto::{ErrorCode, SessionId, Timestamp, TurnDone};
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
let args: Vec<String> = std::env::args().skip(1).collect();
|
// `args_os`, because `args` panics on an argument that is not UTF-8. bxctl's arguments are
|
||||||
|
// text (ids, messages, paths it prints back), so such an argument is a usage error.
|
||||||
|
let args: Vec<String> = match std::env::args_os()
|
||||||
|
.skip(1)
|
||||||
|
.map(|a| a.into_string())
|
||||||
|
.collect()
|
||||||
|
{
|
||||||
|
Ok(args) => args,
|
||||||
|
Err(_) => {
|
||||||
|
eprintln!("bxctl: an argument is not valid UTF-8\n{USAGE}");
|
||||||
|
return ExitCode::from(2);
|
||||||
|
}
|
||||||
|
};
|
||||||
// $BOXMAKER_HOME defaults to /var/lib/boxmaker; defaults for the sockets are read from it.
|
// $BOXMAKER_HOME defaults to /var/lib/boxmaker; defaults for the sockets are read from it.
|
||||||
let home = std::env::var_os("BOXMAKER_HOME")
|
let home = std::env::var_os("BOXMAKER_HOME")
|
||||||
.map(PathBuf::from)
|
.map(PathBuf::from)
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
//! An argument that is not UTF-8 is a usage error, not a panic (M3a review finding 7).
|
||||||
|
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn an_argument_that_is_not_utf8_is_a_usage_error() {
|
||||||
|
let out = Command::new(env!("CARGO_BIN_EXE_bxctl"))
|
||||||
|
.args([OsStr::new("approve"), OsStr::from_bytes(b"4\xff")])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(out.status.code(), Some(2));
|
||||||
|
let err = String::from_utf8_lossy(&out.stderr);
|
||||||
|
assert!(
|
||||||
|
err.starts_with("bxctl: an argument is not valid UTF-8"),
|
||||||
|
"{err}"
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,7 +7,11 @@ use std::process;
|
|||||||
use inferproxy::{Limits, serve};
|
use inferproxy::{Limits, serve};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<String> = env::args().collect();
|
// `args_os`, because `args` panics on an argument that is not UTF-8.
|
||||||
|
let args: Vec<String> = match env::args_os().map(|a| a.into_string()).collect() {
|
||||||
|
Ok(args) => args,
|
||||||
|
Err(_) => usage(),
|
||||||
|
};
|
||||||
let mut listen: Option<String> = None;
|
let mut listen: Option<String> = None;
|
||||||
let mut upstream: Option<String> = None;
|
let mut upstream: Option<String> = None;
|
||||||
let mut i = 1;
|
let mut i = 1;
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
//! An argument that is not UTF-8 is a usage error, not a panic (M3a review finding 7).
|
||||||
|
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn an_argument_that_is_not_utf8_is_a_usage_error() {
|
||||||
|
let out = Command::new(env!("CARGO_BIN_EXE_inferproxy"))
|
||||||
|
.args([OsStr::new("--listen"), OsStr::from_bytes(b"/tmp/\xff.sock")])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(out.status.code(), Some(2));
|
||||||
|
}
|
||||||
@@ -16,11 +16,12 @@ use loopd::selftest::{SelfTestError, run};
|
|||||||
use loopd::tools::{Registry, ToolPort};
|
use loopd::tools::{Registry, ToolPort};
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
let args: Vec<String> = std::env::args().skip(1).collect();
|
// `args_os`: the config path need not be UTF-8, and `args` would panic on one that is not.
|
||||||
let args: Vec<&str> = args.iter().map(String::as_str).collect();
|
let args: Vec<std::ffi::OsString> = std::env::args_os().skip(1).collect();
|
||||||
match args.as_slice() {
|
let words: Vec<Option<&str>> = args.iter().map(|a| a.to_str()).collect();
|
||||||
["selftest", "--config", path] => run_selftest(path),
|
match (words.as_slice(), args.get(2)) {
|
||||||
["serve", "--config", path] => run_serve(path),
|
([Some("selftest"), Some("--config"), _], Some(path)) => run_selftest(Path::new(path)),
|
||||||
|
([Some("serve"), Some("--config"), _], Some(path)) => run_serve(Path::new(path)),
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!("usage: loopd selftest --config <path>");
|
eprintln!("usage: loopd selftest --config <path>");
|
||||||
eprintln!("usage: loopd serve --config <path>");
|
eprintln!("usage: loopd serve --config <path>");
|
||||||
@@ -46,8 +47,8 @@ fn run_selftest_check(client: &Client) -> Result<(), SelfTestError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_selftest(path: &str) -> ExitCode {
|
fn run_selftest(path: &Path) -> ExitCode {
|
||||||
let cfg = match Config::load(Path::new(path)) {
|
let cfg = match Config::load(path) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("loopd: {e}");
|
eprintln!("loopd: {e}");
|
||||||
@@ -62,8 +63,8 @@ fn run_selftest(path: &str) -> ExitCode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_serve(path: &str) -> ExitCode {
|
fn run_serve(path: &Path) -> ExitCode {
|
||||||
let cfg = match Config::load(Path::new(path)) {
|
let cfg = match Config::load(path) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("loopd: {e}");
|
eprintln!("loopd: {e}");
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
//! A config path that is not UTF-8 is read as a path, not a panic (M3a review finding 7).
|
||||||
|
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_config_path_that_is_not_utf8_is_read_as_a_path() {
|
||||||
|
let path = std::env::temp_dir().join(OsStr::from_bytes(b"loopd-missing-\xff.toml"));
|
||||||
|
let out = Command::new(env!("CARGO_BIN_EXE_loopd"))
|
||||||
|
.args([
|
||||||
|
OsStr::new("selftest"),
|
||||||
|
OsStr::new("--config"),
|
||||||
|
path.as_os_str(),
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
out.status.code(),
|
||||||
|
Some(1),
|
||||||
|
"a config error, not a panic (101)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_flag_that_is_not_utf8_is_a_usage_error() {
|
||||||
|
let out = Command::new(env!("CARGO_BIN_EXE_loopd"))
|
||||||
|
.args([
|
||||||
|
OsStr::from_bytes(b"serve\xff"),
|
||||||
|
OsStr::new("--config"),
|
||||||
|
OsStr::new("x"),
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(out.status.code(), Some(2));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user