Allowlist user-supplied field names in HasSelectableFields#107
Open
sdebacker wants to merge 1 commit into
Open
Allowlist user-supplied field names in HasSelectableFields#107sdebacker wants to merge 1 commit into
sdebacker wants to merge 1 commit into
Conversation
The selectFields scope passed request-supplied field names straight to addSelect(), letting a client dictate the column names referenced in the query (contrary to Laravel's own guidance). Restrict the non-translatable path to columns that actually exist on the table; the translatable path stays gated by the model's developer-defined $translatable list. https://claude.ai/code/session_01EUUnydptJJw8Az5AAVQ1r3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The
selectFieldsscope (used by the Pages/Tags/Terms/Menus/… API controllers) built its SELECT list from the request-suppliedfields[<table>]parameter and passed each value directly toaddSelect():Letting user input dictate column names goes against Laravel's own guidance ("you should never allow user input to dictate the column names referenced by your queries"). Eloquent wraps identifiers, so this isn't trivially injectable on MySQL, but it's a fragile, defense-relies-on-the-grammar pattern and lets clients reference columns the endpoint never intended to expose.
Fix
Restrict the non-translatable path to columns that actually exist on the table (resolved once per table via the schema builder and cached). Unknown/empty values are skipped instead of being handed to
addSelect().The translatable path is unchanged — it was already gated by the model's developer-defined
$translatablelist (not user input), and its raw SQL uses bound parameters for the locale.Behavior for legitimate requests is unchanged; only unknown/crafted field names are dropped.
Verification
Logic exercised standalone:
fields[...]id,created_at,positiontitle,status,slug(translatable)id, created_at , titleid,(SELECT api_token FROM users)idselected; the rest droppedpassword,secret_column(non-existent)php -lpasses (PHP 8.4). The package has novendor/in this environment (the suite runs against a host app), so the full test suite wasn't run here.https://claude.ai/code/session_01EUUnydptJJw8Az5AAVQ1r3
Generated by Claude Code