[CORE][SECURITY][HTML] Refactor Security::sanitize to HTML::sanitize

Update composer dependencies, move more general deps from ActivityPub to Core
This commit is contained in:
Diogo Peralta Cordeiro 2022-01-12 17:12:26 +00:00
parent 968b1751fd
commit 480f570238
Signed by: diogo
GPG Key ID: 18D2D35001FBFAB0
9 changed files with 322 additions and 262 deletions

View File

@ -30,7 +30,6 @@ use App\Core\GSFile;
use function App\Core\I18n\_m; use function App\Core\I18n\_m;
use App\Core\Modules\Component; use App\Core\Modules\Component;
use App\Core\Router\Router; use App\Core\Router\Router;
use App\Core\Security;
use App\Core\VisibilityScope; use App\Core\VisibilityScope;
use App\Entity\Activity; use App\Entity\Activity;
use App\Entity\Actor; use App\Entity\Actor;
@ -43,6 +42,7 @@ use App\Util\Exception\RedirectException;
use App\Util\Exception\ServerException; use App\Util\Exception\ServerException;
use App\Util\Form\FormFields; use App\Util\Form\FormFields;
use App\Util\Formatting; use App\Util\Formatting;
use App\Util\HTML;
use Component\Attachment\Entity\ActorToAttachment; use Component\Attachment\Entity\ActorToAttachment;
use Component\Attachment\Entity\AttachmentToNote; use Component\Attachment\Entity\AttachmentToNote;
use Component\Conversation\Conversation; use Component\Conversation\Conversation;
@ -146,7 +146,7 @@ class Posting extends Component
$extra_args = []; $extra_args = [];
Event::handle('AddExtraArgsToNoteContent', [$request, $actor, $data, &$extra_args, $form_params, $form]); Event::handle('AddExtraArgsToNoteContent', [$request, $actor, $data, &$extra_args, $form_params, $form]);
$target = !array_key_exists('in', $data) || $data['in'] === 'public' ? $context_actor : null; $target = !\array_key_exists('in', $data) || $data['in'] === 'public' ? $context_actor : null;
self::storeLocalNote( self::storeLocalNote(
actor: $user->getActor(), actor: $user->getActor(),
@ -290,7 +290,6 @@ class Posting extends Component
Event::handle('NewNotification', [$actor, $activity, ['object' => $mention_ids], _m('{nickname} created a note {note_id}.', ['{nickname}' => $actor->getNickname(), '{note_id}' => $activity->getObjectId()])]); Event::handle('NewNotification', [$actor, $activity, ['object' => $mention_ids], _m('{nickname} created a note {note_id}.', ['{nickname}' => $actor->getNickname(), '{note_id}' => $activity->getObjectId()])]);
} }
return $note; return $note;
} }
@ -303,7 +302,7 @@ class Posting extends Component
return Event::stop; return Event::stop;
case 'text/html': case 'text/html':
// TODO: It has to linkify and stuff as well // TODO: It has to linkify and stuff as well
$rendered = Security::sanitize($content); $rendered = HTML::sanitize($content);
return Event::stop; return Event::stop;
default: default:
return Event::next; return Event::next;

View File

@ -17,6 +17,8 @@
"erusev/parsedown": "^1.7", "erusev/parsedown": "^1.7",
"knplabs/knp-time-bundle": "^1.17", "knplabs/knp-time-bundle": "^1.17",
"lstrojny/functional-php": "^1.17", "lstrojny/functional-php": "^1.17",
"masterminds/html5": "^2.7",
"mf2/mf2": "^0.4.6",
"nyholm/psr7": "^1.4", "nyholm/psr7": "^1.4",
"odolbeau/phone-number-bundle": "^3.1", "odolbeau/phone-number-bundle": "^3.1",
"oro/doctrine-extensions": "^2.0", "oro/doctrine-extensions": "^2.0",
@ -199,7 +201,9 @@
"Codeception\\": "src/Codeception", "Codeception\\": "src/Codeception",
"Codeception\\Extension\\": "ext" "Codeception\\Extension\\": "ext"
}, },
"files": ["functions.php"] "files": [
"functions.php"
]
}, },
"require": { "require": {
"php": ">=5.6.0 <9.0", "php": ">=5.6.0 <9.0",
@ -217,7 +221,9 @@
"codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.1.1 | ^9.0", "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.1.1 | ^9.0",
"codeception/stub": "^2.0 | ^3.0 | ^4.0" "codeception/stub": "^2.0 | ^3.0 | ^4.0"
}, },
"bin":["codecept"], "bin": [
"codecept"
],
"source": { "source": {
"url": "https://github.com/someonewithpc/Codeception.git", "url": "https://github.com/someonewithpc/Codeception.git",
"type": "git", "type": "git",

34
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "4533a7f4059639e03dbbb34b48f09cf5", "content-hash": "db8e2506856c53c049c3338ab21cfc88",
"packages": [ "packages": [
{ {
"name": "alchemy/binary-driver", "name": "alchemy/binary-driver",
@ -1341,24 +1341,24 @@
}, },
{ {
"name": "doctrine/orm", "name": "doctrine/orm",
"version": "2.10.4", "version": "2.11.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/orm.git", "url": "https://github.com/doctrine/orm.git",
"reference": "cccb2e2fdfed2969afb3d65c5ea82bafdefbe1a7" "reference": "bfed8cb6ed448f4ab1ea3fff06e4d6c44439e4ef"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/orm/zipball/cccb2e2fdfed2969afb3d65c5ea82bafdefbe1a7", "url": "https://api.github.com/repos/doctrine/orm/zipball/bfed8cb6ed448f4ab1ea3fff06e4d6c44439e4ef",
"reference": "cccb2e2fdfed2969afb3d65c5ea82bafdefbe1a7", "reference": "bfed8cb6ed448f4ab1ea3fff06e4d6c44439e4ef",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"composer/package-versions-deprecated": "^1.8", "composer-runtime-api": "^2",
"doctrine/cache": "^1.12.1 || ^2.1.1", "doctrine/cache": "^1.12.1 || ^2.1.1",
"doctrine/collections": "^1.5", "doctrine/collections": "^1.5",
"doctrine/common": "^3.0.3", "doctrine/common": "^3.0.3",
"doctrine/dbal": "^2.13.1 || ^3.1.1", "doctrine/dbal": "^2.13.1 || ^3.2",
"doctrine/deprecations": "^0.5.3", "doctrine/deprecations": "^0.5.3",
"doctrine/event-manager": "^1.1", "doctrine/event-manager": "^1.1",
"doctrine/inflector": "^1.4 || ^2.0", "doctrine/inflector": "^1.4 || ^2.0",
@ -1367,7 +1367,7 @@
"doctrine/persistence": "^2.2", "doctrine/persistence": "^2.2",
"ext-ctype": "*", "ext-ctype": "*",
"ext-pdo": "*", "ext-pdo": "*",
"php": "^7.1 ||^8.0", "php": "^7.1 || ^8.0",
"psr/cache": "^1 || ^2 || ^3", "psr/cache": "^1 || ^2 || ^3",
"symfony/console": "^3.0 || ^4.0 || ^5.0 || ^6.0", "symfony/console": "^3.0 || ^4.0 || ^5.0 || ^6.0",
"symfony/polyfill-php72": "^1.23", "symfony/polyfill-php72": "^1.23",
@ -1380,12 +1380,12 @@
"doctrine/annotations": "^1.13", "doctrine/annotations": "^1.13",
"doctrine/coding-standard": "^9.0", "doctrine/coding-standard": "^9.0",
"phpbench/phpbench": "^0.16.10 || ^1.0", "phpbench/phpbench": "^0.16.10 || ^1.0",
"phpstan/phpstan": "1.2.0", "phpstan/phpstan": "1.3.3",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
"squizlabs/php_codesniffer": "3.6.2", "squizlabs/php_codesniffer": "3.6.2",
"symfony/cache": "^4.4 || ^5.2", "symfony/cache": "^4.4 || ^5.4 || ^6.0",
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0", "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
"vimeo/psalm": "4.15.0" "vimeo/psalm": "4.18.1"
}, },
"suggest": { "suggest": {
"symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0", "symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0",
@ -1434,9 +1434,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/doctrine/orm/issues", "issues": "https://github.com/doctrine/orm/issues",
"source": "https://github.com/doctrine/orm/tree/2.10.4" "source": "https://github.com/doctrine/orm/tree/2.11.0"
}, },
"time": "2021-12-20T21:23:47+00:00" "time": "2022-01-12T13:20:33+00:00"
}, },
{ {
"name": "doctrine/persistence", "name": "doctrine/persistence",
@ -12444,12 +12444,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "707ce16c439d6e225f4dc81bd4b2e25b8befcd36" "reference": "66b515292c080298a44a2cd15680711f706ca7bf"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/707ce16c439d6e225f4dc81bd4b2e25b8befcd36", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/66b515292c080298a44a2cd15680711f706ca7bf",
"reference": "707ce16c439d6e225f4dc81bd4b2e25b8befcd36", "reference": "66b515292c080298a44a2cd15680711f706ca7bf",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -12501,7 +12501,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-01-12T11:13:52+00:00" "time": "2022-01-12T13:05:10+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",

View File

@ -1,7 +1,5 @@
{ {
"require": { "require": {
"landrok/activitypub": "^0.5.6", "landrok/activitypub": "^0.5.6"
"masterminds/html5": "^2.7",
"mf2/mf2": "^0.4.6"
} }
} }

View File

@ -53,6 +53,7 @@ use App\Security\EmailVerifier;
use App\Util\Common; use App\Util\Common;
use App\Util\Exception\ConfigurationException; use App\Util\Exception\ConfigurationException;
use App\Util\Formatting; use App\Util\Formatting;
use App\Util\HTML;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use HtmlSanitizer\SanitizerInterface; use HtmlSanitizer\SanitizerInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -169,7 +170,8 @@ class GNUsocial implements EventSubscriberInterface
DB::setManager($this->entity_manager); DB::setManager($this->entity_manager);
Form::setFactory($this->form_factory); Form::setFactory($this->form_factory);
Queue::setMessageBus($this->message_bus); Queue::setMessageBus($this->message_bus);
Security::setHelper($this->security, $this->sanitizer); Security::setHelper($this->security);
HTML::setSanitizer($this->sanitizer);
Router::setRouter($this->router); Router::setRouter($this->router);
HTTPClient::setClient($this->client); HTTPClient::setClient($this->client);
Formatting::setTwig($this->twig); Formatting::setTwig($this->twig);

View File

@ -34,7 +34,6 @@ namespace App\Core;
use App\Entity\LocalUser; use App\Entity\LocalUser;
use BadMethodCallException; use BadMethodCallException;
use HtmlSanitizer\SanitizerInterface;
use Symfony\Component\Security\Core\Security as SymfonySecurity; use Symfony\Component\Security\Core\Security as SymfonySecurity;
/** /**
@ -43,31 +42,24 @@ use Symfony\Component\Security\Core\Security as SymfonySecurity;
* *
* @codeCoverageIgnore * @codeCoverageIgnore
* @mixin SymfonySecurity * @mixin SymfonySecurity
* @mixin SanitizerInterface
* *
* @method static LocalUser getUser() * @method static LocalUser getUser()
*/ */
abstract class Security abstract class Security
{ {
private static ?SymfonySecurity $security; private static ?SymfonySecurity $security;
private static ?SanitizerInterface $sanitizer;
public static function setHelper($sec, $san): void public static function setHelper($sec): void
{ {
self::$security = $sec; self::$security = $sec;
self::$sanitizer = $san;
} }
public static function __callStatic(string $name, array $args) public static function __callStatic(string $name, array $args)
{ {
if (method_exists(self::$security, $name)) { if (method_exists(self::$security, $name)) {
return self::$security->{$name}(...$args); return self::$security->{$name}(...$args);
} else {
if (method_exists(self::$sanitizer, $name)) {
return self::$sanitizer->{$name}(...$args);
} else { } else {
throw new BadMethodCallException("Method Security::{$name} doesn't exist"); throw new BadMethodCallException("Method Security::{$name} doesn't exist");
} }
} }
}
} }

View File

@ -29,11 +29,23 @@ declare(strict_types = 1);
namespace App\Util; namespace App\Util;
use BadMethodCallException;
use Functional as F; use Functional as F;
use HtmlSanitizer\SanitizerInterface;
use InvalidArgumentException; use InvalidArgumentException;
/**
* @mixin SanitizerInterface
*/
abstract class HTML abstract class HTML
{ {
private static ?SanitizerInterface $sanitizer;
public static function setSanitizer($sanitizer): void
{
self::$sanitizer = $sanitizer;
}
/** /**
* Tags whose content is sensitive to indentation, so we shouldn't indent them * Tags whose content is sensitive to indentation, so we shouldn't indent them
*/ */
@ -136,4 +148,13 @@ abstract class HTML
return $out; return $out;
} }
} }
public static function __callStatic(string $name, array $args)
{
if (method_exists(self::$sanitizer, $name)) {
return self::$sanitizer->{$name}(...$args);
} else {
throw new BadMethodCallException("Method Security::{$name} doesn't exist");
}
}
} }

View File

@ -5,9 +5,51 @@
"behat/gherkin": { "behat/gherkin": {
"version": "v4.9.0" "version": "v4.9.0"
}, },
"codeception/codeception": {
"version": "4.1",
"recipe": {
"repo": "github.com/symfony/recipes-contrib",
"branch": "master",
"version": "2.3",
"ref": "30798e46831f4fc641fca83c0423918518901cd7"
},
"files": [
"codeception.yml",
"tests/_data/.gitignore",
"tests/_output/.gitignore",
"tests/_support/AcceptanceTester.php",
"tests/_support/FunctionalTester.php",
"tests/_support/Helper/Acceptance.php",
"tests/_support/Helper/Functional.php",
"tests/_support/Helper/Unit.php",
"tests/_support/UnitTester.php",
"tests/_support/_generated/.gitignore",
"tests/acceptance.suite.yml",
"tests/acceptance/.gitignore",
"tests/functional.suite.yml",
"tests/functional/.gitignore",
"tests/unit.suite.yml",
"tests/unit/.gitignore"
]
},
"codeception/lib-asserts": {
"version": "1.13.2"
},
"codeception/lib-innerbrowser": {
"version": "2.0.1"
},
"codeception/module-asserts": { "codeception/module-asserts": {
"version": "1.3.1" "version": "1.3.1"
}, },
"codeception/module-phpbrowser": {
"version": "2.0.2"
},
"codeception/module-symfony": {
"version": "2.1.1"
},
"codeception/phpunit-wrapper": {
"version": "9.0.6"
},
"codeception/stub": { "codeception/stub": {
"version": "4.0.1" "version": "4.0.1"
}, },

View File

@ -103,7 +103,7 @@ class CommonTest extends GNUsocialTestCase
$user->setId(0); $user->setId(0);
$sec = $this->getMockBuilder(SSecurity::class)->setConstructorArgs([self::$kernel->getContainer()])->getMock(); $sec = $this->getMockBuilder(SSecurity::class)->setConstructorArgs([self::$kernel->getContainer()])->getMock();
$sec->method('getUser')->willReturn($user); $sec->method('getUser')->willReturn($user);
Security::setHelper($sec, null); Security::setHelper($sec);
// $cookies = $client->loginUser($user)->getCookieJar(); // $cookies = $client->loginUser($user)->getCookieJar();
// $cookies->get('MOCKSESSID')->getValue(); // $cookies->get('MOCKSESSID')->getValue();