feat: add validateClipboardCmd with whitelist-based input validation

Prevents shell injection by only allowing known-safe clipboard commands
(dms cl copy, wl-copy, clipmanctl copy, none).
This commit is contained in:
Vybe (Coding Agent) 2026-05-20 10:08:59 +00:00
parent 7c12793b98
commit a86d5e279d
2 changed files with 30 additions and 2 deletions

View file

@ -26,6 +26,12 @@ function findActiveExitNode(peerMap) {
return ""
}
var safeClipboardCmds = ["dms cl copy", "wl-copy", "clipmanctl copy", "none"]
function validateClipboardCmd(cmd) {
return typeof cmd === "string" && safeClipboardCmds.includes(cmd)
}
function errorMessage(cmd) {
var messages = {
"up": "Failed to connect to Tailscale",
@ -40,5 +46,5 @@ function errorMessage(cmd) {
// CommonJS export for Node.js tests (ignored by QML)
if (typeof module !== "undefined" && module.exports) {
module.exports = { parsePeers, makeExitNodeCommand, findActiveExitNode, errorMessage }
module.exports = { parsePeers, makeExitNodeCommand, findActiveExitNode, errorMessage, validateClipboardCmd }
}

View file

@ -1,7 +1,7 @@
import { test } from "node:test"
import assert from "node:assert"
import lib from "../tailscalectl/lib.js"
const { parsePeers, makeExitNodeCommand, findActiveExitNode, errorMessage } = lib
const { parsePeers, makeExitNodeCommand, findActiveExitNode, errorMessage, validateClipboardCmd } = lib
test("parsePeers extracts exitNode from ExitNodeOption", () => {
const peerMap = {
@ -74,3 +74,25 @@ test("errorMessage returns generic message for unknown command", () => {
const msg = errorMessage("unknown", 1)
assert.strictEqual(msg, "Tailscale command failed")
})
// --- validateClipboardCmd ---
test("validateClipboardCmd accepts whitelisted values", () => {
assert.strictEqual(validateClipboardCmd("dms cl copy"), true)
assert.strictEqual(validateClipboardCmd("wl-copy"), true)
assert.strictEqual(validateClipboardCmd("clipmanctl copy"), true)
assert.strictEqual(validateClipboardCmd("none"), true)
})
test("validateClipboardCmd rejects malicious inputs", () => {
assert.strictEqual(validateClipboardCmd("rm -rf /"), false)
assert.strictEqual(validateClipboardCmd("echo hi; malicious"), false)
assert.strictEqual(validateClipboardCmd("$(whoami)"), false)
assert.strictEqual(validateClipboardCmd("wl-copy || rm -rf /"), false)
})
test("validateClipboardCmd rejects empty and falsy inputs", () => {
assert.strictEqual(validateClipboardCmd(""), false)
assert.strictEqual(validateClipboardCmd(null), false)
assert.strictEqual(validateClipboardCmd(undefined), false)
})