use crate::core::shell_escape::sh_squote;
use crate::core::types::Resource;
fn sed_escape(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for c in s.chars() {
if matches!(c, '\\' | '|' | '.' | '*' | '[' | ']' | '^' | '$' | '/') {
out.push('\\');
}
out.push(c);
}
out
}
pub fn check_script(resource: &Resource) -> String {
let target = resource.path.as_deref().unwrap_or("/mnt/unknown");
let t = sh_squote(target);
format!(
"mountpoint -q {t} 2>/dev/null && echo {} || echo {}",
sh_squote(&format!("mounted:{target}")),
sh_squote(&format!("unmounted:{target}"))
)
}
pub fn apply_script(resource: &Resource) -> String {
let source = resource.source.as_deref().unwrap_or("none");
let target = resource.path.as_deref().unwrap_or("/mnt/unknown");
let fstype = resource.fs_type.as_deref().unwrap_or("auto");
let options = resource.options.as_deref().unwrap_or("defaults");
let state = resource.state.as_deref().unwrap_or("mounted");
let s = sh_squote(source);
let t = sh_squote(target);
let ft = sh_squote(fstype);
let o = sh_squote(options);
let mut lines = vec!["set -euo pipefail".to_string()];
match state {
"mounted" => {
lines.push(format!("mkdir -p {t}"));
lines.push(format!(
"if ! mountpoint -q {t}; then\n mount -t {ft} -o {o} {s} {t}\nfi"
));
let fstab_line = sh_squote(&format!("{source} {target} {fstype} {options} 0 0"));
lines.push(format!(
"if ! grep -q {t} /etc/fstab 2>/dev/null; then\n \
echo {fstab_line} >> /etc/fstab\nfi"
));
}
"unmounted" => {
lines.push(format!("if mountpoint -q {t}; then\n umount {t}\nfi"));
}
"absent" => {
lines.push(format!("if mountpoint -q {t}; then\n umount {t}\nfi"));
let sed_pattern = sed_escape(target);
lines.push(format!(
"sed -i {} /etc/fstab 2>/dev/null || true",
sh_squote(&format!("\\|{sed_pattern}|d"))
));
}
_ => {}
}
lines.join("\n")
}
pub fn state_query_script(resource: &Resource) -> String {
let target = resource.path.as_deref().unwrap_or("/mnt/unknown");
let t = sh_squote(target);
format!(
"if mountpoint -q {t}; then\n\
findmnt -n -o SOURCE,FSTYPE,OPTIONS {t} 2>/dev/null\n\
else\n\
echo 'UNMOUNTED'\n\
fi"
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::types::{MachineTarget, ResourceType};
fn mount_resource() -> Resource {
Resource {
resource_type: ResourceType::Mount,
machine: MachineTarget::Single("m1".to_string()),
path: Some("/mnt/data".to_string()),
source: Some("nas:/export".to_string()),
fs_type: Some("nfs".to_string()),
options: Some("rw,noatime".to_string()),
..Default::default()
}
}
#[test]
fn fj154_mount_fields_quoted() {
let r = mount_resource();
let script = apply_script(&r);
assert!(script.contains("mount -t 'nfs' -o 'rw,noatime' 'nas:/export' '/mnt/data'"));
assert!(script.contains("echo 'nas:/export /mnt/data nfs rw,noatime 0 0' >> /etc/fstab"));
}
#[test]
fn fj154_mount_source_injection_neutralized() {
let mut r = mount_resource();
r.source = Some("x';reboot;'".to_string());
let script = apply_script(&r);
assert!(script.contains("'x'\\'';reboot;'\\'''"));
assert!(!script.contains(" 'x';reboot"));
}
#[test]
fn fj154_mount_absent_sed_program_quoted() {
let mut r = mount_resource();
r.state = Some("absent".to_string());
let script = apply_script(&r);
assert!(script.contains("sed -i '\\|\\/mnt\\/data|d' /etc/fstab"));
}
#[test]
fn fj154_mount_absent_quote_in_path_neutralized() {
let mut r = mount_resource();
r.state = Some("absent".to_string());
r.path = Some("/mnt/x';reboot;'".to_string());
let script = apply_script(&r);
assert!(script.contains("'\\''"));
assert!(!script.contains("sed -i '\\|\\/mnt\\/x';reboot"));
}
#[test]
fn fj154_mount_check_and_query_quoted() {
let r = mount_resource();
assert!(check_script(&r).contains("mountpoint -q '/mnt/data'"));
assert!(state_query_script(&r).contains("mountpoint -q '/mnt/data'"));
}
#[test]
fn fj165_mount_check_label_injection_neutralized() {
let mut r = mount_resource();
r.path = Some("x$(touch /tmp/pwn)".to_string());
let script = check_script(&r);
assert!(script.contains("echo 'mounted:x$(touch /tmp/pwn)'"));
assert!(script.contains("echo 'unmounted:x$(touch /tmp/pwn)'"));
assert!(!script.contains("echo mounted:x$(touch"));
assert!(!script.contains("' $(touch"));
}
}