feat: add buildCopyCommand for safe clipboard command construction
Uses validateClipboardCmd to reject untrusted input and properly escapes single quotes in text to prevent shell injection.
This commit is contained in:
parent
a86d5e279d
commit
3c31209ff1
2 changed files with 28 additions and 2 deletions
|
|
@ -32,6 +32,12 @@ function validateClipboardCmd(cmd) {
|
|||
return typeof cmd === "string" && safeClipboardCmds.includes(cmd)
|
||||
}
|
||||
|
||||
function buildCopyCommand(text, clipboardCmd) {
|
||||
if (!validateClipboardCmd(clipboardCmd)) return null
|
||||
var escaped = text.replace(/'/g, "'\\''")
|
||||
return ["sh", "-c", "printf '%s' '" + escaped + "' | " + clipboardCmd]
|
||||
}
|
||||
|
||||
function errorMessage(cmd) {
|
||||
var messages = {
|
||||
"up": "Failed to connect to Tailscale",
|
||||
|
|
@ -46,5 +52,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, validateClipboardCmd }
|
||||
module.exports = { parsePeers, makeExitNodeCommand, findActiveExitNode, errorMessage, validateClipboardCmd, buildCopyCommand }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, validateClipboardCmd } = lib
|
||||
const { parsePeers, makeExitNodeCommand, findActiveExitNode, errorMessage, validateClipboardCmd, buildCopyCommand } = lib
|
||||
|
||||
test("parsePeers extracts exitNode from ExitNodeOption", () => {
|
||||
const peerMap = {
|
||||
|
|
@ -96,3 +96,23 @@ test("validateClipboardCmd rejects empty and falsy inputs", () => {
|
|||
assert.strictEqual(validateClipboardCmd(null), false)
|
||||
assert.strictEqual(validateClipboardCmd(undefined), false)
|
||||
})
|
||||
|
||||
// --- buildCopyCommand ---
|
||||
|
||||
test("buildCopyCommand returns safe command for whitelisted clipboard tool", () => {
|
||||
const cmd = buildCopyCommand("hello", "wl-copy")
|
||||
assert.ok(Array.isArray(cmd))
|
||||
assert.strictEqual(cmd[0], "sh")
|
||||
assert.strictEqual(cmd[1], "-c")
|
||||
})
|
||||
|
||||
test("buildCopyCommand returns null for invalid clipboard command", () => {
|
||||
assert.strictEqual(buildCopyCommand("hello", "rm -rf /"), null)
|
||||
assert.strictEqual(buildCopyCommand("hello", ""), null)
|
||||
assert.strictEqual(buildCopyCommand("hello", null), null)
|
||||
})
|
||||
|
||||
test("buildCopyCommand safely handles text with special characters", () => {
|
||||
const cmd = buildCopyCommand("it's a 'test' with \"quotes\" and\nnewlines", "wl-copy")
|
||||
assert.ok(Array.isArray(cmd))
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue