Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect InvalidQuery Errors Caused by String.to_existing_atom/1 #317

Open
kyleboe opened this issue Apr 30, 2024 · 1 comment
Open

Incorrect InvalidQuery Errors Caused by String.to_existing_atom/1 #317

kyleboe opened this issue Apr 30, 2024 · 1 comment
Labels

Comments

@kyleboe
Copy link

kyleboe commented Apr 30, 2024

This is kind of a wild edge case, but when a nested, valid include is defined, the QueryParser throws an error when that relationship is being included because the atom representation of the nested relationship has not yet been loaded in BEAM.

Example

defmodule PlaylistsController do
  plug JSONAPI.QueryParser,
    include: ~w(songs songs.artists),
    view: PlaylistView
  # . . .
end
defmodule PlaylistView do
  def relationships do
    [songs: SongView]
  end
end

defmodule SongView do
  def relationships do
    [artists: ArtistView]
  end
end

When calling to the controller with the include=songs.artists query param, a 500 error will be returned because of runtime loading of modules/atoms. Specifically this line in handle_nested_include/3:

|> Enum.map(&String.to_existing_atom/1)

Proposed Solution

While I understand the reasoning for the current implementation (we don't want to cast external params to atoms and allow for DOS memory attacks), I think we can approach the problem differently.

Instead of converting the external params to match the internal setting. Could we convert the internal setting to match the incoming params. So instead of using a word list to define valid includes, could we use atoms to begin with and then cast them to strings and compare those strings with the ones from the internet? That way the atoms present in BEAM as soon as the module is loaded.

This may look something like this:

defmodule PlaylistsController do
  plug JSONAPI.QueryParser,
    include: [:songs, songs: :artists],
    view: PlaylistView
  # . . .
end
@mattpolzin
Copy link
Member

So instead of using a word list to define valid includes, could we use atoms to begin with and then cast them to strings and compare those strings with the ones from the internet?

Something along those lines is probably preferable to the existing implementation in more than one way. We need to decide if it is good or bad (or unimportant) that the existing implementation takes nested include options literally (songs.artists means "including artists on songs is allowed" not "including songs is allowed and including artists on songs is allowed"). The current behavior was a side effect of the quickest implementation possible, rather than a carefully considered decision.

I'd like to start planning for a v2 release of the library so we can introduce some breaking changes and this could be one of them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants