Skip to content

v3.6.5 — ssh_db_query shell-injection security fix + row-count fix

Latest

Choose a tag to compare

@bvisible bvisible released this 30 Jun 07:57

🔒 Security

ssh_db_query could execute arbitrary shell commands on the remote host (#44 — thanks @technophile77)

The query builders interpolated the raw query into a double-quoted shell string (mysql -e "${query}", psql -c "${query}", mongo --eval "${query}"), so the remote shell evaluated backticks `…` and $(…)/$VAR before the database engine ever saw the query. The "SELECT-only" tool could therefore run shell commands on the target server (e.g. SELECT `id` / SELECT '$(id)').

The query is now delivered to mysql/psql/mongo on stdin via a single-quoted heredoc (<<'__MCP_SQL_EOF__'), so the remote shell never parses it — only the database engine interprets the SQL/JS. A shared buildHeredoc() helper keeps the existing awk JSON-wrapper pipe on the heredoc opening line (MySQL JSON output is byte-for-byte unchanged) and defensively rejects a delimiter collision.

🐛 Fixed

  • Backtick-quoted identifiers were silently corrupted (#44) — the same shell-evaluation bug rewrote any query using backtick identifiers (hyphenated DB names, cross-database joins, reserved-word columns), returning empty/incorrect results with no error. The stdin heredoc restores them verbatim.
  • row_count was off by one (#45 — thanks @technophile77) — the handler counted cosmetic output lines (the leading [ of the MySQL JSON wrapper, psql header/separator lines): 1 row → 2, 13 rows → 14, 0 rows → 2. A new countQueryRows() derives the count from each engine's actual output structure (MySQL JSON entries, MySQL --batch lines, psql's (N rows) footer with a header/separator fallback, MongoDB printjson documents).

✅ Tests

  • tests/test-database-query-quoting.js — query text carried verbatim on stdin for all three builders, the awk pipe stays on the heredoc opening line, delimiter-collision guard, plus a stochastic round-trip of 500 random shell-metacharacter queries.
  • tests/test-database-row-count.js — the exact issue cases, a look-alike {"row": value inside a cell, MySQL text format, psql (N rows)/(1 row)/(0 rows) footers and the no-footer fallback, MongoDB single/multi-document counting, and empty/whitespace → 0.

Full changelog: CHANGELOG.md