diff --git a/packages/mosaic/framework/tools/git/test-wrapper-guard.sh b/packages/mosaic/framework/tools/git/test-wrapper-guard.sh index aaaf6587..52dd6d21 100755 --- a/packages/mosaic/framework/tools/git/test-wrapper-guard.sh +++ b/packages/mosaic/framework/tools/git/test-wrapper-guard.sh @@ -95,6 +95,22 @@ FIXTURES="$TMP/fixtures.tsv" # the whole command into code because it contains an unrelated `sh -c` is how # this blocked its author a second time. printf '0\t{"tool_input":{"command":"docker run --rm alpine sh -c '"'"'echo hi'"'"'\\necho \\"example: curl -d@b https://git.example.invalid/api/v1/repos/a/b/issues/1/comments\\" >> notes.md"}}\tan unrelated shell on another line does not promote quoted prose to code\n' + # --- round four. The guard was still reading the command as typed rather than + # as the shell will run it: a backslash before a newline is removed before + # anything else happens, so the endpoint token can be split across the join. + printf '2\t{"tool_input":{"command":"curl -d@b https://git.example.invalid/api/v1/repos/a/b/iss\\\\\\nues/1/comments"}}\ta line continuation inside the endpoint token is still that endpoint\n' + printf '2\t{"tool_input":{"command":"curl -d@b https://git.example.invalid/api/v1/repos/a/b/pu\\\\\\nlls/1/reviews"}}\tsame join, review endpoint\n' + printf '0\t{"tool_input":{"command":"cat >> notes.md < "$FIXTURES" fail=0 n=0 diff --git a/packages/mosaic/framework/tools/git/wrapper-guard.sh b/packages/mosaic/framework/tools/git/wrapper-guard.sh index 0250a109..cd539736 100755 --- a/packages/mosaic/framework/tools/git/wrapper-guard.sh +++ b/packages/mosaic/framework/tools/git/wrapper-guard.sh @@ -35,6 +35,15 @@ INPUT="$(cat)" CMD="$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null || true)" [ -z "$CMD" ] && exit 0 +# Read the command the SHELL will run, not the text as typed. A backslash before +# a newline is removed before anything else happens, so +# curl -d@b https://host/api/v1/repos/a/b/iss\ +# ues/1/comments +# executes the comments endpoint while the literal token `issues` never appears +# in the text. Every check below — position, URL, body, endpoint — reads the +# joined form, because that is the command. +CMD="$(printf '%s' "$CMD" | sed -e ':a' -e 'N' -e '$!ba' -e 's/\\\n//g')" + # Honour the override only when it is set in the command itself or the env. case "$CMD" in *MOSAIC_WRAPPER_OVERRIDE=1*) exit 0 ;; esac [ "${MOSAIC_WRAPPER_OVERRIDE:-0}" = "1" ] && exit 0 @@ -146,10 +155,18 @@ SKEL="$(printf '%s' "$CMD" | awk -v inv="$SHELL_EXECUTES_DATA" ' # `timeout 10 curl`, `/usr/bin/curl`. The `env` form matters most, because it is # precisely what an agent reaches for to keep a credential out of the global # environment — the careful spelling was the invisible one. -CMD_PREFIX='([A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*|env|command|builtin|exec|nohup|setsid|stdbuf|nice|ionice|sudo|doas|xargs|time|timeout|[0-9]+[smhd]?|-[^[:space:]]+)[[:space:]]+' +CMD_PREFIX='([A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*|env|command|builtin|exec|nohup|setsid|stdbuf|nice|ionice|sudo|doas|xargs|time|timeout|watch)[[:space:]]+' +# Their options take values: `sudo -u root curl` hid a live write because `root` +# was a word the list did not know. What is skipped is an OPTION and at most one +# value for it — not any word — so `xargs echo curl ...` stays allowed, because +# there `echo` is the command and curl is its argument. Plus a bare duration, +# which is `timeout`'s operand. +PREFIX_OPT='-[^[:space:]]+[[:space:]]+([^-][^[:space:]|;&(){}<>]*[[:space:]]+)?' +PREFIX_ARG='('"$PREFIX_OPT"'|[0-9]+[smhd]?[[:space:]]+)' # A path in front of the client is still the client. CLIENT='([^[:space:]]*/)?(curl|wget|httpie|http)' -CLIENT_AT_CMD_POS='(^|[;&|(){}]|`|\$\()[[:space:]]*('"$CMD_PREFIX"')*'"$CLIENT"'([[:space:]]|$)' +# `find -exec` opens command position the same way an operator does. +CLIENT_AT_CMD_POS='(^|[;&|(){}]|`|\$\(|-execdir|-exec)[[:space:]]*('"$CMD_PREFIX"'('"$CMD_PREFIX"'|'"$PREFIX_ARG"')*)?'"$CLIENT"'([[:space:]]|$)' if printf '%s' "$SKEL" | grep -Eq "$CLIENT_AT_CMD_POS" \ && printf '%s' "$CMD" | grep -Eq 'https?://'; then