From c75d90d9abc61fbe449897411c7d3e5792f7a2c9 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 25 Jan 2022 22:14:28 +0100 Subject: [PATCH 1/5] workflows: lint: switch from `apt` to `apt-get -y`, add update Using apt in scripts is discouraged. Also add an update to hopefully fix the lua-check installation failure in CI. --- .github/workflows/lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ebc5817e..b18c02a5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Install Dependencies - run: sudo apt install lua-check + run: sudo apt-get -y update && sudo apt-get -y install lua-check - name: Install example site run: ln -s ./docs/site-example ./site - name: Lint Lua code @@ -22,7 +22,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Install Dependencies - run: sudo apt install shellcheck + run: sudo apt-get -y update && sudo apt-get -y install shellcheck - name: Install example site run: ln -s ./docs/site-example ./site - name: Lint shell code From 94519cfc56dab7258f98ca0e77078f15cabaf3df Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 25 Jan 2022 20:10:55 +0100 Subject: [PATCH 2/5] gluon-web-*: remove unused "token" form value This was a remnant of LuCI that was never used in gluon-web. --- .../files/lib/gluon/config-mode/view/admin/upgrade.html | 1 - .../files/lib/gluon/config-mode/view/admin/upgrade_confirm.html | 2 -- .../gluon-web-model/files/lib/gluon/web/view/model/form.html | 1 - 3 files changed, 4 deletions(-) diff --git a/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/upgrade.html b/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/upgrade.html index 31555f16..7778be4a 100644 --- a/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/upgrade.html +++ b/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/upgrade.html @@ -44,7 +44,6 @@ $Id$
-
diff --git a/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/upgrade_confirm.html b/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/upgrade_confirm.html index 92c25a63..9733132f 100644 --- a/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/upgrade_confirm.html +++ b/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/upgrade_confirm.html @@ -49,13 +49,11 @@ You may obtain a copy of the License at
" /> -
" /> -
diff --git a/package/gluon-web-model/files/lib/gluon/web/view/model/form.html b/package/gluon-web-model/files/lib/gluon/web/view/model/form.html index 06270be3..d222dde2 100644 --- a/package/gluon-web-model/files/lib/gluon/web/view/model/form.html +++ b/package/gluon-web-model/files/lib/gluon/web/view/model/form.html @@ -1,5 +1,4 @@
-
From de43b306d4423e828c34a2eac642b57fdbcd8561 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 1 Feb 2022 23:26:30 +0100 Subject: [PATCH 3/5] gluon-web: add CRLF to text/plain Internal Server Error output Having a trailing newline is nice when viewing the returned data in a terminal. --- package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua index 42dc47d1..336b2832 100644 --- a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua +++ b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua @@ -208,6 +208,6 @@ return function(config, http) if not ok then http:status(500, "Internal Server Error") http:prepare_content("text/plain") - http:write(err) + http:write(err .. "\r\n") end end From f3960eeb471e745d1dfedc87ee82c42809ce83e1 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 25 Jan 2022 20:56:00 +0100 Subject: [PATCH 4/5] gluon-web: improve error handling of parse_message_body() Actually raise an error and turn it into an HTTP 400 return code when something goes wrong, rather than ignoring the error. We also improve the conditions under which errors are thrown before pump() is called: We don't need to check for the multipart/form-data content-type twice, and a POST without this content-type is now always an error. --- .../usr/lib/lua/gluon/web/dispatcher.lua | 10 ++++++-- .../usr/lib/lua/gluon/web/http/protocol.lua | 23 +++++++------------ 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua index 336b2832..d2a6b500 100644 --- a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua +++ b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/dispatcher.lua @@ -184,9 +184,15 @@ local function dispatch(config, http, request) return end - http:parse_input(node.filehandler) + local ok, err = pcall(http.parse_input, http, node.filehandler) + if not ok then + http:status(400, "Bad request") + http:prepare_content("text/plain") + http:write(err .. "\r\n") + return + end - local ok, err = pcall(node.target) + ok, err = pcall(node.target) if not ok then http:status(500, "Internal Server Error") renderer.render_layout("error/500", { diff --git a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/http/protocol.lua b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/http/protocol.lua index 8d070d95..62b60bdc 100644 --- a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/http/protocol.lua +++ b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/http/protocol.lua @@ -108,16 +108,11 @@ end -- o String value containing a chunk of the file data -- o Boolean which indicates whether the current chunk is the last one (eof) local function mimedecode_message_body(src, msg, filecb) - - if msg and msg.env.CONTENT_TYPE then - msg.mime_boundary = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)$") + local mime_boundary = (msg.env.CONTENT_TYPE or ''):match("^multipart/form%-data; boundary=(.+)$") + if not mime_boundary then + error("Invalid Content-Type found") end - if not msg.mime_boundary then - return nil, "Invalid Content-Type found" - end - - local tlen = 0 local inhdr = false local field = nil @@ -188,10 +183,10 @@ local function mimedecode_message_body(src, msg, filecb) local spos, epos, found repeat - spos, epos = data:find("\r\n--" .. msg.mime_boundary .. "\r\n", 1, true) + spos, epos = data:find("\r\n--" .. mime_boundary .. "\r\n", 1, true) if not spos then - spos, epos = data:find("\r\n--" .. msg.mime_boundary .. "--\r\n", 1, true) + spos, epos = data:find("\r\n--" .. mime_boundary .. "--\r\n", 1, true) end @@ -250,20 +245,18 @@ local function mimedecode_message_body(src, msg, filecb) return true end - return pump(src, snk) + assert(pump(src, snk)) end -- This function will examine the Content-Type within the given message object -- to select the appropriate content decoder. -- Currently only the multipart/form-data mime type is supported. function M.parse_message_body(src, msg, filecb) - if not (msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE) then + if msg.env.REQUEST_METHOD ~= "POST" then return end - if msg.env.CONTENT_TYPE:match("^multipart/form%-data") then - return mimedecode_message_body(src, msg, filecb) - end + mimedecode_message_body(src, msg, filecb) end return M From a83466be6eaad0bfd4d92ffae3dd415a3adbbb9c Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 25 Jan 2022 21:52:02 +0100 Subject: [PATCH 5/5] gluon-web: prohibit cross-origin POST As gluon-web uses standard multipart/form-data requests, browsers don't enforce any cross-origin restrictions. To prevent malicious injection of POST requests into the config mode, match the Origin header against the Host header of the request. --- .../usr/lib/lua/gluon/web/http/protocol.lua | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/http/protocol.lua b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/http/protocol.lua index 62b60bdc..5f56fa1b 100644 --- a/package/gluon-web/luasrc/usr/lib/lua/gluon/web/http/protocol.lua +++ b/package/gluon-web/luasrc/usr/lib/lua/gluon/web/http/protocol.lua @@ -248,6 +248,47 @@ local function mimedecode_message_body(src, msg, filecb) assert(pump(src, snk)) end +local function check_post_origin(msg) + local default_port = '80' + local request_scheme = 'http' + if msg.env.HTTPS then + default_port = '443' + request_scheme = 'https' + end + + local request_host = msg.env.HTTP_HOST + if not request_host then + error('POST request without Host header') + end + if not request_host:match(':[0-9]+$') then + request_host = request_host .. ':' .. default_port + end + + local origin = msg.env.HTTP_ORIGIN + if not origin then + error('POST request without Origin header') + end + local origin_scheme, origin_host = origin:match('^([^:]*)://(.*)$') + if not origin_host then + error('POST request with invalid Origin header') + end + if not origin_host:match(':[0-9]+$') then + local origin_port + if origin_scheme == 'http' then + origin_port = '80' + elseif origin_scheme == 'https' then + origin_port = '443' + else + error('POST request with invalid Origin header') + end + origin_host = origin_host .. ':' .. origin_port + end + + if request_scheme ~= origin_scheme or request_host ~= origin_host then + error('Invalid cross-origin POST') + end +end + -- This function will examine the Content-Type within the given message object -- to select the appropriate content decoder. -- Currently only the multipart/form-data mime type is supported. @@ -256,6 +297,8 @@ function M.parse_message_body(src, msg, filecb) return end + check_post_origin(msg) + mimedecode_message_body(src, msg, filecb) end