diff --git a/R/notifications.R b/R/notifications.R index 1f9c9e9..e7b56a9 100644 --- a/R/notifications.R +++ b/R/notifications.R @@ -84,20 +84,18 @@ beep <- function(msg = "done", .send_desktop_notify <- function(msg, status) { # Linux: notify-send if (.has_command("notify-send")) { - cmd <- paste0("notify-send '", status, "' '", gsub("'", "'\\''", msg), "'") - system(cmd, ignore.stdout = TRUE, ignore.stderr = TRUE) + system2("notify-send", args = c(status, msg), + stdout = TRUE, stderr = TRUE) return(invisible(NULL)) } # macOS: osascript if (.has_command("osascript")) { - escaped_msg <- gsub('"', '\\"', msg) - escaped_status <- gsub('"', '\\"', status) - cmd <- paste0( - "osascript -e 'display notification \"", escaped_msg, - "\" with title \"", escaped_status, "\"'" - ) - system(cmd, ignore.stdout = TRUE, ignore.stderr = TRUE) + system2("osascript", + args = c("-e", + paste0("display notification \"", msg, + "\" with title \"", status, "\"")), + stdout = TRUE, stderr = TRUE) return(invisible(NULL)) } @@ -120,21 +118,25 @@ beep <- function(msg = "done", timestamp = format(Sys.time(), "%Y-%m-%dT%H:%M:%S%z") ), auto_unbox = TRUE) - # Use curl via system() for maximum compatibility (no extra R deps) + # Use curl via system2() for maximum compatibility (no extra R deps). + # system2() passes arguments directly to the executable without shell + # interpolation, avoiding command injection. if (.has_command("curl")) { - cmd <- paste0( - "curl -s -X POST -H 'Content-Type: application/json' ", - "-d '", payload, "' '", url, "' ", - "--max-time ", timeout - ) - system(cmd, ignore.stdout = TRUE, ignore.stderr = TRUE) + system2("curl", + args = c("-s", "-X", "POST", + "-H", "Content-Type: application/json", + "-d", payload, + url, + "--max-time", as.character(timeout)), + stdout = TRUE, stderr = TRUE) } else if (.has_command("wget")) { - cmd <- paste0( - "wget -q -O /dev/null --post-data='", payload, - "' --header='Content-Type: application/json' ", - "--timeout=", timeout, " '", url, "'" - ) - system(cmd, ignore.stdout = TRUE, ignore.stderr = TRUE) + system2("wget", + args = c("-q", "-O", "/dev/null", + paste0("--post-data=", payload), + paste0("--header=Content-Type: application/json"), + paste0("--timeout=", timeout), + url), + stdout = TRUE, stderr = TRUE) } invisible(NULL)