Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions features/db-import.feature
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,22 @@ Feature: Import a WordPress database
🍣
"""

@require-sqlite
Scenario: `wp db import` rejects dot-commands in SQLite dump files
Given a WP install
And a malicious_sqlite.sql file:
"""
CREATE TABLE wp_cli_sqlite_meta (id int NOT NULL);
.shell touch side_effect_sqlite.txt
"""

When I try `wp db import malicious_sqlite.sql`
Then STDERR should contain:
"""
SQLite dot-commands are not allowed in import files.
"""
And the side_effect_sqlite.txt file should not exist
Comment on lines +260 to +265

# SQLite does not use the MySQL client and has no concept of SQL modes.
@require-mysql-or-mariadb
Scenario: `wp db import` adapts the SQL mode via --init-command by default
Expand Down
4 changes: 4 additions & 0 deletions src/DB_Command_SQLite.php
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,10 @@ protected function sqlite_import( $file, $assoc_args ) {
$contents = (string) file_get_contents( $file );
}

if ( preg_match( '/^\s*\.[a-zA-Z]+/m', $contents ) ) {
WP_CLI::error( 'SQLite dot-commands are not allowed in import files.' );
Comment on lines 454 to +458
Comment on lines +457 to +458

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document the new import restriction.

sqlite_import() now rejects SQLite dot-commands, but its docblock at Line 423 only describes generic SQL import behavior. Update it to document the rejection before preprocessing and execution.

As per coding guidelines, update relevant inline code documentation when changes affect user-facing functionality.

Suggested docblock update
 /**
  * Import SQL into SQLite database.
+ *
+ * Rejects SQLite dot-commands before preprocessing and execution.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/DB_Command_SQLite.php` around lines 457 - 458, Update the docblock for
sqlite_import() to document that import files containing SQLite dot-commands are
rejected before preprocessing and execution. Keep the existing generic SQL
import documentation intact and describe this user-facing restriction alongside
it.

Source: Coding guidelines

}

Comment on lines +457 to +460

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/sh
set -eu

command -v sqlite3 >/dev/null
command -v python3 >/dev/null

tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
side_effect="$tmpdir/side_effect"

cat >"$tmpdir/dump.sql" <<EOF
/*
.shell touch "$side_effect"
*/
CREATE TABLE test_table (id INTEGER);
EOF

sqlite3 "$tmpdir/test.db" <"$tmpdir/dump.sql"
test ! -e "$side_effect"

python3 - "$tmpdir/dump.sql" <<'PY'
import re
import sys

contents = open(sys.argv[1], encoding="utf-8").read()
assert re.search(r'^\s*\.[a-zA-Z]+', contents, re.MULTILINE)
PY

Repository: wp-cli/db-command

Length of output: 155


🏁 Script executed:

#!/bin/sh
set -eu

echo "== repo files =="
git ls-files | sed -n '1,80p'

echo "== target file outline/size =="
wc -l src/DB_Command_SQLite.php
ast-grep outline src/DB_Command_SQLite.php --view expanded 2>/dev/null | sed -n '1,220p' || true

echo "== relevant target lines =="
cat -n src/DB_Command_SQLite.php | sed -n '400,490p'

echo "== imports/import methods references =="
rg -n "SQLite dot-commands|import files|\.import|import|DB_Command_SQLite" src tests -S

Repository: wp-cli/db-command

Length of output: 8372


🌐 Web query:

SQLite command-line shell dot commands SQL comments dot command at left margin documentation

💡 Result:

In the SQLite command-line interface (CLI), dot-commands—also known as meta-commands—are special instructions interpreted directly by the sqlite3 program rather than by the SQLite library [1][2]. The official rules governing dot-commands are as follows: 1. Left Margin Requirement: A dot-command must begin with a "." at the absolute left margin of the input line, with no preceding whitespace [1][3]. 2. Single-Line Constraint: Dot-commands must be entirely contained on a single input line [1][4]. 3. SQL Interaction: Dot-commands cannot be used in the middle of an ordinary SQL statement and cannot be executed at a continuation prompt [1][4]. 4. Comment Syntax: There is no comment syntax available for dot-commands; they do not recognize or support comments [1][5]. While dot-commands themselves do not support comments, the SQLite CLI does support whole-line comments for SQL scripts [4]. These comments must begin with a '#' character at the left margin, with no preceding whitespace, and extend to the end of the line [4]. For a complete list of available dot-commands in your current version, you can execute the.help command within the sqlite3 shell [1][6].

Citations:


🏁 Script executed:

#!/bin/sh
set -eu

tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT

bin="$(command -v sqlite3 || echo '"sqlite3 unavailable"')"
echo "sqlite3=$bin"
if [ "$bin" = 'sqlite3 unavailable' ]; then
  exit 1
fi

sqlite3 "$tmpdir/out.db" <<'SQL' | sed -n '1,80p'
-- test CLI behavior against representative inputs used by the raw regex
.create abc
select ".x" as q;
CREATE TABLE test_table (id INTEGER);
/*
.shell touch "$tmpdir/sideeffect"
*/
INSERT INTO test_table VALUES (1);
SQL

test -e "$tmpdir/out.db" || exit 2

echo "== raw regex matches =="
python3 - <<'PY'
import re, sys
patterns = [
    ('leading-dot', re.compile(r'^\s*\.[a-zA-Z]+', re.MULTILINE)),
    ('dot-at-left', re.compile(r'^\.[a-zA-Z]+', re.MULTILINE)),
]
for name, pat in patterns:
    hits = []
    with open(sys.argv[1]) as f:
        for i,line in enumerate(f,1):
            if pat.search(line):
                hits.append((i,line.rstrip()))
        print(name, hits)
PY "$tmpdir/dump.sql" 2>/dev/null || true

echo "== CLI behavior with exact regex-like leading whitespace dot command =="
cat >"$tmpdir/exact.sql" <<EOF
  .shell touch "$tmpdir/regexmatched"
  CREATE TABLE d (id INTEGER);
EOF
rm -f "$tmpdir/regexmatched"
sqlite3 "$tmpdir/exact.db" <"$tmpdir/exact.sql" | sed -n '1,40p' || true
if [ -e "$tmpdir/regexmatched" ]; then
  echo "regexmatched exists"
else
  echo "regexmatched does not exist"
fi

cat >"$tmpdir/exact2.sql" <<EOF
# comment before
CREATE TABLE e (id INTEGER);
EOF
sqlite3 "$tmpdir/exact2.db" <"$tmpdir/exact2.sql" || true

Repository: wp-cli/db-command

Length of output: 227


🌐 Web query:

site:sqlite.org/cli.html dot-commands left margin SQLite docs

💡 Result:

In SQLite, dot-commands (special commands used in the command-line shell) must begin with a "." character at the left margin of the input line [1][2]. This means there can be no preceding whitespace before the dot [1][2]. According to the official SQLite documentation, the following rules apply to dot-command syntax [1][2]: - The command must begin with a "." at the left margin with no preceding whitespace [1][2]. - It must be entirely contained on a single input line [1][2]. - Dot-commands cannot be used in the middle of an ordinary SQL statement or at a continuation prompt [1][2]. - There is no comment syntax available for dot-commands [1][2]. Similarly, whole-line comments that begin with a "#" character also require that there be no whitespace prior to the initial "#" [1][2].

Citations:


Align dot-command detection with SQLite CLI rules.

The current ^\s*\.[a-zA-Z]+ pattern rejects tokens after leading whitespace and inside SQL comments. SQLite requires dot-commands to start at the left margin, be single-line, and not appear in the middle of an ordinary SQL statement. Use a stateful scanner matching that state, or document this import policy as intentionally stricter than SQLite CLI behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/DB_Command_SQLite.php` around lines 457 - 460, Update the dot-command
validation in the import handling around the preg_match check to follow SQLite
CLI rules: recognize commands only when the dot starts at the left margin, on
its own statement line, and outside SQL comments or ordinary SQL statements.
Replace the current broad regex with a stateful scanner, or explicitly document
and preserve the intentionally stricter import policy if that behavior is
required.

Source: MCP tools

// Ignore errors about unique constraints and existing indexes.
$contents = str_replace( 'INSERT INTO', 'INSERT OR IGNORE INTO', $contents );
$contents = preg_replace( '/\bCREATE TABLE (?!IF NOT EXISTS\b)/i', 'CREATE TABLE IF NOT EXISTS ', $contents );
Expand Down
Loading