diff --git a/packages/mosaic/framework/tools/git/test-wrapper-guard.sh b/packages/mosaic/framework/tools/git/test-wrapper-guard.sh index 309c64d4..3ab9fd85 100755 --- a/packages/mosaic/framework/tools/git/test-wrapper-guard.sh +++ b/packages/mosaic/framework/tools/git/test-wrapper-guard.sh @@ -282,6 +282,18 @@ FIXTURES="$TMP/fixtures.tsv" printf '2\t{"tool_input":{"command":"curl -K/tmp/provider-write.cfg"}}\tcurl accepts the value attached to the short flag\t--config/-K\n' printf '2\t{"tool_input":{"command":"curl -sK /tmp/provider-write.cfg"}}\tand inside a bundle, which a space-separated test does not see\t--config/-K\n' printf '0\t{"tool_input":{"command":"tar -K /tmp/archive.tar"}}\t-K on a command that is not curl is not this hook'"'"'s business\n' + # curl by any ordinary path spelling. The first version of the config check + # matched the bare word only, so these three executed the same wrapped write + # while the guard reported clean. Recognizing only the unqualified name is + # caller-name parsing, and that is the class this file exists to refuse. + printf '2\t{"tool_input":{"command":"/usr/bin/curl --config /tmp/provider-write.cfg"}}\tan absolute path is the same invocation, not a different one\t--config/-K\n' + printf '2\t{"tool_input":{"command":"env /usr/bin/curl -K/tmp/provider-write.cfg"}}\tand it is still curl behind env, with the value attached\t--config/-K\n' + printf '2\t{"tool_input":{"command":"./curl --config /tmp/provider-write.cfg"}}\ta relative path costs two characters and used to be enough\t--config/-K\n' + # The prefix must end at a slash: a basename that merely ENDS in curl is a + # different program, and blocking it would be the over-block that gets a guard + # routed around rather than fixed. + printf '0\t{"tool_input":{"command":"mycurl --config /tmp/provider-write.cfg"}}\tmycurl is not curl, and over-blocking is its own failure\n' + printf '0\t{"tool_input":{"command":"/opt/x/curl-wrapper --config /tmp/provider-write.cfg"}}\tnor is curl-wrapper, whose name only starts the same way\n' # Percent-encoded endpoints. Not hypothetical: /issues/1174 and /iss%%75es/1174 # both returned HTTP 200 with the same object from the live forge, so the diff --git a/packages/mosaic/framework/tools/git/wrapper-guard.sh b/packages/mosaic/framework/tools/git/wrapper-guard.sh index abc8e6e2..763536b6 100755 --- a/packages/mosaic/framework/tools/git/wrapper-guard.sh +++ b/packages/mosaic/framework/tools/git/wrapper-guard.sh @@ -290,7 +290,18 @@ fi # guard that recognizes only the space- and equals-separated forms is defeated by # deleting one character. Scoped to curl so that `eslint --config .eslintrc.json` # and every other tool with a --config flag are untouched. -if printf '%s' "$CMD" | grep -Eq '(^|[[:space:]|;&(])curl([[:space:]]|$)' \ +# +# curl is matched as a BASENAME, not as a bare word: `/usr/bin/curl`, `./curl` +# and `env /usr/bin/curl` are ordinary spellings of the same invocation, and the +# first version of this check saw none of them. Recognizing only the unqualified +# name is caller-name parsing, which is the failure class this file removed +# elsewhere and which came straight back in with this control. +# +# A wrapper script that execs curl on the operator's behalf is still invisible +# here, because neither the name nor the request appears in the command text. +# That is a real limit of inspecting a command string rather than a defect this +# regex can close, and it is stated rather than left for the next reader to find. +if printf '%s' "$CMD" | grep -Eq '(^|[[:space:]|;&(])([^[:space:]|;&()]*/)?curl([[:space:]]|$)' \ && printf '%s' "$CMD" | grep -Eq -- '(^|[[:space:]])(-[A-Za-z]*K([[:space:]=]|$|[^[:space:]])|--config([[:space:]=]|$))'; then cat <