From dddd9218f6c2871a1e466c2beef926aa90f1e1b2 Mon Sep 17 00:00:00 2001 From: vybe Date: Wed, 20 May 2026 10:16:02 +0000 Subject: [PATCH] feat: add parseClipboardDetection for safe clipboard tool parsing Trims output and validates against whitelist, falling back to 'none' for empty, multi-line, or unexpected values. --- tailscalectl/lib.js | 7 ++++++- test/lib.test.js | 29 ++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/tailscalectl/lib.js b/tailscalectl/lib.js index da35592..2c3eda4 100644 --- a/tailscalectl/lib.js +++ b/tailscalectl/lib.js @@ -38,6 +38,11 @@ function buildCopyCommand(text, clipboardCmd) { return ["sh", "-c", "printf '%s' '" + escaped + "' | " + clipboardCmd] } +function parseClipboardDetection(stdout) { + var trimmed = typeof stdout === "string" ? stdout.trim() : "" + return validateClipboardCmd(trimmed) ? trimmed : "none" +} + function errorMessage(cmd) { var messages = { "up": "Failed to connect to Tailscale", @@ -52,5 +57,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, buildCopyCommand } + module.exports = { parsePeers, makeExitNodeCommand, findActiveExitNode, errorMessage, validateClipboardCmd, buildCopyCommand, parseClipboardDetection } } diff --git a/test/lib.test.js b/test/lib.test.js index 598b1f5..edc92a9 100644 --- a/test/lib.test.js +++ b/test/lib.test.js @@ -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, buildCopyCommand } = lib +const { parsePeers, makeExitNodeCommand, findActiveExitNode, errorMessage, validateClipboardCmd, buildCopyCommand, parseClipboardDetection } = lib test("parsePeers extracts exitNode from ExitNodeOption", () => { const peerMap = { @@ -116,3 +116,30 @@ 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)) }) + +// --- parseClipboardDetection --- + +test("parseClipboardDetection passes through expected output strings", () => { + assert.strictEqual(parseClipboardDetection("dms cl copy"), "dms cl copy") + assert.strictEqual(parseClipboardDetection("wl-copy"), "wl-copy") + assert.strictEqual(parseClipboardDetection("clipmanctl copy"), "clipmanctl copy") + assert.strictEqual(parseClipboardDetection("none"), "none") +}) + +test("parseClipboardDetection trims extra whitespace", () => { + assert.strictEqual(parseClipboardDetection(" wl-copy \n"), "wl-copy") +}) + +test("parseClipboardDetection falls back to none for multi-line output", () => { + assert.strictEqual(parseClipboardDetection("wl-copy\nrm -rf /"), "none") +}) + +test("parseClipboardDetection falls back to empty output", () => { + assert.strictEqual(parseClipboardDetection(""), "none") + assert.strictEqual(parseClipboardDetection(" "), "none") +}) + +test("parseClipboardDetection falls back to none for unexpected strings", () => { + assert.strictEqual(parseClipboardDetection("xclip -selection clipboard"), "none") + assert.strictEqual(parseClipboardDetection("custom-tool"), "none") +})