shift-org / shift-org/shift-docs

Passing arguments to `./shift` subcommands is broken by unquoted `$@`

Open Beginner friendly
#1,081 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
30
Forks
25
Avg merge
9m
Merged PRs (30d)
1

Description

Any ./shift subcommand that forwards arguments mangles them, because $@ is passed unquoted at every level of the call chain. The most visible casualty is ./shift mysql -e "<sql>", which never runs the SQL and instead prints mysql's usage text.

Reproducing

$ ./shift mysql -e "SHOW VARIABLES LIKE 'character_set%';"

Expected: the variables. Actual: mysql's help/usage output.

Cause

The SQL string is split on whitespace before mysql ever sees it. shift is a bash script (#!/usr/bin/env bash), and unquoted $@ in bash re-splits each positional parameter on IFS and then glob-expands the results. The relevant chain, all unquoted:

# dispatcher, bottom of the file
sub_${SUB_CMD} $@

sub_mysql() { #                   - Open a mysql prompt with the db selected
    cd "${ROOT}"
    sub_compose exec db mysql -u ${MYSQL_USER} -h db -P 3306 -p"${MYSQL_PASSWORD}" ${MYSQL_DATABASE} $@
}

sub_compose() { # <cmd...>        - Run a compose with associated files
    docker compose $@
}

So -e "SHOW VARIABLES LIKE 'character_set%';" arrives at mysql as five separate arguments:

[-e] [SHOW] [VARIABLES] [LIKE] ['x%';]

mysql receives -e SHOW, then treats VARIABLES, LIKE, and the rest as stray arguments, and bails out to its usage text.

Glob expansion makes it worse. Because the split words are also subject to pathname expansion, an asterisk in the SQL expands against the current directory:

$ ./shift mysql -e "SELECT * FROM calevent LIMIT 1"

delivers this to mysql:

[-e] [SELECT] [app] [backend] [bin] [cal] [CLAUDE.md] [db-local-empty.sql]
[docker-compose.yml] [docs] [LICENSE.md] [netlify.toml] ... [services]

Note that quoting $@ in sub_mysql alone is not sufficient, since the arguments are re-split again inside sub_compose, and once more by the dispatcher.

Affected subcommands

Every one that forwards $@: mysql, mysql-pipe, mysqldump, compose, attach, logs, and the dispatcher itself. mysql-pipe happens to work in practice only because SQL reaches it on stdin rather than as an argument.

Workaround

Pipe the SQL instead of passing it as an argument:

printf "SHOW VARIABLES LIKE 'character_set%%';\n" | ./shift mysql-pipe

Suggested fix

Quote "$@" at each level (dispatcher, sub_compose, and each sub_* that forwards arguments). Worth a pass over the other unquoted expansions in the same lines while in there.

This is easy to trip over and hard to diagnose, since mysql's usage output does not look like a quoting error. It also bit the verification steps written into #1077.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in ./shift, tracing the dispatcher, sub_mysql, and sub_compose call chain named in the issue. Reproduce the mysql command, then inspect each affected forwarding subcommand: mysql, mysql-pipe, mysqldump, compose, attach, and logs. Done when arguments remain intact through the chain, the SQL command returns variables instead of usage output, and glob characters are not expanded.

Written by the indexing model from the issue text.

Assessment

Tech stack
bash, docker-compose
Domain
cli, devops
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.