From 3cef6e4824444d0e3418d3ac2ef47ee61a720b49 Mon Sep 17 00:00:00 2001 From: mrilyew <99399973+mrilyew@users.noreply.github.com> Date: Sun, 8 Jun 2025 11:08:53 +0300 Subject: [PATCH] add list of allowed hosts in notes images Co-Authored-By: n1rwana <93197434+n1rwana@users.noreply.github.com> --- Web/Models/Entities/Note.php | 51 +++++++++++++++++++----- Web/Presenters/InternalAPIPresenter.php | 22 ++++++++++ Web/routes.yml | 2 + Web/static/img/fn_placeholder.jpg | Bin 0 -> 15203 bytes openvk-example.yml | 3 ++ 5 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 Web/static/img/fn_placeholder.jpg diff --git a/Web/Models/Entities/Note.php b/Web/Models/Entities/Note.php index a44c421b..b9829822 100644 --- a/Web/Models/Entities/Note.php +++ b/Web/Models/Entities/Note.php @@ -6,12 +6,42 @@ namespace openvk\Web\Models\Entities; use HTMLPurifier_Config; use HTMLPurifier; +use HTMLPurifier_Filter; + +class SecurityFilter extends HTMLPurifier_Filter +{ + public function preFilter($html, $config, $context) + { + $html = preg_replace_callback( + '/]*src\s*=\s*["\']([^"\']*)["\'][^>]*>/i', + function ($matches) { + $originalSrc = $matches[1]; + $src = $originalSrc; + + if (OPENVK_ROOT_CONF["openvk"]["preferences"]["notes"]["disableHotlinking"] ?? true) { + if (!str_contains($src, "/image.php?url=")) { + $src = '/image.php?url=' . base64_encode($originalSrc); + } /*else { + $src = preg_replace_callback('/(.*)\/image\.php\?url=(.*)/i', function ($matches) { + return base64_decode($matches[2]); + }, $src); + }*/ + } + + return str_replace($originalSrc, $src, $matches[0]); + }, + $html + ); + + return $html; + } +} class Note extends Postable { protected $tableName = "notes"; - protected function renderHTML(): string + protected function renderHTML(?string $content = null): string { $config = HTMLPurifier_Config::createDefault(); $config->set("Attr.AllowedClasses", []); @@ -78,16 +108,19 @@ class Note extends Postable $config->set("Attr.AllowedClasses", [ "underline", ]); + $config->set('Filter.Custom', [new SecurityFilter()]); - $source = null; - if (is_null($this->getRecord())) { - if (isset($this->changes["source"])) { - $source = $this->changes["source"]; + $source = $content; + if (!$source) { + if (is_null($this->getRecord())) { + if (isset($this->changes["source"])) { + $source = $this->changes["source"]; + } else { + throw new \LogicException("Can't render note without content set."); + } } else { - throw new \LogicException("Can't render note without content set."); + $source = $this->getRecord()->source; } - } else { - $source = $this->getRecord()->source; } $purifier = new HTMLPurifier($config); @@ -117,7 +150,7 @@ class Note extends Postable $this->save(); } - return $cached; + return $this->renderHTML($cached); } public function getSource(): string diff --git a/Web/Presenters/InternalAPIPresenter.php b/Web/Presenters/InternalAPIPresenter.php index 464059cb..eb9daac2 100644 --- a/Web/Presenters/InternalAPIPresenter.php +++ b/Web/Presenters/InternalAPIPresenter.php @@ -176,4 +176,26 @@ final class InternalAPIPresenter extends OpenVKPresenter exit(''); } } + + public function renderImageFilter() + { + $is_enabled = OPENVK_ROOT_CONF["openvk"]["preferences"]["notes"]["disableHotlinking"] ?? true; + $allowed_hosts = OPENVK_ROOT_CONF["openvk"]["preferences"]["notes"]["allowedHosts"] ?? []; + + $url = $this->requestParam("url"); + $url = base64_decode($url); + + if (!$is_enabled) { + $this->redirect($url); + } + + $url_parsed = parse_url($url); + $host = $url_parsed['host']; + + if (in_array($host, $allowed_hosts)) { + $this->redirect($url); + } else { + $this->redirect('/assets/packages/static/openvk/img/fn_placeholder.jpg'); + } + } } diff --git a/Web/routes.yml b/Web/routes.yml index 9738b740..f1cf2af6 100644 --- a/Web/routes.yml +++ b/Web/routes.yml @@ -413,6 +413,8 @@ routes: handler: "InternalAPI->getPhotosFromPost" - url: "/iapi/getPostTemplate/{num}_{num}" handler: "InternalAPI->getPostTemplate" + - url: "/image.php" + handler: "InternalAPI->imageFilter" - url: "/tour" handler: "About->tour" - url: "/fave" diff --git a/Web/static/img/fn_placeholder.jpg b/Web/static/img/fn_placeholder.jpg new file mode 100644 index 0000000000000000000000000000000000000000..25b7f914d560573f05a2d19792a6cb658bfcaabc GIT binary patch literal 15203 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