From f04d90af6faa1ec2796783afb5a8827b1b96585b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Mon, 13 Nov 2023 17:46:56 +0100 Subject: [PATCH] feat: add XSS protection by implementing form data filtering --- composer.json | 5 +- composer.lock | 262 +++++++++++++++++++++++- src/Form/AddressType.php | 1 + src/Form/ApplicationStatusType.php | 1 + src/Form/AssignmentType.php | 9 +- src/Form/AvailabilityType.php | 1 + src/Form/BankAccountType.php | 1 + src/Form/CommunicationType.php | 1 + src/Form/ContactType.php | 3 +- src/Form/DocumentCheckType.php | 1 + src/Form/Extension/AntiXssExtension.php | 42 ++++ src/Form/FaqType.php | 1 + src/Form/FeeType.php | 1 + src/Form/FeedbackType.php | 1 + src/Form/JobProfileType.php | 3 +- src/Form/TeamerApplicationType.php | 1 + src/Form/TeamerJobProfileType.php | 1 + src/Form/TeamerProfileType.php | 1 + src/Form/TrainingType.php | 1 + 19 files changed, 324 insertions(+), 13 deletions(-) create mode 100644 src/Form/Extension/AntiXssExtension.php diff --git a/composer.json b/composer.json index 82bb0be..32b9f21 100644 --- a/composer.json +++ b/composer.json @@ -64,7 +64,8 @@ "twig/html-extra": "^3.7", "twig/intl-extra": "^3.7", "twig/string-extra": "^3.7", - "twig/twig": "^2.12|^3.0" + "twig/twig": "^2.12|^3.0", + "voku/anti-xss": "^4.1" }, "config": { "allow-plugins": { @@ -127,4 +128,4 @@ "symfony/web-profiler-bundle": "6.3.*" }, "version": "0.1.0" -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 14a6e3f..f46fd88 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "df338b352545df0665c61f700f265651", + "content-hash": "807c60574ab0d809d424a5782a0f5806", "packages": [ { "name": "beberlei/doctrineextensions", @@ -9019,6 +9019,266 @@ }, "time": "2020-10-02T23:36:20+00:00" }, + { + "name": "voku/anti-xss", + "version": "4.1.42", + "source": { + "type": "git", + "url": "https://github.com/voku/anti-xss.git", + "reference": "bca1f8607e55a3c5077483615cd93bd8f11bd675" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/anti-xss/zipball/bca1f8607e55a3c5077483615cd93bd8f11bd675", + "reference": "bca1f8607e55a3c5077483615cd93bd8f11bd675", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "voku/portable-utf8": "~6.0.2" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "voku\\helper\\": "src/voku/helper/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "EllisLab Dev Team", + "homepage": "http://ellislab.com/" + }, + { + "name": "Lars Moelleken", + "email": "lars@moelleken.org", + "homepage": "https://www.moelleken.org/" + } + ], + "description": "anti xss-library", + "homepage": "https://github.com/voku/anti-xss", + "keywords": [ + "anti-xss", + "clean", + "security", + "xss" + ], + "support": { + "issues": "https://github.com/voku/anti-xss/issues", + "source": "https://github.com/voku/anti-xss/tree/4.1.42" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/anti-xss", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/anti-xss", + "type": "tidelift" + } + ], + "time": "2023-07-03T14:40:46+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b56450eed252f6801410d810c8e1727224ae0743" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", + "reference": "b56450eed252f6801410d810c8e1727224ae0743", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "http://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.1" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2022-03-08T17:03:00+00:00" + }, + { + "name": "voku/portable-utf8", + "version": "6.0.13", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-utf8.git", + "reference": "b8ce36bf26593e5c2e81b1850ef0ffb299d2043f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-utf8/zipball/b8ce36bf26593e5c2e81b1850ef0ffb299d2043f", + "reference": "b8ce36bf26593e5c2e81b1850ef0ffb299d2043f", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "symfony/polyfill-iconv": "~1.0", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php72": "~1.0", + "voku/portable-ascii": "~2.0.0" + }, + "require-dev": { + "phpstan/phpstan": "1.9.*@dev", + "phpstan/phpstan-strict-rules": "1.4.*@dev", + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0", + "thecodingmachine/phpstan-strict-rules": "1.0.*@dev", + "voku/phpstan-rules": "3.1.*@dev" + }, + "suggest": { + "ext-ctype": "Use Ctype for e.g. hexadecimal digit detection", + "ext-fileinfo": "Use Fileinfo for better binary file detection", + "ext-iconv": "Use iconv for best performance", + "ext-intl": "Use Intl for best performance", + "ext-json": "Use JSON for string detection", + "ext-mbstring": "Use Mbstring for best performance" + }, + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "(Apache-2.0 or GPL-2.0)" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Hamid Sarfraz", + "homepage": "http://pageconfig.com/" + }, + { + "name": "Lars Moelleken", + "homepage": "http://www.moelleken.org/" + } + ], + "description": "Portable UTF-8 library - performance optimized (unicode) string functions for php.", + "homepage": "https://github.com/voku/portable-utf8", + "keywords": [ + "UTF", + "clean", + "php", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "issues": "https://github.com/voku/portable-utf8/issues", + "source": "https://github.com/voku/portable-utf8/tree/6.0.13" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-utf8", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-utf8", + "type": "tidelift" + } + ], + "time": "2023-03-08T08:35:38+00:00" + }, { "name": "webmozart/assert", "version": "1.11.0", diff --git a/src/Form/AddressType.php b/src/Form/AddressType.php index 3332581..25c924d 100644 --- a/src/Form/AddressType.php +++ b/src/Form/AddressType.php @@ -33,6 +33,7 @@ class AddressType extends AbstractType { $resolver->setDefaults([ 'data_class' => Address::class, + 'anti_xss' => true, ]); } } \ No newline at end of file diff --git a/src/Form/ApplicationStatusType.php b/src/Form/ApplicationStatusType.php index acfd4f1..3fb70c4 100644 --- a/src/Form/ApplicationStatusType.php +++ b/src/Form/ApplicationStatusType.php @@ -41,6 +41,7 @@ class ApplicationStatusType extends AbstractType { $resolver->setDefaults([ 'data_class' => ApplicationStatusDto::class, + 'anti_xss' => true, ]); } } \ No newline at end of file diff --git a/src/Form/AssignmentType.php b/src/Form/AssignmentType.php index 11854a4..99c8f67 100644 --- a/src/Form/AssignmentType.php +++ b/src/Form/AssignmentType.php @@ -12,7 +12,6 @@ use Doctrine\ORM\EntityRepository; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; -use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; @@ -24,13 +23,6 @@ class AssignmentType extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { $builder - ->add('status', ChoiceType::class, [ - 'label' => 'Status', - 'choices' => [ - 'offen' => Assignment::STATUS_OPEN, - 'geschlossen' => Assignment::STATUS_CLOSED, - ], - ]) ->add('availableDispositions', IntegerType::class, [ 'label' => 'zu vergeben', 'required' => false, @@ -144,6 +136,7 @@ class AssignmentType extends AbstractType { $resolver->setDefaults([ 'data_class' => Assignment::class, + 'anti_xss' => true, ]); } } \ No newline at end of file diff --git a/src/Form/AvailabilityType.php b/src/Form/AvailabilityType.php index c0d4ec2..51d7ef3 100644 --- a/src/Form/AvailabilityType.php +++ b/src/Form/AvailabilityType.php @@ -32,6 +32,7 @@ class AvailabilityType extends AbstractType { $resolver->setDefaults([ 'data_class' => Availability::class, + 'anti_xss' => true, ]); } } \ No newline at end of file diff --git a/src/Form/BankAccountType.php b/src/Form/BankAccountType.php index 7780f56..c95d60a 100644 --- a/src/Form/BankAccountType.php +++ b/src/Form/BankAccountType.php @@ -32,6 +32,7 @@ class BankAccountType extends AbstractType { $resolver->setDefaults([ 'data_class' => BankAccount::class, + 'anti_xss' => true, ]); } } diff --git a/src/Form/CommunicationType.php b/src/Form/CommunicationType.php index c61ab5b..be99506 100644 --- a/src/Form/CommunicationType.php +++ b/src/Form/CommunicationType.php @@ -30,6 +30,7 @@ class CommunicationType extends AbstractType { $resolver->setDefaults([ 'data_class' => Communication::class, + 'anti_xss' => true, ]); } } \ No newline at end of file diff --git a/src/Form/ContactType.php b/src/Form/ContactType.php index ad7bfb4..00ebbe0 100644 --- a/src/Form/ContactType.php +++ b/src/Form/ContactType.php @@ -33,7 +33,8 @@ class ContactType extends AbstractType { $resolver ->setDefaults([ - 'data_class' => Contact::class, + 'data_class' => Contact::class, + 'anti_xss' => true, ]) ->setRequired(['upload_session']) ->setAllowedTypes('upload_session', UploadSessionDto::class) diff --git a/src/Form/DocumentCheckType.php b/src/Form/DocumentCheckType.php index 84c6f60..b1c16c2 100644 --- a/src/Form/DocumentCheckType.php +++ b/src/Form/DocumentCheckType.php @@ -46,6 +46,7 @@ class DocumentCheckType extends AbstractType 'abgelehnt' => Upload::STATUS_REJECTED, ], ], + 'anti_xss' => true, ]) ->setRequired(['document_type']) ->setAllowedTypes('document_type', 'string') diff --git a/src/Form/Extension/AntiXssExtension.php b/src/Form/Extension/AntiXssExtension.php new file mode 100644 index 0000000..facc5d7 --- /dev/null +++ b/src/Form/Extension/AntiXssExtension.php @@ -0,0 +1,42 @@ +addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { + $data = $event->getData(); + $antiXss = new AntiXSS(); + foreach ($data as $key => $value) { + $data[$key] = $antiXss->xss_clean($value); + } + $event->setData($data); + }); + } + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'anti_xss' => false, + ]); + } + + public static function getExtendedTypes(): iterable + { + return [FormType::class]; + } +} diff --git a/src/Form/FaqType.php b/src/Form/FaqType.php index 7ddfcf4..4512cf6 100644 --- a/src/Form/FaqType.php +++ b/src/Form/FaqType.php @@ -28,6 +28,7 @@ class FaqType extends AbstractType { $resolver->setDefaults([ 'data_class' => Faq::class, + 'anti_xss' => true, ]); } } \ No newline at end of file diff --git a/src/Form/FeeType.php b/src/Form/FeeType.php index a0a8a85..e6063a3 100644 --- a/src/Form/FeeType.php +++ b/src/Form/FeeType.php @@ -32,6 +32,7 @@ class FeeType extends AbstractType { $resolver->setDefaults([ 'data_class' => Fee::class, + 'anti_xss' => true, ]); } } \ No newline at end of file diff --git a/src/Form/FeedbackType.php b/src/Form/FeedbackType.php index 829399d..fdf0fab 100644 --- a/src/Form/FeedbackType.php +++ b/src/Form/FeedbackType.php @@ -41,6 +41,7 @@ class FeedbackType extends AbstractType $resolver ->setDefaults([ 'data_class' => Feedback::class, + 'anti_xss' => true, ]) ->setRequired(['feedback_set']) ->setAllowedTypes('feedback_set', FeedbackSet::class) diff --git a/src/Form/JobProfileType.php b/src/Form/JobProfileType.php index f91775f..1838025 100644 --- a/src/Form/JobProfileType.php +++ b/src/Form/JobProfileType.php @@ -50,7 +50,7 @@ class JobProfileType extends AbstractType 'label' => 'Feedback-Vorlage', 'class' => FeedbackSet::class, 'choice_label' => 'name', - 'placeholder' => '', + 'placeholder' => 'Kein Feedback', ]) ; } @@ -59,6 +59,7 @@ class JobProfileType extends AbstractType { $resolver->setDefaults([ 'data_class' => JobProfile::class, + 'anti_xss' => true, ]); } } \ No newline at end of file diff --git a/src/Form/TeamerApplicationType.php b/src/Form/TeamerApplicationType.php index f405d1d..5d34e91 100644 --- a/src/Form/TeamerApplicationType.php +++ b/src/Form/TeamerApplicationType.php @@ -61,6 +61,7 @@ class TeamerApplicationType extends AbstractType { $resolver->setDefaults([ 'data_class' => Application::class, + 'anti_xss' => true, ]); } } \ No newline at end of file diff --git a/src/Form/TeamerJobProfileType.php b/src/Form/TeamerJobProfileType.php index 66def3f..2609e52 100644 --- a/src/Form/TeamerJobProfileType.php +++ b/src/Form/TeamerJobProfileType.php @@ -80,6 +80,7 @@ class TeamerJobProfileType extends AbstractType 'data_class' => Teamer::class, 'job_profiles' => [], 'selectable_job_profiles' => [], + 'anti_xss' => true, ]); } } \ No newline at end of file diff --git a/src/Form/TeamerProfileType.php b/src/Form/TeamerProfileType.php index 1165fbb..303ee09 100644 --- a/src/Form/TeamerProfileType.php +++ b/src/Form/TeamerProfileType.php @@ -113,6 +113,7 @@ class TeamerProfileType extends AbstractType 'validation_groups' => [ 'profile', ], + 'anti_xss' => true, ]) ->setRequired(['upload_session']) ->setAllowedTypes('upload_session', UploadSessionDto::class) diff --git a/src/Form/TrainingType.php b/src/Form/TrainingType.php index 7d629e2..11596b1 100644 --- a/src/Form/TrainingType.php +++ b/src/Form/TrainingType.php @@ -23,6 +23,7 @@ class TrainingType extends AbstractType { $resolver->setDefaults([ 'data_class' => Training::class, + 'anti_xss' => true, ]); } } \ No newline at end of file