Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add warning when PATH env separator is in project path #11318

Merged
merged 5 commits into from
Nov 13, 2022
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
66 changes: 54 additions & 12 deletions crates/cargo-util/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@ use tempfile::Builder as TempFileBuilder;
/// environment variable this is will be used for, which is included in the
/// error message.
pub fn join_paths<T: AsRef<OsStr>>(paths: &[T], env: &str) -> Result<OsString> {
env::join_paths(paths.iter())
.with_context(|| {
let paths = paths.iter().map(Path::new).collect::<Vec<_>>();
format!("failed to join path array: {:?}", paths)
})
.with_context(|| {
format!(
"failed to join search paths together\n\
Does ${} have an unterminated quote character?",
env
)
})
env::join_paths(paths.iter()).with_context(|| {
let mut message = format!(
"failed to join paths from `${env}` together\n\n\
Check if any of path segments listed below contain an \
unterminated quote character or path separator:"
);
for path in paths {
use std::fmt::Write;
write!(&mut message, "\n {:?}", Path::new(path)).unwrap();
}

message
})
}

/// Returns the name of the environment variable used for searching for
Expand Down Expand Up @@ -743,3 +744,44 @@ fn exclude_from_time_machine(path: &Path) {
// Errors are ignored, since it's an optional feature and failure
// doesn't prevent Cargo from working
}

#[cfg(test)]
mod tests {
use super::join_paths;

#[test]
fn join_paths_lists_paths_on_error() {
let valid_paths = vec!["/testing/one", "/testing/two"];
// does not fail on valid input
let _joined = join_paths(&valid_paths, "TESTING1").unwrap();

#[cfg(unix)]
{
let invalid_paths = vec!["/testing/one", "/testing/t:wo/three"];
let err = join_paths(&invalid_paths, "TESTING2").unwrap_err();
assert_eq!(
err.to_string(),
"failed to join paths from `$TESTING2` together\n\n\
Check if any of path segments listed below contain an \
unterminated quote character or path separator:\
\n \"/testing/one\"\
\n \"/testing/t:wo/three\"\
"
);
}
#[cfg(windows)]
{
let invalid_paths = vec!["/testing/one", "/testing/t\"wo/three"];
let err = join_paths(&invalid_paths, "TESTING2").unwrap_err();
assert_eq!(
err.to_string(),
"failed to join paths from `$TESTING2` together\n\n\
Check if any of path segments listed below contain an \
unterminated quote character or path separator:\
\n \"/testing/one\"\
\n \"/testing/t\\\"wo/three\"\
"
);
}
}
}
20 changes: 19 additions & 1 deletion src/cargo/ops/cargo_new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ use cargo_util::paths;
use serde::de;
use serde::Deserialize;
use std::collections::BTreeMap;
use std::fmt;
use std::ffi::OsStr;
use std::io::{BufRead, BufReader, ErrorKind};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{fmt, slice};
use toml_edit::easy as toml;

#[derive(Clone, Copy, Debug, PartialEq)]
Expand Down Expand Up @@ -261,6 +262,19 @@ fn check_name(
Ok(())
}

/// Checks if the path contains any invalid PATH env characters.
fn check_path(path: &Path, shell: &mut Shell) -> CargoResult<()> {
// warn if the path contains characters that will break `env::join_paths`
if let Err(_) = paths::join_paths(slice::from_ref(&OsStr::new(path)), "") {
let path = path.to_string_lossy();
shell.warn(format!(
"the path `{path}` contains invalid PATH characters (usually `:`, `;`, or `\"`)\n\
It is recommended to use a different name to avoid problems."
))?;
}
Ok(())
}

fn detect_source_paths_and_types(
package_path: &Path,
package_name: &str,
Expand Down Expand Up @@ -421,6 +435,8 @@ pub fn new(opts: &NewOptions, config: &Config) -> CargoResult<()> {
)
}

check_path(path, &mut config.shell())?;

let is_bin = opts.kind.is_bin();

let name = get_name(path, opts)?;
Expand Down Expand Up @@ -458,6 +474,8 @@ pub fn init(opts: &NewOptions, config: &Config) -> CargoResult<NewProjectKind> {
anyhow::bail!("`cargo init` cannot be run on existing Cargo packages")
}

check_path(path, &mut config.shell())?;

let name = get_name(path, opts)?;

let mut src_paths_types = vec![];
Expand Down
2 changes: 2 additions & 0 deletions tests/testsuite/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ mod mercurial_autodetect;
mod multibin_project_name_clash;
#[cfg(not(windows))]
mod no_filename;
#[cfg(unix)]
mod path_contains_separator;
mod pijul_autodetect;
mod reserved_name;
mod simple_bin;
Expand Down
Empty file.
26 changes: 26 additions & 0 deletions tests/testsuite/init/path_contains_separator/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use cargo_test_support::compare::assert_ui;
use cargo_test_support::prelude::*;
use cargo_test_support::{t, Project};

use cargo_test_support::curr_dir;

#[cargo_test]
fn path_contains_separator() {
let project = Project::from_template(curr_dir!().join("in"));
let project_root = &project.root().join("test:ing");

if !project_root.exists() {
t!(std::fs::create_dir(&project_root));
}

snapbox::cmd::Command::cargo_ui()
.arg_line("init --bin --vcs none --edition 2015 --name testing")
.current_dir(project_root)
.assert()
.success()
.stdout_matches_path(curr_dir!().join("stdout.log"))
.stderr_matches_path(curr_dir!().join("stderr.log"));

assert_ui().subset_matches(curr_dir!().join("out"), project_root);
assert!(!project_root.join(".gitignore").is_file());
}
8 changes: 8 additions & 0 deletions tests/testsuite/init/path_contains_separator/out/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "testing"
version = "0.1.0"
edition = "2015"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
3 changes: 3 additions & 0 deletions tests/testsuite/init/path_contains_separator/out/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}
3 changes: 3 additions & 0 deletions tests/testsuite/init/path_contains_separator/stderr.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
warning: the path `[ROOT]/case/test:ing/.` contains invalid PATH characters (usually `:`, `;`, or `"`)
It is recommended to use a different name to avoid problems.
Created binary (application) package
Empty file.
14 changes: 14 additions & 0 deletions tests/testsuite/new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,3 +529,17 @@ Caused by:
)
.run();
}

#[cfg(unix)]
#[cargo_test]
fn path_with_invalid_character() {
cargo_process("new --name testing test:ing")
.with_stderr(
"\
[WARNING] the path `[CWD]/test:ing` contains invalid PATH characters (usually `:`, `;`, or `\"`)
It is recommended to use a different name to avoid problems.
[CREATED] binary (application) `testing` package
",
)
.run();
}