From 0650a250405af1e480b1ecf8c33bc960b9f333f5 Mon Sep 17 00:00:00 2001 From: umutcagand Date: Tue, 20 Oct 2026 03:21:44 +0300 Subject: [PATCH] fix(jsontools): read Node.js input from stdin (#14190) --- plugins/jsontools/jsontools.plugin.zsh | 19 +++++-- plugins/jsontools/tests/jsontools.test.zsh | 64 ++++++++++++++++++++++ 2 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 plugins/jsontools/tests/jsontools.test.zsh diff --git a/plugins/jsontools/jsontools.plugin.zsh b/plugins/jsontools/jsontools.plugin.zsh index 49828d32d..993138d26 100644 --- a/plugins/jsontools/jsontools.plugin.zsh +++ b/plugins/jsontools/jsontools.plugin.zsh @@ -22,14 +22,17 @@ fi # Define json tools for each method case "$JSONTOOLS_METHOD" in node) - # node doesn't make it easy to deal with stdin, so we pass it as an argument with xargs -0 + # Read stdin directly so input is not limited by command-line argument size. function pp_json() { - xargs -0 node -e 'console.log(JSON.stringify(JSON.parse(process.argv[1]), null, 4));' + node -e ' + const input = require("fs").readFileSync(0, "utf8"); + console.log(JSON.stringify(JSON.parse(input), null, 4)); + ' } function is_json() { - xargs -0 node -e ' + node -e ' try { - json = JSON.parse(process.argv[1]); + JSON.parse(require("fs").readFileSync(0, "utf8")); console.log("true"); process.exit(0); } catch (e) { @@ -39,10 +42,14 @@ case "$JSONTOOLS_METHOD" in ' } function urlencode_json() { - xargs -0 node -e "console.log(encodeURIComponent(process.argv[1]))" + node -e ' + console.log(encodeURIComponent(require("fs").readFileSync(0, "utf8"))); + ' } function urldecode_json() { - xargs -0 node -e "console.log(decodeURIComponent(process.argv[1]))" + node -e ' + console.log(decodeURIComponent(require("fs").readFileSync(0, "utf8"))); + ' } ;; python3) diff --git a/plugins/jsontools/tests/jsontools.test.zsh b/plugins/jsontools/tests/jsontools.test.zsh new file mode 100644 index 000000000..3b594c165 --- /dev/null +++ b/plugins/jsontools/tests/jsontools.test.zsh @@ -0,0 +1,64 @@ +#!/usr/bin/env zsh + +# Run with: zsh -df plugins/jsontools/tests/jsontools.test.zsh +emulate -L zsh +setopt err_return + +(( $+commands[node] )) || { + print -u2 'These tests require Node.js.' + exit 1 +} + +JSONTOOLS_METHOD=node +source "${0:A:h:h}/jsontools.plugin.zsh" +local test_dir=$(mktemp -d) + +check_output() { + local tool=$1 description=$2 expected_status=${3:-0} + local -i result=0 + $tool < "$test_dir/input" > "$test_dir/actual" || result=$? + if (( result != expected_status )) \ + || ! cmp -s "$test_dir/expected" "$test_dir/actual"; then + print -u2 -r -- "FAIL: $description ($tool exited $result)" + return 1 + fi + print -r -- "PASS: $description" +} + +{ + print -rn -- '{"value":42}' > "$test_dir/input" + printf '{\n "value": 42\n}\n' > "$test_dir/expected" + check_output pp_json 'pretty-print a small document' + + # Larger than the 128 KiB limit for a single command-line argument. + local large_value=${(l:262144::x:):-} + print -rn -- '{"value":"'"$large_value"'"}' > "$test_dir/input" + printf '{\n "value": "%s"\n}\n' "$large_value" > "$test_dir/expected" + check_output pp_json 'pretty-print a large document' + print -r -- true > "$test_dir/expected" + check_output is_json 'validate a large document' + + print -r -- '%7B%22value%22%3A%22'"$large_value"'%22%7D' \ + > "$test_dir/expected" + check_output urlencode_json 'encode a large document' + local encoded=$(< "$test_dir/expected") + print -rn -- "$encoded" > "$test_dir/input" + print -r -- '{"value":"'"$large_value"'"}' > "$test_dir/expected" + check_output urldecode_json 'decode a large document' + + # Passed as an argument, node used to parse this as its own option. + print -rn -- '--version' > "$test_dir/input" + print -r -- '--version' > "$test_dir/expected" + check_output urlencode_json 'encode input that starts with --' + + print -rn -- '{invalid}' > "$test_dir/input" + print -r -- false > "$test_dir/expected" + check_output is_json 'reject invalid JSON' 1 + + printf '1\0002' > "$test_dir/input" + check_output is_json 'reject input with NUL bytes' 1 + + print 'All jsontools Node.js tests passed.' +} always { + rm -rf -- "$test_dir" +}