chubot 2 days ago

The article mentioned printf '%q ', but it is a bit hard to find. Here is a handy way to remember it.

First, define this function:

    quote-argv() { printf '%q ' "$@"; }
    # (uses subtle vectorization of printf over args)
Now this works correctly:

    ssh example.com "$(quote-argv ls 'file with spaces')"
    ls: cannot access 'file with spaces': No such file or directory
In contrast to:

    $ ssh example.com ls 'file with spaces'
    ls: cannot access 'file': No such file or directory
    ls: cannot access 'with': No such file or directory
    ls: cannot access 'spaces': No such file or directory
And yes the "hidden argv join" of ssh is VERY bad, and it is repeated in shell's eval builtin.

They should both only take a SINGLE arg.

It is basically a self-own because spaces are an OPERATOR in shell! (the operator that separates words)

When you concatenate operators and variables, then you are mixing code and data, which is a security problem.

---

As for the exec workaround, I think this is also deficiency of shell. Oils will probably grow an 'invoke' builtin which generalizes 'command' and 'builtin', which are non-orthogonal.

'command true' means "external or builtin" (disabling shell function lookup), but there should be something that means "external only".

2
blueflow 2 days ago

> hidden argv join

It is not hidden. It is written down in plain sight:

  A complete command line may be specified as command, or it may have additional arguments. If supplied, the arguments will be appended to the command, separated by spaces, before it is sent to the server to be executed.
- third line in `man 1 ssh`

chubot 2 days ago

It's hidden in the sense that it creates ambiguity at the usage site. Compare with sudo:

    $ sudo ls 'file with spaces'
    ls: cannot access 'file with spaces': No such file or directory
If ssh (and sh eval) did not accept multiple arguments, then this wouldn't even get to ls:

    $ ssh example.com ls 'file with spaces'
    ls: cannot access 'file': No such file or directory
    ls: cannot access 'with': No such file or directory
    ls: cannot access 'spaces': No such file or directory
Accepting argv is better. Or forcing this is better:

    $ ssh example.com "ls 'file with spaces'"
So it's clear it's a single shell string.

Accepting a shell string is sometimes OK, but silently joining multiple args is useless, and insecure.

"RTFM" is not a good answer when security is involved.

blueflow 2 days ago

This stubborn attitude to refuse to consult the documentation at all and then expect the tool to work according to your preconceptions.

Tools do have rough edges, if you don't want to learn about them, you will get bitten.

nothrabannosir 1 day ago

This statement can be true without contradicting anything anyone said upstream. Otherwise could use it to justify just about any bad design decision.

Yes it’s in the docs. Yes people who carefully read the docs won’t get bitten. Also yes the design could be improved so people don’t make this mistake even without reading the docs.

Both things can be true. We’re currently only talking about the latter, though.

blueflow 1 day ago

> We’re currently only talking about the latter, though.

I'm surprised, as i started this subthread explicitly to contest that the argv join is "hidden".

pwdisswordfishz 11 hours ago

> Tools do have rough edges, if you don't want to learn about them, you will get bitten.

I presume you consider INTERCAL to be a sanely designed programming language.

blueflow 11 hours ago

I'm not defending SSH's design, im criticizing peoples unwillingness to learn about the design as it is so they can work around it.

Edit: The INTERCAL handbook is a great read, and despite being satirical, it is more detailed and qualified than the documentation of some other popular projects.

chubot 1 day ago

It’s a design mistake because it adds exactly zero functionality.

The only thing it adds is insecurity.

If the feature didn’t exist, then it wouldn’t need to be documented, and the world would be better.

immibis 15 hours ago

This very stubborn attitude to defend a bad design because it's documented.

Bugs can be fixed.

blueflow 12 hours ago

It is bad design, but your idea of something does not make anything non-conforming a bug.

scbrg 10 hours ago

That's honestly not particularly clear. It doesn't say the command will be invoked by a shell on the remote host. Sure the whole "separated by spaces" thing sorta implies it will, as spaces don't mean much to anything but a shell, but it's still fairly vague.

In fact, later on the man page only mentions a shell in the part that talks about the behavior when no additional arguments are given:

  When the user's identity has been accepted by the server, the server either executes the given command in a non-interactive session or, if no command has been specified, logs into the machine and gives the user a normal shell as an interactive session.
The wording "executes the given command" would generally not imply "I'll just throw it at $SHELL and see what happens".

A few lines later it gets even more confusing:

  The session terminates when the command or shell on the remote machine exits and all X11 and TCP connections have been closed.
...which I definitely would say suggests that either a shell is executed or the command supplied as argument to ssh. That it means "command as interpreted by a shell on the remote host" is far from obvious.

blueflow 2 hours ago

> The wording "executes the given command" would generally not imply "I'll just throw it at $SHELL and see what happens".

"command" means exactly that. Evaluation by shell. With that in mind, the manual page should read less ambiguous to you.

I actually don't have a good source for that, but you can check the execve(2) manpage. If command would refer to the execution of an argument vector, it would have been mentioned in there.

The other meaning of "command" refers to specific programs like those in /bin.

o11c 2 days ago

Use ' %q' and you also fix the problem of program names starting with a dash.

chubot 2 days ago

Ah yes, that's clever:

    $ sh -c "$(quote-argv -echo 'file with spaces')"
    sh: 0: Illegal option -h

    $ sh -c "$(quote-argv-left -echo 'file with spaces')"
    sh: 1: -echo: not found
Over ssh:

    $ ssh example.com "$(quote-argv-left -dashtest 'file with spaces')"
    -dashtest
    file with spaces