invidious icon indicating copy to clipboard operation
invidious copied to clipboard

Replace `Kemal::StaticFileHandler` with direct subclass of stdlib `HTTP::StaticFileHandler` on Crystal >= 1.17.0

Open syeopite opened this issue 6 months ago • 9 comments

Fixes the CI for Crystal nightly


Kemal's subclass of the stdlib HTTP::StaticFileHandler is not as maintained as its parent, and so misses out on many enhancements and bug fixes from upstream, which unfortunately also includes the patches for security vulnerabilities...

Though this isn't necessarily Kemal's fault since the bulk of the stdlib handler's logic was done in a single big method, making any changes hard to maintain. This was fixed in Crystal 1.17.0 where the handler was refactored into many private methods, making it easier for an inheriting type to implement custom behaviors while still leveraging much of the pre-existing code.

Since we don't actually use any of the Kemal specific features added by Kemal::StaticFileHandler, there really isn't a reason to not just create a new handler based upon the stdlib implementation instead which will address the problems mentioned above.

This PR implements a new handler which inherits from the stdlib version and overrides the helper methods added in Crystal 1.17.0 to add the caching behavior with minimal code changes. Since this new handler depends on the code in Crystal 1.17.0, it will only be applied on versions greater than or equal to 1.17.0. On older versions we'll fallback to the current monkey patched Kemal::StaticFileHandler

syeopite avatar Jun 04 '25 01:06 syeopite

Hi! I'm wondering if there's anything blocking these commits? I'm hoping to be able to use the new crystallang version hopefully soon.

NovaAndrom3da avatar Aug 20 '25 17:08 NovaAndrom3da

Hi! I'm wondering if there's anything blocking these commits? I'm hoping to be able to use the new crystallang version hopefully soon.

The PR is still going through the code review process but after that it can be merged

syeopite avatar Aug 24 '25 05:08 syeopite

This is now needed to build the latest invidious commits with Crystal 1.17.1 🫤 :

In src/ext/kemal_static_file_handler.cr:167:49

 167 | directory_listing(context.response, request_path, file_path)
                                           ^-----------
Error: expected argument #2 to 'Kemal::StaticFileHandler#directory_listing' to be Path, not String

Overloads are:
 - HTTP::StaticFileHandler#directory_listing(io : IO, request_path : Path, path : Path)

Fijxu avatar Sep 08 '25 21:09 Fijxu

I just tested this PR and I can't get Invidious interface to load completely, css files are empty and js files do not load:

image

Fijxu avatar Sep 09 '25 15:09 Fijxu

Oops looks like #5337 is required for compression to work correctly... I just rebased against master which includes that commit and its fixed now.

A test case can't be added due to needing to import handlers.cr which depends on many other Invidious objects and constants... I guess we should separate the handlers there to make designing tests easier

syeopite avatar Sep 11 '25 03:09 syeopite

I tested it and works fine now ;). Thanks

Fijxu avatar Sep 11 '25 23:09 Fijxu

in contrast to @Fijxu I'm not seeing any of the static content load in the browser (neither Firefox-143.0 nor Google-Chrome-140.0.7339.207 on linux). Even the favicon fails to load. This seems to be related to content-type mismatch coupled with "x-content-type-options: nosniff"

here are some of the headers for the request for the favicon GET

scheme: https
host: invidious.localdomain
filename:  /favicon-16x16.png

snippet of Request Headers Accept: image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5

snippet of Response Headers content-type: text/html; charset=UTF-8

Console log error

The resource from “https://invidious.localdomain/feed/popular” was blocked due to MIME type (“text/html”) mismatch (X-Content-Type-Options: nosniff).

This same mismatch occurs with css files and js (i.e. they are sent with "content-type: text/html; charset=UTF-8")

EDIT: This is Gentoo amd64 with crystal-1.17.1 kemal-1.7.2¹ exception_page-0.5.0 invidious-v2.20250913.0 ¹ kemal-1.6.0 is not in gentoo's guru repo (community maintained) due to the path transversal vulnerability that was fixed in kemal-1.7.0

dekeonus avatar Sep 28 '25 16:09 dekeonus

After further examination, the issue I'm having is that the redirect to the static assets is not correct. I restarted (after reconfiguring) invidious to serve directly (rather than by reverse proxy), to rule out some interaction there. Note the location header is "/" in the response headers returned below.

GET

scheme: http
host: 10.144.1.5:3000
filename: /css/empty.css

v: b32b077
Address: 10.144.1.5:3000

Status: 302 Found
Version: HTTP/1.1
Transferred: 0 GB (0 GB size)
Referrer Policy: same-origin
DNS Resolution: System

Response Headers

HTTP/1.1 302 Found
Connection: keep-alive
Content-Type: text/html
Date: Sun, 28 Sep 2025 19:50:24 GMT
X-Frame-Options: sameorigin
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; manifest-src 'self'; media-src 'self' blob:; child-src 'self' blob:; frame-src 'self'; frame-ancestors 'none'
Referrer-Policy: same-origin
Permissions-Policy: interest-cohort=()
Location: /
Content-Length: 0

Request Headers

GET /css/empty.css?v=b32b077 HTTP/1.1
Host: 10.144.1.5:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:143.0) Gecko/20100101 Firefox/143.0
Accept: text/css,*/*;q=0.1
Accept-Language: en-AU,en-GB;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://10.144.1.5:3000/
DNT: 1
Sec-GPC: 1
Connection: keep-alive

dekeonus avatar Sep 28 '25 20:09 dekeonus

Sorry for the noise, my issue was insufficient sed in the gentoo build script. However, that said, it does raise the issue of less than ideal operation.

In gentoo's case the build script modifies several files to put assets/, locales/, and config/{sql,migrate-scripts}/ under /usr/share/invidious. When invidious fails to find one of the asset files, it continues running without it (that's fine) BUT without logging the fact that it failed to load the asset file, nor sending to the client a 404 response due to a non-existent resource.

dekeonus avatar Sep 28 '25 21:09 dekeonus