framework: make tool discoverability, workspace placement and model tiering mechanical #1174
@@ -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 <<EOF\\nwe ran: curl -d@b https://git.example.invalid/api/v1/repos/a/b/iss\\\\\\nues/1/comments\\nEOF"}}\tjoining lines does not turn a document into a command\n'
|
||||
# Transparent prefixes take option VALUES, and the value was a word the list
|
||||
# did not know — so the client went missing again behind an ordinary `sudo -u`.
|
||||
printf '2\t{"tool_input":{"command":"sudo -u root curl -d@b https://git.example.invalid/api/v1/repos/a/b/issues/1/comments"}}\tan option value after a prefix does not hide the client\n'
|
||||
printf '2\t{"tool_input":{"command":"timeout --signal TERM 10 curl -d@b https://git.example.invalid/api/v1/repos/a/b/issues/1/comments"}}\tan option pair plus a duration does not hide the client\n'
|
||||
# ...but only an OPTION and its value are skipped, never any word: in
|
||||
# `xargs echo curl ...` the command is echo and the client is its argument.
|
||||
printf '0\t{"tool_input":{"command":"xargs echo curl -d@b https://git.example.invalid/api/v1/repos/a/b/issues/1/comments"}}\ta client passed as an argument to echo is not a call\n'
|
||||
printf '0\t{"tool_input":{"command":"sudo apt-get install curl"}}\tinstalling the client is not calling it\n'
|
||||
# Execution through another command still opens command position.
|
||||
printf '2\t{"tool_input":{"command":"find . -maxdepth 0 -exec curl -d@b https://git.example.invalid/api/v1/repos/a/b/issues/1/comments ;"}}\tfind -exec runs the client\n'
|
||||
} > "$FIXTURES"
|
||||
|
||||
fail=0 n=0
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user