diff --git a/app/Http/Controllers/GroupController.php b/app/Http/Controllers/GroupController.php index fe69022718..ca28014d04 100644 --- a/app/Http/Controllers/GroupController.php +++ b/app/Http/Controllers/GroupController.php @@ -455,7 +455,7 @@ public function delete($id): RedirectResponse $group->delete(); return redirect('/group')->with('success', __('groups.delete_succeeded', [ - 'name' => $name, + 'name' => e($name), ])); } else { return redirect('/user/forbidden'); @@ -576,7 +576,7 @@ public function getJoinGroup($group_id): RedirectResponse return redirect() ->back() ->with('success', __('groups.now_following', [ - 'name' => $group->name, + 'name' => e($group->name), 'link' => url('/group/view/'.$group->idgroups), ])); } catch (\Exception $e) { @@ -661,7 +661,7 @@ public function inviteNearbyRestarter($groupId, $userId): RedirectResponse } } - return redirect('/group/nearby/'.intval($groupId))->with('success', $user->name.' has been invited'); + return redirect('/group/nearby/'.intval($groupId))->with('success', e($user->name).' has been invited'); } /** diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index fe174a0c8f..419ba1f523 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -294,7 +294,7 @@ public function postSoftDeleteUser(Request $request): RedirectResponse if (Auth::id() !== $user_id) { return redirect('user/all')->with('danger', __('profile.soft_deleted', [ - 'name' => $old_user_name + 'name' => e($old_user_name) ])); } else { return redirect('login'); diff --git a/app/Http/Middleware/AcceptUserInvites.php b/app/Http/Middleware/AcceptUserInvites.php index 465d1c2af1..3631e4d762 100644 --- a/app/Http/Middleware/AcceptUserInvites.php +++ b/app/Http/Middleware/AcceptUserInvites.php @@ -45,13 +45,13 @@ public function handle(Request $request, Closure $next): Response $acceptance->delete(); $request->session()->push('invites-feedback', __('groups.you_have_joined', [ 'url' => url("/group/view/{$group->idgroups}"), - 'name' => $group->name + 'name' => e($group->name) ])); // Else that must mean the User is already part of the Group. // We can then delete the Invite and create a new session } else { - $request->session()->push('invites-feedback', 'You are already a member of idgroups}").'">'.e($group->name).''); } } $request->session()->forget('groups'); @@ -78,13 +78,13 @@ public function handle(Request $request, Closure $next): Response $acceptance->delete(); $request->session()->push('invites-feedback', __('events.you_have_joined', [ 'url' => url("/party/view/{$event->idevents}"), - 'name' => $event->venue + 'name' => e($event->venue) ])); // Else that must mean the User is already part of the Event. // We can then delete the Invite and create a new session } else { - $request->session()->push('invites-feedback', 'You are already a member of idevents}").'">'.e($event->venue).''); } } $request->session()->forget('events'); diff --git a/app/Models/Brands.php b/app/Models/Brands.php index bb0dad6dd1..1535a94715 100644 --- a/app/Models/Brands.php +++ b/app/Models/Brands.php @@ -21,4 +21,9 @@ class Brands extends Model * @var array */ protected $hidden = []; + + public function setBrandNameAttribute($value) + { + $this->attributes['brand_name'] = $value === null ? null : strip_tags((string) $value); + } } diff --git a/app/Models/Category.php b/app/Models/Category.php index 0b8f0ddf20..e678160513 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -33,6 +33,11 @@ class Category extends Model // Setters + public function setNameAttribute($value) + { + $this->attributes['name'] = $value === null ? null : strip_tags((string) $value); + } + //Getters public function findAll() { diff --git a/app/Models/Group.php b/app/Models/Group.php index b949d3d555..a49a083c1a 100644 --- a/app/Models/Group.php +++ b/app/Models/Group.php @@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Lang; use Illuminate\Support\Facades\Log; +use Stevebauman\Purify\Facades\Purify; use OwenIt\Auditing\Contracts\Auditable; use Illuminate\Database\Eloquent\SoftDeletes; @@ -446,6 +447,16 @@ public function setDistanceAttribute($val) $this->distance = $val; } + public function setNameAttribute($value) + { + $this->attributes['name'] = $value === null ? null : strip_tags((string) $value); + } + + public function setFreeTextAttribute($value) + { + $this->attributes['free_text'] = $value === null ? null : Purify::clean((string) $value); + } + public function createDiscourseGroup() { // Get the host who created the group. $success = false; diff --git a/app/Models/GroupTags.php b/app/Models/GroupTags.php index ae1ad1bffc..9ffbeba2a0 100644 --- a/app/Models/GroupTags.php +++ b/app/Models/GroupTags.php @@ -42,5 +42,10 @@ public function groupTagGroups(): HasMany // Setters + public function setTagNameAttribute($value) + { + $this->attributes['tag_name'] = $value === null ? null : strip_tags((string) $value); + } + //Getters } diff --git a/app/Models/Network.php b/app/Models/Network.php index 2a0bda8ba6..fa18de9afb 100644 --- a/app/Models/Network.php +++ b/app/Models/Network.php @@ -6,11 +6,22 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use App\Models\Group; use Illuminate\Database\Eloquent\Model; +use Stevebauman\Purify\Facades\Purify; class Network extends Model { use HasFactory; + public function setNameAttribute($value) + { + $this->attributes['name'] = $value === null ? null : strip_tags((string) $value); + } + + public function setDescriptionAttribute($value) + { + $this->attributes['description'] = $value === null ? null : Purify::clean((string) $value); + } + public function groups(): BelongsToMany { return $this->belongsToMany(Group::class, 'group_network', 'network_id', 'group_id'); diff --git a/app/Models/Party.php b/app/Models/Party.php index 25aadd1a2e..e72a36a3b4 100644 --- a/app/Models/Party.php +++ b/app/Models/Party.php @@ -13,6 +13,7 @@ use DB; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Stevebauman\Purify\Facades\Purify; use Illuminate\Support\Str; use Notification; use OwenIt\Auditing\Contracts\Auditable; @@ -767,6 +768,16 @@ public function getEventEndUtcAttribute() { return array_key_exists('event_end_utc', $this->attributes) ? Carbon::parse($this->attributes['event_end_utc'], 'UTC')->toIso8601String() : null; } + public function setVenueAttribute($value) + { + $this->attributes['venue'] = $value === null ? null : strip_tags((string) $value); + } + + public function setFreeTextAttribute($value) + { + $this->attributes['free_text'] = $value === null ? null : Purify::clean((string) $value); + } + // Mutators for previous event_date/start/end fields. These are now superceded by the UTC fields and therefore // should never be set directly. Throw exceptions to ensure that they are not. public function setEventDateAttribute($val) { diff --git a/app/Models/Skills.php b/app/Models/Skills.php index fb5a5a98b8..efa9d547a0 100644 --- a/app/Models/Skills.php +++ b/app/Models/Skills.php @@ -26,5 +26,10 @@ class Skills extends Model // Setters + public function setSkillNameAttribute($value) + { + $this->attributes['skill_name'] = $value === null ? null : strip_tags((string) $value); + } + //Getters } diff --git a/app/Models/User.php b/app/Models/User.php index 9cd00516e5..40c1d0bd55 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use App\Events\UserDeleted; use App\Events\UserUpdated; +use Stevebauman\Purify\Facades\Purify; use App\Helpers\Fixometer; use App\Models\Network; use App\Models\UserGroups; @@ -64,6 +65,21 @@ class User extends Authenticatable implements Auditable, HasLocalePreference 'remember_token', ]; + public function setNameAttribute($value) + { + $this->attributes['name'] = $value === null ? null : strip_tags((string) $value); + } + + public function setLocationAttribute($value) + { + $this->attributes['location'] = $value === null ? null : strip_tags((string) $value); + } + + public function setBiographyAttribute($value) + { + $this->attributes['biography'] = $value === null ? null : Purify::clean((string) $value); + } + /** * The event map for the model. * diff --git a/composer.json b/composer.json index 88c2ac6508..164523713f 100644 --- a/composer.json +++ b/composer.json @@ -44,9 +44,10 @@ "sentry/sentry-laravel": "^4.13", "soundasleep/html2text": "^1.1", "spatie/calendar-links": "^1.6", - "spatie/laravel-validation-rules": "^3.4", "spatie/laravel-html": "^3.12.0", + "spatie/laravel-validation-rules": "^3.4", "spinen/laravel-discourse-sso": "dev-l12-compatibility", + "stevebauman/purify": "^6.3", "symfony/http-client": "^7.2", "symfony/http-foundation": "^7.2", "symfony/mailgun-mailer": "^7.2", diff --git a/composer.lock b/composer.lock index 0be2dcd596..dffeae7e4c 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": "a79d8dc54c6b1514c5b254e265806d8f", + "content-hash": "3c3716ad51a3bef71683f8aa7d634fe5", "packages": [ { "name": "addwiki/mediawiki-api", @@ -1706,6 +1706,67 @@ ], "time": "2023-06-01T07:04:22+00:00" }, + { + "name": "ezyang/htmlpurifier", + "version": "v4.19.0", + "source": { + "type": "git", + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/b287d2a16aceffbf6e0295559b39662612b77fcf", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf", + "shasum": "" + }, + "require": { + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "require-dev": { + "cerdic/css-tidy": "^1.7 || ^2.0", + "simpletest/simpletest": "dev-master" + }, + "suggest": { + "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", + "ext-bcmath": "Used for unit conversion and imagecrash protection", + "ext-iconv": "Converts text to and from non-UTF-8 encodings", + "ext-tidy": "Used for pretty-printing HTML" + }, + "type": "library", + "autoload": { + "files": [ + "library/HTMLPurifier.composer.php" + ], + "psr-0": { + "HTMLPurifier": "library/" + }, + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "support": { + "issues": "https://github.com/ezyang/htmlpurifier/issues", + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.0" + }, + "time": "2025-10-17T16:34:55+00:00" + }, { "name": "filp/whoops", "version": "2.18.0", @@ -7177,6 +7238,72 @@ }, "time": "2025-02-17T00:51:48+00:00" }, + { + "name": "stevebauman/purify", + "version": "v6.3.2", + "source": { + "type": "git", + "url": "https://github.com/stevebauman/purify.git", + "reference": "deba4aa55a45a7593c369b52d481c87b545a5bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stevebauman/purify/zipball/deba4aa55a45a7593c369b52d481c87b545a5bf8", + "reference": "deba4aa55a45a7593c369b52d481c87b545a5bf8", + "shasum": "" + }, + "require": { + "ezyang/htmlpurifier": "^4.17", + "illuminate/contracts": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "php": ">=7.4" + }, + "require-dev": { + "orchestra/testbench": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "phpunit/phpunit": "^8.0|^9.0|^10.0|^11.5.3|^12.5.12" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Purify": "Stevebauman\\Purify\\Facades\\Purify" + }, + "providers": [ + "Stevebauman\\Purify\\PurifyServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Stevebauman\\Purify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Steve Bauman", + "email": "steven_bauman@outlook.com" + } + ], + "description": "An HTML Purifier / Sanitizer for Laravel", + "keywords": [ + "Purifier", + "clean", + "cleaner", + "html", + "laravel", + "purification", + "purify" + ], + "support": { + "issues": "https://github.com/stevebauman/purify/issues", + "source": "https://github.com/stevebauman/purify/tree/v6.3.2" + }, + "time": "2026-03-18T16:42:42+00:00" + }, { "name": "swagger-api/swagger-ui", "version": "v5.21.0", @@ -13980,5 +14107,5 @@ "php": "^8.2" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/config/purify.php b/config/purify.php new file mode 100644 index 0000000000..e9b68556f9 --- /dev/null +++ b/config/purify.php @@ -0,0 +1,120 @@ + 'default', + + /* + |-------------------------------------------------------------------------- + | Config sets + |-------------------------------------------------------------------------- + | + | Here you may configure various sets of configuration for differentiated use of HTMLPurifier. + | A specific set of configuration can be applied by calling the "config($name)" method on + | a Purify instance. Feel free to add/remove/customize these attributes as you wish. + | + | Documentation: http://htmlpurifier.org/live/configdoc/plain.html + | + | Core.Encoding The encoding to convert input to. + | HTML.Doctype Doctype to use during filtering. + | HTML.Allowed The allowed HTML Elements with their allowed attributes. + | HTML.ForbiddenElements The forbidden HTML elements. Elements that are listed in this + | string will be removed, however their content will remain. + | CSS.AllowedProperties The Allowed CSS properties. + | AutoFormat.AutoParagraph Newlines are converted in to paragraphs whenever possible. + | AutoFormat.RemoveEmpty Remove empty elements that contribute no semantic information to the document. + | + */ + + 'configs' => [ + + 'default' => [ + 'Core.Encoding' => 'utf-8', + 'HTML.Doctype' => 'HTML 4.01 Transitional', + 'HTML.Allowed' => 'h1,h2,h3,h4,h5,h6,b,u,strong,i,em,s,del,a[href|title],ul,ol,li,p[style],br,span,img[width|height|alt|src],blockquote', + 'HTML.ForbiddenElements' => '', + 'CSS.AllowedProperties' => 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align', + 'URI.AllowedSchemes' => [ + 'http' => true, + 'https' => true, + 'mailto' => true, + ], + 'AutoFormat.AutoParagraph' => false, + 'AutoFormat.RemoveEmpty' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | HTMLPurifier definitions + |-------------------------------------------------------------------------- + | + | Here you may specify a class that augments the HTML definitions used by + | HTMLPurifier. Additional HTML5 definitions are provided out of the box. + | When specifying a custom class, make sure it implements the interface: + | + | \Stevebauman\Purify\Definitions\Definition + | + | Note that these definitions are applied to every Purifier instance. + | + | Documentation: http://htmlpurifier.org/docs/enduser-customize.html + | + */ + + 'definitions' => Html5Definition::class, + + /* + |-------------------------------------------------------------------------- + | HTMLPurifier CSS definitions + |-------------------------------------------------------------------------- + | + | Here you may specify a class that augments the CSS definitions used by + | HTMLPurifier. When specifying a custom class, make sure it implements + | the interface: + | + | \Stevebauman\Purify\Definitions\CssDefinition + | + | Note that these definitions are applied to every Purifier instance. + | + | CSS should be extending $definition->info['css-attribute'] = values + | See HTMLPurifier_CSSDefinition for further explanation + | + */ + + 'css-definitions' => null, + + /* + |-------------------------------------------------------------------------- + | Serializer + |-------------------------------------------------------------------------- + | + | The storage implementation where HTMLPurifier can store its serializer files. + | If the filesystem cache is in use, the path must be writable through the + | storage disk by the web server, otherwise an exception will be thrown. + | + */ + + 'serializer' => [ + 'driver' => env('CACHE_STORE', env('CACHE_DRIVER', 'file')), + 'cache' => \Stevebauman\Purify\Cache\CacheDefinitionCache::class, + ], + + // 'serializer' => [ + // 'disk' => env('FILESYSTEM_DISK', 'local'), + // 'path' => 'purify', + // 'cache' => \Stevebauman\Purify\Cache\FilesystemDefinitionCache::class, + // ], + +]; diff --git a/package-lock.json b/package-lock.json index b07eb4401f..d8a72208d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "bootstrap4-datetimepicker": "^5.2.3", "chart.js": "^2.7.2", "codemirror": "^5.39.2", + "dompurify": "^3.4.0", "dropzone": "^5.7.2", "ekko-lightbox": "^5.3.0", "floatthead": "^2.1.2", @@ -104,23 +105,74 @@ } }, "node_modules/@asamuzakjp/css-color": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.4.tgz", - "integrity": "sha512-SeuBV4rnjpFNjI8HSgKUwteuFdkHwkboq31HWzznuqgySQir+jSTczoWVVL4jvOjKjuH80fMDG0Fvg1Sb+OJsA==", + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "peer": true, + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "dev": true, + "peer": true, + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector/node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", "dev": true, "peer": true, "dependencies": { - "@csstools/css-calc": "^2.1.3", - "@csstools/css-color-parser": "^3.0.9", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "lru-cache": "^10.4.3" + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "node_modules/@asamuzakjp/dom-selector/node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "peer": true + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "peer": true, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", "dev": true, "peer": true }, @@ -1914,6 +1966,40 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "peer": true, + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@bramus/specificity/node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "peer": true, + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/@bramus/specificity/node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "peer": true + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -1925,9 +2011,9 @@ } }, "node_modules/@csstools/color-helpers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", - "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", "dev": true, "funding": [ { @@ -1941,13 +2027,13 @@ ], "peer": true, "engines": { - "node": ">=18" + "node": ">=20.19.0" } }, "node_modules/@csstools/css-calc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.3.tgz", - "integrity": "sha512-XBG3talrhid44BY1x3MHzUx/aTG8+x/Zi57M4aTKK9RFB4aLlF3TTSzfzn8nWVHWL3FgAXAxmupmDd6VWww+pw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.0.tgz", + "integrity": "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==", "dev": true, "funding": [ { @@ -1961,17 +2047,17 @@ ], "peer": true, "engines": { - "node": ">=18" + "node": ">=20.19.0" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" } }, "node_modules/@csstools/css-color-parser": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.9.tgz", - "integrity": "sha512-wILs5Zk7BU86UArYBJTPy/FMPPKVKHMj1ycCEyf3VUptol0JNRLFU/BZsJ4aiIHJEbSLiizzRrw8Pc1uAEDrXw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.0.tgz", + "integrity": "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==", "dev": true, "funding": [ { @@ -1985,21 +2071,21 @@ ], "peer": true, "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.3" + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.2.0" }, "engines": { - "node": ">=18" + "node": ">=20.19.0" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", - "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", "dev": true, "funding": [ { @@ -2013,16 +2099,16 @@ ], "peer": true, "engines": { - "node": ">=18" + "node": ">=20.19.0" }, "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-tokenizer": "^4.0.0" } }, "node_modules/@csstools/css-tokenizer": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", - "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", "dev": true, "funding": [ { @@ -2036,7 +2122,7 @@ ], "peer": true, "engines": { - "node": ">=18" + "node": ">=20.19.0" } }, "node_modules/@discoveryjs/json-ext": { @@ -2048,6 +2134,24 @@ "node": ">=10.0.0" } }, + "node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "dev": true, + "peer": true, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -3643,9 +3747,9 @@ "dev": true }, "node_modules/@types/leaflet": { - "version": "1.9.17", - "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.17.tgz", - "integrity": "sha512-IJ4K6t7I3Fh5qXbQ1uwL3CFVbCi6haW9+53oLWgdKlLP7EaS21byWFJxxqOx9y8I0AP0actXSJLVMbyvxhkUTA==", + "version": "1.9.21", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz", + "integrity": "sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==", "peer": true, "dependencies": { "@types/geojson": "*" @@ -3783,6 +3887,12 @@ "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", "dev": true }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -5021,6 +5131,16 @@ "node": ">= 6.0.0" } }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "peer": true, + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -6492,20 +6612,20 @@ } }, "node_modules/css-loader": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", - "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.4.tgz", + "integrity": "sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==", "dev": true, "peer": true, "dependencies": { "icss-utils": "^5.1.0", - "postcss": "^8.4.33", + "postcss": "^8.4.40", "postcss-modules-extract-imports": "^3.1.0", "postcss-modules-local-by-default": "^4.0.5", "postcss-modules-scope": "^3.2.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" + "semver": "^7.6.3" }, "engines": { "node": ">= 18.12.0" @@ -6515,7 +6635,7 @@ "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "@rspack/core": "0.x || 1.x", + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", "webpack": "^5.27.0" }, "peerDependenciesMeta": { @@ -6841,9 +6961,9 @@ } }, "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "dev": true }, "node_modules/dedent": { @@ -7138,9 +7258,12 @@ } }, "node_modules/dompurify": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.8.tgz", - "integrity": "sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==" + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.1.tgz", + "integrity": "sha512-JahakDAIg1gyOm7dlgWSDjV4n7Ip2PKR55NIT6jrMfIgLFgWo81vdr1/QGqWtFNRqXP9UV71oVePtjqS2ebnPw==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } }, "node_modules/domutils": { "version": "1.7.0", @@ -12659,35 +12782,36 @@ "license": "MIT" }, "node_modules/jsdom": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", - "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "version": "29.0.2", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.2.tgz", + "integrity": "sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==", "dev": true, "peer": true, "dependencies": { - "cssstyle": "^4.2.1", - "data-urls": "^5.0.0", - "decimal.js": "^10.5.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", + "@asamuzakjp/css-color": "^5.1.5", + "@asamuzakjp/dom-selector": "^7.0.6", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.1", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.16", - "parse5": "^7.2.1", - "rrweb-cssom": "^0.8.0", + "lru-cache": "^11.2.7", + "parse5": "^8.0.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^5.1.1", + "tough-cookie": "^6.0.1", + "undici": "^7.24.5", "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.1", - "ws": "^8.18.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" }, "peerDependencies": { "canvas": "^3.0.0" @@ -12707,156 +12831,148 @@ "jsdom": ">=10.0.0" } }, - "node_modules/jsdom/node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "node_modules/jsdom/node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.3.tgz", + "integrity": "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "peer": true, - "engines": { - "node": ">= 14" + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } } }, - "node_modules/jsdom/node_modules/cssstyle": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.1.tgz", - "integrity": "sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q==", + "node_modules/jsdom/node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", "dev": true, "peer": true, "dependencies": { - "@asamuzakjp/css-color": "^3.1.2", - "rrweb-cssom": "^0.8.0" + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" }, "engines": { - "node": ">=18" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, "node_modules/jsdom/node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", "dev": true, "peer": true, "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/jsdom/node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", "dev": true, "peer": true, "dependencies": { - "whatwg-encoding": "^3.1.1" + "@exodus/bytes": "^1.6.0" }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, - "node_modules/jsdom/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "node_modules/jsdom/node_modules/lru-cache": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", "dev": true, "peer": true, - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, "engines": { - "node": ">= 14" + "node": "20 || >=22" } }, - "node_modules/jsdom/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "node_modules/jsdom/node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", "dev": true, - "peer": true, - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } + "peer": true }, "node_modules/jsdom/node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", "dev": true, "peer": true, "dependencies": { - "tldts": "^6.1.32" + "tldts": "^7.0.5" }, "engines": { "node": ">=16" } }, "node_modules/jsdom/node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", "dev": true, "peer": true, "dependencies": { "punycode": "^2.3.1" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/jsdom/node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/jsdom/node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", "dev": true, "peer": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/jsdom/node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", "dev": true, "peer": true, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/jsdom/node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", "dev": true, "peer": true, "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/jsdom/node_modules/xml-name-validator": { @@ -17185,26 +17301,26 @@ } }, "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", "dev": true, "peer": true, "dependencies": { - "entities": "^6.0.0" + "entities": "^8.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" } }, "node_modules/parse5/node_modules/entities": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", - "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", "dev": true, "peer": true, "engines": { - "node": ">=0.12" + "node": ">=20.19.0" }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" @@ -18259,6 +18375,11 @@ "quill": "^1.3.5" } }, + "node_modules/quill-paste-smart/node_modules/dompurify": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.9.tgz", + "integrity": "sha512-i6mvVmWN4xo9LrhCOZrDgSs9noW6nOahbrmzjRbPF36YPyj5Ue5lgok0MHDWkG7xzpWFO2OYttXdzM7rJxHvNA==" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -18657,13 +18778,6 @@ "inherits": "^2.0.1" } }, - "node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "peer": true - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -18890,13 +19004,10 @@ } }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -18904,24 +19015,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -19999,22 +20092,22 @@ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, "node_modules/tldts": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", - "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "version": "7.0.28", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.28.tgz", + "integrity": "sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==", "dev": true, "peer": true, "dependencies": { - "tldts-core": "^6.1.86" + "tldts-core": "^7.0.28" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", - "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "version": "7.0.28", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.28.tgz", + "integrity": "sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==", "dev": true, "peer": true }, @@ -20197,6 +20290,16 @@ "node": "*" } }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/package.json b/package.json index ecb0d41f12..54643f4b06 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "bootstrap4-datetimepicker": "^5.2.3", "chart.js": "^2.7.2", "codemirror": "^5.39.2", + "dompurify": "^3.4.0", "dropzone": "^5.7.2", "ekko-lightbox": "^5.3.0", "floatthead": "^2.1.2", diff --git a/resources/js/components/GroupActions.vue b/resources/js/components/GroupActions.vue index eabb0bdc6d..73046e2656 100644 --- a/resources/js/components/GroupActions.vue +++ b/resources/js/components/GroupActions.vue @@ -56,10 +56,10 @@ @@ -100,6 +100,11 @@ export default { } }, methods: { + escapeHtml(str) { + const el = document.createElement('span') + el.textContent = str + return el.innerHTML + }, leaveGroup() { this.$refs.confirmLeave.show() }, diff --git a/resources/js/components/GroupPage.vue b/resources/js/components/GroupPage.vue index 1fcc137cf3..e46a6ccc19 100644 --- a/resources/js/components/GroupPage.vue +++ b/resources/js/components/GroupPage.vue @@ -159,13 +159,20 @@ export default { haveLeft: false } }, + methods: { + escapeHtml(str) { + const el = document.createElement('span') + el.textContent = str + return el.innerHTML + }, + }, computed: { group() { return this.$store.getters['groups/get'](this.idgroups) }, translatedHaveLeft() { return this.$lang.get('groups.now_unfollowed', { - name: this.group.name, + name: this.escapeHtml(this.group.name), link: '/group/view/' + this.group.id }) } diff --git a/resources/js/components/ReadMore.vue b/resources/js/components/ReadMore.vue index a0111f3dff..61115143b8 100644 --- a/resources/js/components/ReadMore.vue +++ b/resources/js/components/ReadMore.vue @@ -4,10 +4,10 @@

- + - - + + @@ -20,6 +20,7 @@