Skip to content

Commit

Permalink
Escape single quotes in variable/values
Browse files Browse the repository at this point in the history
Closes #65
  • Loading branch information
LucasPickering committed Feb 12, 2024
1 parent aee4aca commit 36d8030
Showing 1 changed file with 63 additions and 4 deletions.
67 changes: 63 additions & 4 deletions src/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,12 @@ impl Shell {
pub fn export(&self, environment: &Environment) -> String {
let mut output = String::new();
for (variable, value) in environment.iter_unmasked() {
// Escape single quotes to prevent injection vulnerabilities
let variable = escape(variable);
let value = escape(value);

// Generate a shell command to export the variable
match self.kind {
// Single quotes are needed to prevent injection
// vulnerabilities.
// TODO escape inner single quotes
// https://github.com/LucasPickering/env-select/issues/65
ShellKind::Bash | ShellKind::Zsh => {
writeln!(output, "export '{variable}'='{value}'")
.expect("string writing is infallible");
Expand Down Expand Up @@ -154,3 +154,62 @@ impl From<ShellKind> for Shell {
Self::from_kind(kind)
}
}

/// Escape single quotes in the given string, replacing them with \'
fn escape(value: &str) -> String {
value.replace('\'', "\\'")
}

#[cfg(test)]
mod tests {
use super::*;
use crate::{
config::Profile,
test_util::{literal, map},
};
use rstest::rstest;

/// Bash and Zsh use the same export format so we can test them together
#[rstest]
fn test_bash_zsh_export(
#[values(ShellKind::Bash, ShellKind::Zsh)] shell_kind: ShellKind,
) {
let shell = Shell::from_kind(shell_kind);
let environment = environment(&shell);
assert_eq!(
shell.export(&environment).as_str(),
"\
export 'SIMPLE'='simple'
export 'ESCAPED\\'oops\\''='\\'; echo bobby tables \\''
"
);
}

/// Fish has its own variable syntax so it needs to be tested separately
#[test]
fn test_fish_export() {
let shell = Shell::from_kind(ShellKind::Fish);
let environment = environment(&shell);
assert_eq!(
shell.export(&environment).as_str(),
"\
set -gx 'SIMPLE' 'simple'
set -gx 'ESCAPED\\'oops\\'' '\\'; echo bobby tables \\''
"
);
}

fn environment(shell: &Shell) -> Environment {
Environment::from_profile(
shell,
&Profile {
variables: map([
("SIMPLE", literal("simple")),
("ESCAPED'oops'", literal("'; echo bobby tables '")),
]),
..Default::default()
},
)
.unwrap()
}
}

0 comments on commit 36d8030

Please sign in to comment.