From 9714d0f03682f33e3c0cee38e1bf510448ea48c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?mr=E2=9D=A4=EF=B8=8F=F0=9F=A4=A2?=
<99399973+mrilyew@users.noreply.github.com>
Date: Sun, 15 Jun 2025 16:55:27 +0300
Subject: [PATCH 1/2] feat(notes): use whitelist for images sources (#1352)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Переиначенный #942, но в нём картинка скачивалась с сервера, в этом в
конфиге задаётся список разрешённых хостов и затем идёт редирект если
ссылка прошла проверку. Если не прошла то редиректает на заглушку.
Впрочем, это не поможет если в конфиге не указан cdn, но по крайней мере
не будет приколов с автозапуском методов на основном сайте
После мержа в конфиг добавьте kaslana.ovk.to

---------
Co-authored-by: n1rwana <93197434+n1rwana@users.noreply.github.com>
---
Web/Models/Entities/Note.php | 51 ++++++++++++++----
Web/Presenters/InternalAPIPresenter.php | 22 ++++++++
Web/Presenters/templates/About/Version.xml | 8 +--
.../templates/components/textArea.xml | 4 +-
Web/routes.yml | 2 +
Web/static/img/fn_placeholder.jpg | Bin 0 -> 14532 bytes
openvk-example.yml | 3 ++
7 files changed, 76 insertions(+), 14 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/Presenters/templates/About/Version.xml b/Web/Presenters/templates/About/Version.xml
index c4d9c132..8b61d41c 100644
--- a/Web/Presenters/templates/About/Version.xml
+++ b/Web/Presenters/templates/About/Version.xml
@@ -385,8 +385,8 @@
OpenVK QA Team | @@ -486,7 +486,7 @@
---|