Software / code / prosody
File
util/prosodyctl/shell.lua @ 13854:0b01f40df0f9 13.0
mod_http_file_share: Add media-src 'self' to Content-Security-Policy header
This allows certain media files to be loaded when navigated to directly in a
web browser.
Note that in some browsers (Chrome), the media gets transformed
internally into a HTML page with some basic styles, but these are blocked due
to our default-src policy of 'none' Although this could be unblocked with
style-src unsafe-inline, it is not our plan to fix this, because this would
have negative security implications.
The reason for our CSP is to prevent the file share service from being used to
host malicious HTML/CSS/JS. Yes, CSS can be malicious.
Our file share service is for uploading and downloading files, it is not a
substitute for website/content hosting.
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Fri, 18 Apr 2025 12:25:06 +0100 |
| parent | 13796:c8e534b4f2e2 |
line wrap: on
line source
local config = require "prosody.core.configmanager"; local human_io = require "prosody.util.human.io"; local server = require "prosody.net.server"; local st = require "prosody.util.stanza"; local path = require "prosody.util.paths"; local parse_args = require "prosody.util.argparse".parse; local tc = require "prosody.util.termcolours"; local isatty = require "prosody.util.pposix".isatty; local term_width = require"prosody.util.human.io".term_width; local have_readline, readline = pcall(require, "readline"); local adminstream = require "prosody.util.adminstream"; if have_readline then readline.set_readline_name("prosody"); readline.set_options({ histfile = path.join(prosody.paths.data, ".shell_history"); ignoredups = true; }); end local function read_line(prompt_string) if have_readline then return readline.readline(prompt_string); else io.write(prompt_string); return io.read("*line"); end end local function send_line(client, line, interactive) client.send(st.stanza("repl-input", { width = tostring(term_width()), repl = interactive == false and "0" or "1" }):text(line)); end local function repl(client) local line = read_line(client.prompt_string or "prosody> "); if not line or line == "quit" or line == "exit" or line == "bye" then if not line then print(""); end if have_readline then readline.save_history(); end os.exit(0, true); end send_line(client, line); end local function printbanner() local banner = config.get("*", "console_banner"); if banner then return print(banner); end print([[ ____ \ / _ | _ \ _ __ ___ ___ _-_ __| |_ _ | |_) | '__/ _ \/ __|/ _ \ / _` | | | | | __/| | | (_) \__ \ |_| | (_| | |_| | |_| |_| \___/|___/\___/ \__,_|\__, | A study in simplicity |___/ ]]); print("Welcome to the Prosody administration console. For a list of commands, type: help"); print("You may find more help on using this console in our online documentation at "); print("https://prosody.im/doc/console\n"); end local function check() local lfs = require "lfs"; local socket_path = path.resolve_relative_path(prosody.paths.data, config.get("*", "admin_socket") or "prosody.sock"); local state = lfs.attributes(socket_path, "mode"); return state == "socket"; end local function start(arg) --luacheck: ignore 212/arg local client = adminstream.client(); local opts, err, where = parse_args(arg); local ttyout = isatty(io.stdout); if not opts then if err == "param-not-found" then print("Unknown command-line option: "..tostring(where)); elseif err == "missing-value" then print("Expected a value to follow command-line option: "..where); end os.exit(1); end if arg[1] then if arg[2] then arg[1] = ("{"..string.rep("%q", #arg, ", ").."}"):format(table.unpack(arg, 1, #arg)); end client.events.add_handler("connected", function() send_line(client, arg[1], false); return true; end, 1); local errors = 0; -- TODO This is weird, but works for now. client.events.add_handler("received", function(stanza) if stanza.name == "repl-output" or stanza.name == "repl-result" then local dest = io.stdout; if stanza.attr.type == "error" then errors = errors + 1; dest = io.stderr; end if stanza.attr.eol == "0" then dest:write(stanza:get_text()); else dest:write(stanza:get_text(), "\n"); end end if stanza.name == "repl-result" then os.exit(errors); end return true; end, 1); end client.events.add_handler("connected", function () if not opts.quiet then printbanner(); end repl(client); end); client.events.add_handler("disconnected", function () print("--- session closed ---"); os.exit(0, true); end); client.events.add_handler("received", function (stanza) if stanza.name ~= "repl-request-input" then return; end if stanza.attr.type == "password" then local password = human_io.read_password(); client.send(st.stanza("repl-requested-input", { type = stanza.attr.type; id = stanza.attr.id; status = password and "submit" or "cancel"; }):text(password or "")); else io.stderr:write("Internal error - unexpected input request type "..tostring(stanza.attr.type).."\n"); os.exit(1); end return true; end, 2); client.events.add_handler("received", function (stanza) if stanza.name == "repl-output" or stanza.name == "repl-result" then local result_prefix = stanza.attr.type == "error" and "!" or "|"; local out = result_prefix.." "..stanza:get_text(); if ttyout and stanza.attr.type == "error" then out = tc.getstring(tc.getstyle("red"), out); end print(out); end if stanza.name == "repl-result" then repl(client); end end); client.prompt_string = config.get("*", "admin_shell_prompt"); local socket_path = path.resolve_relative_path(prosody.paths.data, opts.socket or config.get("*", "admin_socket") or "prosody.sock"); local conn = adminstream.connection(socket_path, client.listeners); local ok, err = conn:connect(); if not ok then if err == "no unix socket support" then print("** LuaSocket unix socket support not available or incompatible, ensure your"); print("** version is up to date."); else print("** Unable to connect to server - is it running? Is mod_admin_shell enabled?"); print("** Connection error: "..err); end os.exit(1); end server.loop(); end return { shell = start; available = check; };