diff --git a/crates/tinytools-std/src/filesystem/run_linter/mod.rs b/crates/tinytools-std/src/filesystem/run_linter/mod.rs index b9c55aa..857f55e 100644 --- a/crates/tinytools-std/src/filesystem/run_linter/mod.rs +++ b/crates/tinytools-std/src/filesystem/run_linter/mod.rs @@ -121,6 +121,11 @@ impl Tool for RunLinterTool { (no absolute paths or '..')", )); } + // A value starting with `-` would reach eslint as an option + // (`--output-file=` writes a file). No path needs one. + if path.starts_with('-') { + return Ok(ToolResult::error("path must be a path, not an option")); + } let mut cmd = tokio::process::Command::new("npx"); cmd.args(["eslint", "--format", "compact", path]) .current_dir(&workspace_dir); diff --git a/crates/tinytools-std/src/filesystem/run_linter/mod_tests.rs b/crates/tinytools-std/src/filesystem/run_linter/mod_tests.rs index 5a05573..2fb8012 100644 --- a/crates/tinytools-std/src/filesystem/run_linter/mod_tests.rs +++ b/crates/tinytools-std/src/filesystem/run_linter/mod_tests.rs @@ -95,3 +95,18 @@ async fn a_workspace_descriptor_overrides_the_configured_directory() { assert!(result.is_error); assert!(result.output().contains("Could not detect project type")); } + +// A path starting with `-` would reach eslint as an option +// (`--output-file=` writes a file). No path needs a leading `-`. +#[tokio::test] +async fn eslint_rejects_an_option_shaped_path() { + let tmp = TempDir::new().unwrap(); + std::fs::write(tmp.path().join("package.json"), "{}").unwrap(); + let result = make_tool(&tmp) + .execute(json!({"linter": "eslint", "path": "--output-file=pwned.txt"})) + .await + .unwrap(); + assert!(result.is_error); + assert!(result.output().contains("not an option")); + assert!(!tmp.path().join("pwned.txt").exists()); +}