-
Notifications
You must be signed in to change notification settings - Fork 520
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
Return status 400 on invalid file names #343
base: master
Are you sure you want to change the base?
Conversation
This PR would close #342. This is the simplest possible thing that would do what I need. If you want something more elaborate, please let me know! |
@weavejester Is this a satisfactory solution, or would you like something else? |
Sorry for the late response. There are a few issues:
|
Thanks for taking a look! 1 and 2 addressed. I tried to find examples of other error handling for the async model in the code base, but couldn't find any. I opted to As to 3) I'm not sure what you mean. I went to some lengths to avoid making stylistic changes to the indentation. edit: I didn't squash these (yet) to make review slightly easier. |
(try | ||
(handler (multipart-params-request request options) respond raise) | ||
(catch InvalidFileNameException e | ||
(handler request |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should mirror the functionality of the 1-arity form, so:
(catch InvalidFileNameException e
(respond (response/bad-request (.getMessage e))))
(try | ||
(handler (multipart-params-request request options)) | ||
(catch InvalidFileNameException e | ||
(response/bad-request (.getMessage e))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably make this customizeable, so perhaps something like:
(let [invalid-filename-handler
(:invalid-filename-handler options default-invalid-filename-handler)]
(fn
([request]
...)
@@ -0,0 +1,27 @@ | |||
(ns ring.middleware.multipart-params.test.multipart-params |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why have you created a new test file? Just add your tests to the existing file at: ring-core/test/ring/middleware/test/multipart_params.clj
(deftest wrap-multipart-params-error-test | ||
(let [invalid-file-name "foo\\0.bar" | ||
error-msg (str "Invalid file name: " invalid-file-name)] | ||
(with-redefs [multipart-params-request |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't use with-redefs
here. It would be better to pass in a request with an invalid filename.
error-msg)))] | ||
(testing "Synchronous error response" | ||
(let [handler (wrap-multipart-params (constantly (response/response "Ok")))] | ||
(is (= (response/bad-request error-msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer using the full request map in tests, over helper functions like bad-request
.
That's correct, because it should mirror the 1-arity version.
The incorrect indentation was here and here, but that's been fixed in the latest commit. |
Thanks for your patience and guidance on this @weavejester! Should be ready for another review now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good. Just a few minor changes.
:progress-fn - a function that gets called during uploads. The | ||
function should expect four parameters: request, | ||
bytes-read, content-length, and item-count." | ||
:encoding - character encoding to use for multipart parsing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should change the formatting so that it's more like:
:encoding
- Character encoding...
With the addition of :invalid-filename-handler
, there's just too much whitespace, and the lines go over 80 characters as well.
(let [invalid-file-name-handler | ||
(:invalid-filename-handler options default-invalid-filename-handler)] | ||
(try | ||
(handler (multipart-params-request request options) respond raise) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The try/catch
should only wrap the multipart-params-request
call, since we don't want exceptions thrown by the handler to be caught within this block.
Done @weavejester. I also I pushed the error handling down one level, to avoid code duplication. Just let me know if the use of meta, to indicate that the response resulted in an error, is making you see red and I can change it to e.g. using return value with |
We shouldn't be passing metadata instead of catching an exception. Instead, just have it as you had previously, but narrow the scope of the try/catch. Something like (defn wrap-multipart-params [handler options]
(let [invalid-file-name-handler
(:invalid-filename-handler options default-invalid-filename-handler)]
(fn
([request]
(let [req-or-ex (try (multipart-params-request request options)
(catch InvalidFileNameException ex ex)]
(if (instance? Throwable req-or-ex)
(invalid-filename-handler request)
(handler req-or-ex))))
([request respond raise]
(let [req-or-ex (try (multipart-params-request request options)
(catch InvalidFileNameException ex ex)]
(if (instance? Throwable req-or-ex)
(invalid-filename-handler request respond raise)
(handler req-or-ex respond raise)))))) There's potential to simplify this, but I'm not sure whether we should retain the exception or not. If we don't need to, we can just have the catch swallow the exception and return |
557f2bb
to
65c03a0
Compare
@weavejester That does indeed look better. Updated! |
(let [req-or-ex (try | ||
(multipart-params-request request options) | ||
(catch Exception ex ex)) | ||
invalid-filename-handler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This let
binding can be moved out of the inner function so we don't need to perform a key lookup each request.
b23a0a5
to
3f988b4
Compare
Done! |
(let [req-or-ex (try | ||
(multipart-params-request request options) | ||
(catch Exception ex ex))] | ||
(if (instance? Throwable req-or-ex) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Throwable
needs to be InvalidFileNameException
.
(multipart-params-request request options) | ||
(catch Exception ex ex))] | ||
(if (instance? Throwable req-or-ex) | ||
(respond (invalid-filename-handler request req-or-ex)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to support the asynchronous 3-arity form.
(fn ([request] | ||
(let [req-or-ex (try | ||
(multipart-params-request request options) | ||
(catch Exception ex ex))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exception
needs to be InvalidFileNameException
.
03ac5c2
to
6386ed3
Compare
Latest feedback addressed 🙂 |
|
||
(defn- add-invalid-filename-to-req | ||
[request invalid-filename-exception] | ||
(assoc request ::invalid-filename (.getName invalid-filename-exception) )) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra whitespace that needs to be removed.
Done |
Ping @weavejester. Is this in a merge-worthy state now? Do you want me to squash or are you using the github workflow for that? |
Oh, I forgot to commit my last review. Hang on, let me do that now. |
@@ -135,41 +137,71 @@ | |||
{:multipart-params params} | |||
{:params params})))) | |||
|
|||
(defn default-invalid-filename-handler | |||
([request] | |||
(-> request ::invalid-filename response/bad-request)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a simple explanatory message to the default response, so people know what caused the error? For example:
(response/bad-request
(str "Invalid filename in multipart upload: "
(::invalid-filename request)))
Done, thanks! 👍 |
Thanks! That looks good. Can you squash the commits and ensure the commit message adheres to the contributing guidelines? |
The multipart middleware would throw an exception when it encountered an invalid filename. For most apps that exception would be caught by the default exception handler and result in a status code of 500 instead of 400. This commit catches this specific exception and returns a 400 response from the server instead. The error response is made customizable through the optional :invalid-filename-handler.
6d235b8
to
7bf0e7a
Compare
Done :) |
ping @weavejester, is this in a mergeworthy state? |
Thanks for being patient. I've been thinking about this, as I'm not completely happy with the solution we've ended up with. Other middleware, such as Longer term, I'd like to introduce optional exceptions to all those functions. I've been thinking about introducing some middleware for Ring 1.8 that would catch exceptions and produce some manner of response. With In conjunction with that middleware, an extra However, in the meantime we can adopt the simpler solution of just passing over invalid data. My thought is that we can omit the What are your thoughts? |
Most projects probably have an exceptions middleware, so if your intention is for the new ring exception middleware to be extensible to the point where it can replace all the home-grown solutions, then I think this is a good idea. All exceptions will then be handled in a uniform manner. If the new middleware isn't going to be extensible, then I think I'd prefer the current implementation for reasons of code locality and because it has fewer moving parts. I'm not sure continuing without I don't think anyone really cares about the exact response given to the Hackerone script kiddie, that handcrafted a request with a malformed filename, beyond avoiding a stacktrace in their logs. We could trim this down, to avoid committing to an approach that is likely to be temporary, and just do a |
I'd prefer not to have error handlers on every piece of middleware. Both synchronous and asynchronous middleware can raise exceptions, and exceptions are designed to shortcircuit logic. My thought is that a global layer to handle turning exceptions into responses would be a better solution than a piecemeal set of options.
I'm not sure how often In which case let's just omit multipart parameters with invalid filenames. This is consistent with how other middleware handle it. In future an option can be introduced that explicitly raises exceptions on invalid data. |
0d7f29a
to
0103527
Compare
The multipart middleware would throw an exception when it encountered an
invalid filename.
For most apps that exception would be caught by the default exception
handler and result in a status code of 500 instead of 400.
This commit catches this specific exception and returns a 400 response
from the server instead.