From e46ed1ff975aec5b6f2bc7b17abe9d3b5d243028 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 15 Aug 2019 19:06:38 +0300 Subject: [PATCH 1/7] API/getHeadlines: fix order of returned feeds to be consistent with main UI --- classes/api.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/classes/api.php b/classes/api.php index 44c9841ce..01ea1970d 100755 --- a/classes/api.php +++ b/classes/api.php @@ -535,6 +535,7 @@ class API extends Handler { /* Labels */ + /* API only: -4 All feeds, including virtual feeds */ if ($cat_id == -4 || $cat_id == -2) { $counters = Counters::getLabelCounters(true); @@ -582,7 +583,7 @@ class API extends Handler { if ($include_nested && $cat_id) { $sth = $pdo->prepare("SELECT id, title, order_id FROM ttrss_feed_categories - WHERE parent_cat = ? AND owner_uid = ? ORDER BY id, title"); + WHERE parent_cat = ? AND owner_uid = ? ORDER BY order_id, title"); $sth->execute([$cat_id, $_SESSION['uid']]); @@ -611,12 +612,13 @@ class API extends Handler { $limit_qpart = ""; } + /* API only: -3 All feeds, excluding virtual feeds (e.g. Labels and such) */ if ($cat_id == -4 || $cat_id == -3) { $sth = $pdo->prepare("SELECT id, feed_url, cat_id, title, order_id, ". SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated FROM ttrss_feeds WHERE owner_uid = ? - ORDER BY cat_id, title " . $limit_qpart); + ORDER BY order_id, title " . $limit_qpart); $sth->execute([$_SESSION['uid']]); } else { @@ -627,7 +629,7 @@ class API extends Handler { FROM ttrss_feeds WHERE (cat_id = :cat OR (:cat = 0 AND cat_id IS NULL)) AND owner_uid = :uid - ORDER BY cat_id, title " . $limit_qpart); + ORDER BY order_id, title " . $limit_qpart); $sth->execute([":uid" => $_SESSION['uid'], ":cat" => $cat_id]); } From 10c63ed58240c3e652b931be9a880026c465bb50 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 15 Aug 2019 20:23:45 +0300 Subject: [PATCH 2/7] pluginhost: add helper methods to get private/public pluginmethod endpoint URLs --- classes/pluginhost.php | 30 ++++++++++++++++++++++++++++++ plugins/af_proxy_http/init.php | 3 +-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/classes/pluginhost.php b/classes/pluginhost.php index 4d5b3252c..4cc85f044 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -491,4 +491,34 @@ class PluginHost { function get_owner_uid() { return $this->owner_uid; } + + // handled by classes/pluginhandler.php, requires valid session + function get_method_url($sender, $method, $params) { + return get_self_url_prefix() . "/backend.php?" . + http_build_query( + array_merge( + [ + "op" => "pluginhandler", + "plugin" => strtolower(get_class($sender)), + "pmethod" => $method + ], + $params)); + } + + // WARNING: endpoint in public.php, exposed to unauthenticated users + function get_public_method_url($sender, $method, $params) { + if ($sender->is_public_method($method)) { + return get_self_url_prefix() . "/public.php?" . + http_build_query( + array_merge( + [ + "op" => "pluginhandler", + "plugin" => strtolower(get_class($sender)), + "pmethod" => $method + ], + $params)); + } else { + user_error("get_public_method_url: requested method '$method' of '" . get_class($sender) . "' is private."); + } + } } diff --git a/plugins/af_proxy_http/init.php b/plugins/af_proxy_http/init.php index 421e04d1f..80100160d 100644 --- a/plugins/af_proxy_http/init.php +++ b/plugins/af_proxy_http/init.php @@ -141,8 +141,7 @@ class Af_Proxy_Http extends Plugin { } } - return get_self_url_prefix() . "/public.php?op=pluginhandler&plugin=af_proxy_http&pmethod=imgproxy&url=" . - urlencode($url); + return $this->host->get_public_method_url($this, "imgproxy", ["url" => $url]); } } } From 865c54abcb5bdfb6d68757cfadccd8383cdc213c Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 15 Aug 2019 20:27:21 +0300 Subject: [PATCH 3/7] fix get_method_url() to use correct method parameter --- classes/pluginhost.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/pluginhost.php b/classes/pluginhost.php index 4cc85f044..9330e9e5e 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -500,7 +500,7 @@ class PluginHost { [ "op" => "pluginhandler", "plugin" => strtolower(get_class($sender)), - "pmethod" => $method + "method" => $method ], $params)); } From e53cd12ffd7237af3ffa87431d849db44b892d63 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 16 Aug 2019 09:27:05 +0300 Subject: [PATCH 4/7] pluginhandler: better error reporting for incorrect usage --- classes/handler/public.php | 20 ++++++++++---------- classes/pluginhandler.php | 5 ++++- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/classes/handler/public.php b/classes/handler/public.php index 05a84494b..92240fd10 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -1203,30 +1203,30 @@ class Handler_Public extends Handler { public function pluginhandler() { $host = new PluginHost(); - $plugin = basename(clean($_REQUEST["plugin"])); + $plugin_name = clean_filename($_REQUEST["plugin"]); $method = clean($_REQUEST["pmethod"]); - $host->load($plugin, PluginHost::KIND_USER, 0); + $host->load($plugin_name, PluginHost::KIND_USER, 0); $host->load_data(); - $pclass = $host->get_plugin($plugin); + $plugin = $host->get_plugin($plugin_name); - if ($pclass) { - if (method_exists($pclass, $method)) { - if ($pclass->is_public_method($method)) { - $pclass->$method(); + if ($plugin) { + if (method_exists($plugin, $method)) { + if ($plugin->is_public_method($method)) { + $plugin->$method(); } else { - user_error("pluginhandler: Requested private method '$method' of plugin '$plugin'."); + user_error("PluginHandler[PUBLIC]: Requested private method '$method' of plugin '$plugin_name'."); header("Content-Type: text/json"); print error_json(6); } } else { - user_error("pluginhandler: Requested unknown method '$method' of plugin '$plugin'."); + user_error("PluginHandler[PUBLIC]: Requested unknown method '$method' of plugin '$plugin_name'."); header("Content-Type: text/json"); print error_json(13); } } else { - user_error("pluginhandler: Requested method '$method' of unknown plugin '$plugin'."); + user_error("PluginHandler[PUBLIC]: Requested method '$method' of unknown plugin '$plugin_name'."); header("Content-Type: text/json"); print error_json(14); } diff --git a/classes/pluginhandler.php b/classes/pluginhandler.php index d10343e09..9682e440f 100644 --- a/classes/pluginhandler.php +++ b/classes/pluginhandler.php @@ -5,15 +5,18 @@ class PluginHandler extends Handler_Protected { } function catchall($method) { - $plugin = PluginHost::getInstance()->get_plugin(clean($_REQUEST["plugin"])); + $plugin_name = clean($_REQUEST["plugin"]); + $plugin = PluginHost::getInstance()->get_plugin($plugin_name); if ($plugin) { if (method_exists($plugin, $method)) { $plugin->$method(); } else { + user_error("PluginHandler: Requested unknown method '$method' of plugin '$plugin_name'.", E_USER_WARNING); print error_json(13); } } else { + user_error("PluginHandler: Requested method '$method' of unknown plugin '$plugin_name'.", E_USER_WARNING); print error_json(14); } } From 9c366a4811dd0b919171f3de04c4cd781c5e8e20 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 16 Aug 2019 09:27:14 +0300 Subject: [PATCH 5/7] clean_filename: also remove markup --- include/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/functions.php b/include/functions.php index c326ac468..e78f0de9d 100644 --- a/include/functions.php +++ b/include/functions.php @@ -593,7 +593,7 @@ } function clean_filename($filename) { - return basename(preg_replace("/\.\.|[\/\\\]/", "", $filename)); + return basename(preg_replace("/\.\.|[\/\\\]/", "", clean($filename))); } function make_password($length = 12) { From d94348421d8d791eec29afd2e01fb1c9ba181de1 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 16 Aug 2019 09:31:16 +0300 Subject: [PATCH 6/7] use clean_filename() instead of basename()/clean() combinations in a bunch of places --- classes/backend.php | 2 +- classes/pluginhost.php | 2 +- classes/rpc.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/backend.php b/classes/backend.php index 5bd724728..122e28c65 100644 --- a/classes/backend.php +++ b/classes/backend.php @@ -88,7 +88,7 @@ class Backend extends Handler { } function help() { - $topic = basename(clean($_REQUEST["topic"])); // only one for now + $topic = clean_filename($_REQUEST["topic"]); // only one for now if ($topic == "main") { $info = get_hotkeys_info(); diff --git a/classes/pluginhost.php b/classes/pluginhost.php index 9330e9e5e..eab808ae9 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -186,7 +186,7 @@ class PluginHost { foreach ($plugins as $class) { $class = trim($class); - $class_file = strtolower(basename($class)); + $class_file = strtolower(clean_filename($class)); if (!is_dir(__DIR__."/../plugins/$class_file") && !is_dir(__DIR__."/../plugins.local/$class_file")) continue; diff --git a/classes/rpc.php b/classes/rpc.php index 8736cbb65..84c9cfe92 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -572,7 +572,7 @@ class RPC extends Handler_Protected { function log() { $msg = clean($_REQUEST['msg']); - $file = basename(clean($_REQUEST['file'])); + $file = clean_filename($_REQUEST['file']); $line = (int) clean($_REQUEST['line']); $context = clean($_REQUEST['context']); From a2d26867e653d34a80e504478ebe30ff69ef4a33 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 16 Aug 2019 09:38:24 +0300 Subject: [PATCH 7/7] pluginhandler/public: report errors with E_USER_WARNING --- classes/handler/public.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/handler/public.php b/classes/handler/public.php index 92240fd10..91413b976 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -1216,17 +1216,17 @@ class Handler_Public extends Handler { if ($plugin->is_public_method($method)) { $plugin->$method(); } else { - user_error("PluginHandler[PUBLIC]: Requested private method '$method' of plugin '$plugin_name'."); + user_error("PluginHandler[PUBLIC]: Requested private method '$method' of plugin '$plugin_name'.", E_USER_WARNING); header("Content-Type: text/json"); print error_json(6); } } else { - user_error("PluginHandler[PUBLIC]: Requested unknown method '$method' of plugin '$plugin_name'."); + user_error("PluginHandler[PUBLIC]: Requested unknown method '$method' of plugin '$plugin_name'.", E_USER_WARNING); header("Content-Type: text/json"); print error_json(13); } } else { - user_error("PluginHandler[PUBLIC]: Requested method '$method' of unknown plugin '$plugin_name'."); + user_error("PluginHandler[PUBLIC]: Requested method '$method' of unknown plugin '$plugin_name'.", E_USER_WARNING); header("Content-Type: text/json"); print error_json(14); }