Skip to content

fix(site): restrict each site's database grant to its own database - #512

Merged
mrrobot47 merged 6 commits into
EasyEngine:developfrom
mrrobot47:fix/restrict-existing-db-grants
Oct 12, 2026
Merged

mrrobot47 merged 6 commits into
EasyEngine:developfrom
mrrobot47:fix/restrict-existing-db-grants

Conversation

@mrrobot47

Copy link
Copy Markdown
Member

On the global database, _ and % in a database-level GRANT are wildcards even inside backticks. New sites have had their grant escaped since v4.13.1. This adds a container migration that does the same for existing sites: for each site on the global database, it adds a grant on the site's exact database name and then removes the old pattern grant. The site keeps access throughout.

  • Grants that were changed by hand, database users shared between sites and users without a % host are left as they are, with a warning naming the site.
  • Grant rows upgraded from MariaDB 10.2 are accepted.
  • If the global database is not running, the migration prints the statements to run instead of failing the upgrade. It is idempotent, and down() is a no-op (re-granting the pattern would widen access again).

It also keeps the escaped grant name within MariaDB's 64-character limit: default database names for very long domains are shortened so the grant always fits (on v4.13.1 such sites could not be created).

Tested end to end against MariaDB 10.11 and 12.3 on PHP 7.4 and 8.5, with PHPUnit, and live on a test server.

…limit

Escaping every `_`, `%` and `\` in the grant's database name can make it longer than 64 characters. MariaDB 10.11 then stores the name cut short, so the grant no longer covers the site's database, and MariaDB 12 rejects the GRANT. Escape as many pattern characters as fit and let `_` stand for each remaining one, which still matches it.

The quoting moves to sql_quote_grant_database() so the upgrade migration can use the same grant name.
…base

Sites on the global database created before the grant escaped `_` and `%` still hold a grant whose database name is a pattern, and a long escaped name may have been stored cut to 64 characters. A container migration adds the current grant for each such site, then revokes the old one only if it exists, so it is safe to run again. Local and remote database sites are skipped. A site whose grant can't be read or changed gets a warning with the SQL to run, and the upgrade continues. down() keeps the new grant.
A default database name is now cut so that its escaped grant name fits in 64 characters, and a suffixed default name respects the same limit, so a new site's grant covers exactly its own database. An explicit --dbname that is too long for that still works through the partial escape, with a warning at create time.
…n the grant migration

The migration now only rewrites old grant rows that are exactly the ALL PRIVILEGES grant EasyEngine creates, checked against the privilege columns the server reports. A grant that was changed by hand, a grant that another site of the same database user relies on, and a user that exists only for other hosts are reported with a warning naming the site instead. Sites whose database name is too long for an exact grant get a warning too.

The grants of all sites are read in one round trip, and a stopped or unreadable global database gives a single warning with SQL only for the sites it applies to.
reserve_global_db_names() refuses names longer than 64 bytes, so the default name is now also cut on a character boundary to fit 64 bytes, not only 64 characters. The create-time warning for a long explicit --dbname now says the grant may match other names of the same length.
…manual SQL safe

Privilege columns added after MariaDB 10.2 are set to 'N' on existing rows when mariadb-upgrade adds them, so the migration now requires 'Y' only on the 10.2 privilege columns (and 'N' on Grant_priv) when it decides whether a row is the grant EasyEngine created.

When the global database is stopped or its grants can't be read, the warning now states when the SQL applies, gives one statement per line and the command to open a root shell, and lists sites that share a database user with only the GRANT. The not-exact warning now says the grant may still match other names of the same length.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The offline fallback omits truncated legacy grants, potentially leaving unintended database access.

1 open finding
What changed in this PR

Restricts global database users to their sites’ databases and safely sizes new database names for exact grants.

Changes:

  • Adds grant-aware database-name quoting and truncation.
  • Migrates existing legacy wildcard grants to restricted grants.
  • Provides warnings and manual SQL when automatic migration is unsafe.
File Description
src/​helper/​site-utils.php Adds safe grant quoting and database-name sizing.
migrations/​container/​20260930120000_site-command_restrict_global_db_grants.php Migrates legacy global database grants.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@mrrobot47
mrrobot47 merged commit 801e2a7 into EasyEngine:develop Oct 12, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants