Skip to content

isValidSqlIdentifier не отсеивает зарезервированные слова MySQL 8 #655

Description

@biz87

Проблема

GridColumnRules::isValidSqlIdentifier() проверяет только форму идентификатора и пропускает зарезервированные слова MySQL 8 — order, rank, groups, function, system и прочие.

core/components/minishop3/src/Services/Grid/GridColumnRules.php:26-29:

public static function isValidSqlIdentifier(string $name): bool
{
    return (bool) preg_match(self::SQL_IDENTIFIER_PATTERN, $name);
}

Паттерн — /^[a-z0-9_]+$/i, то есть order его проходит.

При этом в правилах проекта (CLAUDE.md, раздел «Key Conventions → PHP») использование зарезервированных слов MySQL 8 в названиях колонок прямо запрещено, с рекомендацией альтернатив: sort_order вместо order, user_groups вместо groups, sort_index вместо rank.

Где это применяется

Валидатор используется в семи местах и покрывает не только extra fields:

  • ExtraFieldsService.php:239 — валидация key при создании пользовательского поля
  • GridColumnTypeValidator.php:86,93foreignKey и displayField в relation-колонках
  • GridRelationFieldExtractor.php:72-73
  • RelationColumnSpec.php:86
  • GridColumnRules.php:36 — внутренний вызов

То есть имя, пришедшее из пользовательского ввода, проходит эту проверку и дальше попадает в генерацию миграций и в построение SQL.

Насколько это опасно сегодня

Не критично — Phinx оборачивает идентификаторы в бэктики при DDL, поэтому ADD COLUMN \order`` сам по себе не упадёт. Но:

  • любое место, где имя колонки попадает в сырой SQL без экранирования, сломается синтаксической ошибкой, причём в рантайме и не сразу;
  • сортировка и фильтрация по такой колонке в грид-запросах — типичный кандидат на такую поломку;
  • пользователь при этом не получит внятной ошибки на этапе создания поля, когда её ещё дёшево исправить.

Правило в CLAUDE.md появилось не на пустом месте — мы уже наступали на это (см. комментарий про rank в src/Processors/Product/GetList.php).

Предлагаемое решение

Добавить в GridColumnRules список зарезервированных слов MySQL 8 и проверять по нему в isValidSqlIdentifier() (либо отдельным методом, если для части вызовов проверка избыточна — тогда явно решить, для каких).

Готового списка в кодовой базе нет — сейчас есть только точечный комментарий про rank. Полный перечень зарезервированных слов есть в документации MySQL 8; достаточно захардкодить его константой.

Отдельно стоит решить, что делать с уже созданными полями: валидация на входе новые случаи закроет, но существующие записи не тронет.

Критерии приёмки

  • isValidSqlIdentifier('order'), ('rank'), ('groups') возвращают false
  • Сообщение об ошибке при создании extra field объясняет причину и предлагает альтернативу
  • Существующие валидные имена (sort_order, preview_url, category_name) продолжают проходить
  • Есть юнит-тест на набор зарезервированных слов
  • Решено и зафиксировано, что делать с полями, созданными до фикса

Контекст

Найдено при ревью PR #646 (issue #645). Сам PR корректен — он добавил валидацию формы идентификатора там, где её вообще не было; проверка на зарезервированные слова просто выходила за его рамки.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpriority: lowНизкий приоритет, когда будет время

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions