From 09b0411be9c2c82476560fb303b6cbf41098dc49 Mon Sep 17 00:00:00 2001 From: medilies Date: Sun, 11 Aug 2024 21:33:41 +0100 Subject: [PATCH] cleanup --- CONTRIBUTING.md | 13 ++++ README.md | 94 +++++++++++------------- composer.json | 7 +- src/Dompurify/DompurifyService.php | 2 - src/Dompurify/DompurifyServiceConfig.php | 4 +- src/Xssless.php | 16 ++-- src/laravel/Commands/SetupCommand.php | 1 + src/laravel/Commands/StartCommand.php | 1 + src/laravel/Facades/Xssless.php | 1 + tests/Dompurify/DompurifyCliTest.php | 21 +----- tests/Dompurify/DompurifyServiceTest.php | 29 ++------ tests/Pest.php | 5 +- tests/TestCase.php | 10 --- 13 files changed, 84 insertions(+), 120 deletions(-) create mode 100644 CONTRIBUTING.md delete mode 100644 tests/TestCase.php diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6a29319 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# CONTRIBUTING + +## Testing + +```bash +./vendor/bin/pest +``` + +## Formatting + +```bash +./vendor/bin/pint +``` diff --git a/README.md b/README.md index 79feb5b..2d925ef 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,14 @@ ## Why use Xssless -- **Prevent XSS Attacks:** Safeguard your application by cleaning user-submitted HTML, eliminating potential XSS threats. -- **Painless HTML 5 support:** Xssless leverages engines with the best HTML5 support. -- **Easy to build policies:** (todo). +- Your application features a [Rich Text Editor](https://en.wikipedia.org/wiki/Online_rich-text_editor) and you want to prevent all XSS. +- You want full HTML5 & CSS3 support. +- You want to allow all safe HTML elements, their attributes, and CSS properties without going deep into whitelist configs. +- [TODO] You want a fluent and an intuitive way to build policies. The default driver aligns with [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#html-sanitization) recommendations: -> HTML Sanitization will strip dangerous HTML from a variable and return a safe string of HTML. OWASP recommends **DOMPurify** for HTML Sanitization. +> ... OWASP recommends **DOMPurify** for HTML Sanitization. ## Requirements @@ -32,7 +33,7 @@ Install the package via composer: composer require medilies/xssless ``` -For non Laravel projects pick a config and run the following code: +For non Laravel projects, pick a config and run the following code: ```php $config = new Medilies\Xssless\Dompurify\DompurifyCliConfig('node', 'npm'); @@ -42,44 +43,15 @@ $config = new Medilies\Xssless\Dompurify\DompurifyCliConfig('node', 'npm'); ->setup(); ``` -### Laravel setup - -You can publish the config file with: - -```bash -php artisan vendor:publish --tag="xssless-config" -``` - -This is the contents of the published config file: - -```php -return [ - 'default' => 'dompurify-cli', - - 'drivers' => [ - 'dompurify-cli' => new DompurifyCliConfig( - node: env('NODE_PATH'), - npm: env('NPM_PATH'), - binary: null, - tempFolder: null, - ), - 'dompurify-service' => new DompurifyServiceConfig( - node: env('NODE_PATH'), - npm: env('NPM_PATH'), - host: '127.0.0.1', - port: 63000, - binary: null, - ), - ], -]; -``` - -Run the following command after picking your `xssless.default` config: +For non Laravel projects, run the following command: ```shell php artisan xssless:setup ``` +> [!IMPORTANT] +> You may need to re-run the setup when switching drivers. + ## Usage Using `Medilies\Xssless\Dompurify\DompurifyCliConfig`: @@ -111,13 +83,43 @@ $xssless->clean($html); ### Laravel usage -Using `Medilies\Xssless\Dompurify\DompurifyCliConfig`: +You can publish the config file with: + +```bash +php artisan vendor:publish --tag="xssless-config" +``` + +This is the contents of the published config file: + +```php +return [ + 'default' => 'dompurify-cli', + + 'drivers' => [ + 'dompurify-cli' => new DompurifyCliConfig( + node: env('NODE_PATH'), + npm: env('NPM_PATH'), + binary: null, + tempFolder: null, + ), + 'dompurify-service' => new DompurifyServiceConfig( + node: env('NODE_PATH'), + npm: env('NPM_PATH'), + host: '127.0.0.1', + port: 63000, + binary: null, + ), + ], +]; +``` + +Using `dompurify-cli`: ```php Medilies\Xssless\Laravel\Facades\Xssless::clean($html); ``` -Using `Medilies\Xssless\Dompurify\DompurifyServiceConfig`: +Using `dompurify-service`: ```shell php artisan xssless:start @@ -135,18 +137,6 @@ Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed re Please see [CONTRIBUTING](CONTRIBUTING.md) for details. -### Testing - -```bash -./vendor/bin/pest -``` - -### Formatting - -```bash -./vendor/bin/pint -``` - ## Security Vulnerabilities Please review [our security policy](../../security/policy) on how to report security vulnerabilities. diff --git a/composer.json b/composer.json index 138b879..187608b 100644 --- a/composer.json +++ b/composer.json @@ -2,10 +2,15 @@ "name": "medilies/xssless", "description": "Clean your strings from XSS threats.", "keywords": [ + "html", "xss", + "safe", + "prevent", "sanitizer", "purifier", - "html" + "cleaner", + "filter", + "laravel" ], "homepage": "https://github.com/medilies/xssless", "license": "MIT", diff --git a/src/Dompurify/DompurifyService.php b/src/Dompurify/DompurifyService.php index eef8783..caf04e6 100644 --- a/src/Dompurify/DompurifyService.php +++ b/src/Dompurify/DompurifyService.php @@ -145,6 +145,4 @@ private function isSigTerm(): bool // { // return $this->serviceProcess->getTermSignal() === 1; // } - - // 92..97, 104, 113..136 } diff --git a/src/Dompurify/DompurifyServiceConfig.php b/src/Dompurify/DompurifyServiceConfig.php index e3bf332..c953ffa 100644 --- a/src/Dompurify/DompurifyServiceConfig.php +++ b/src/Dompurify/DompurifyServiceConfig.php @@ -6,13 +6,13 @@ class DompurifyServiceConfig implements ConfigInterface { - public readonly string $class; + private readonly string $class; public function __construct( public string $node = 'node', public string $npm = 'npm', public string $host = '127.0.0.1', - public int $port = 6300, + public int $port = 63000, public ?string $binary = null, ) { $this->class = DompurifyService::class; diff --git a/src/Xssless.php b/src/Xssless.php index 01cb8ed..1b6d0bb 100755 --- a/src/Xssless.php +++ b/src/Xssless.php @@ -8,9 +8,9 @@ class Xssless // TODO: policy builder - public function clean(string $html, ?ConfigInterface $config = null): string + public function clean(string $html, ?ConfigInterface $tempConfig = null): string { - $cleaner = $this->makeCleaner($config); + $cleaner = $this->makeCleaner($tempConfig); return match (true) { $cleaner instanceof CliInterface => $this->exec($cleaner, $html), @@ -18,9 +18,9 @@ public function clean(string $html, ?ConfigInterface $config = null): string }; } - public function start(?ConfigInterface $config = null): ServiceInterface + public function start(?ConfigInterface $tempConfig = null): ServiceInterface { - $service = $this->makeCleaner($config); + $service = $this->makeCleaner($tempConfig); if (! $service instanceof ServiceInterface) { throw new XsslessException("'".$service::class."' must implement: '".ServiceInterface::class."'."); @@ -29,9 +29,9 @@ public function start(?ConfigInterface $config = null): ServiceInterface return $service->start(); } - public function setup(?ConfigInterface $config = null): void + public function setup(?ConfigInterface $tempConfig = null): void { - $service = $this->makeCleaner($config); + $service = $this->makeCleaner($tempConfig); if (! $service instanceof HasSetupInterface) { throw new XsslessException("'".$service::class."' must implement: '".HasSetupInterface::class."'."); @@ -66,9 +66,9 @@ public function using(ConfigInterface $config): static return $this; } - private function makeCleaner(?ConfigInterface $config = null): CliInterface|ServiceInterface + private function makeCleaner(?ConfigInterface $tempConfig = null): CliInterface|ServiceInterface { - $config ??= $this->config ?? null; + $config = $tempConfig ?? $this->config ?? null; if (is_null($config)) { throw new XsslessException('A config must be provided.'); diff --git a/src/laravel/Commands/SetupCommand.php b/src/laravel/Commands/SetupCommand.php index 682d911..9e78887 100644 --- a/src/laravel/Commands/SetupCommand.php +++ b/src/laravel/Commands/SetupCommand.php @@ -15,6 +15,7 @@ class SetupCommand extends Command public function handle(): void { + // TODO: non Laravel command Xssless::usingLaravelConfig()->setup(); } } diff --git a/src/laravel/Commands/StartCommand.php b/src/laravel/Commands/StartCommand.php index f59306f..8eb88ec 100644 --- a/src/laravel/Commands/StartCommand.php +++ b/src/laravel/Commands/StartCommand.php @@ -15,6 +15,7 @@ class StartCommand extends Command public function handle(): void { + // TODO: non Laravel command $service = Xssless::usingLaravelConfig()->start(); $terminate = function ($signal) use ($service) { diff --git a/src/laravel/Facades/Xssless.php b/src/laravel/Facades/Xssless.php index b1feb20..6e0aa7f 100644 --- a/src/laravel/Facades/Xssless.php +++ b/src/laravel/Facades/Xssless.php @@ -18,6 +18,7 @@ class Xssless extends Facade { protected static function getFacadeAccessor(): string { + // TODO: model cast return \Medilies\Xssless\Xssless::class; } } diff --git a/tests/Dompurify/DompurifyCliTest.php b/tests/Dompurify/DompurifyCliTest.php index 46cedd1..84336ff 100644 --- a/tests/Dompurify/DompurifyCliTest.php +++ b/tests/Dompurify/DompurifyCliTest.php @@ -8,27 +8,20 @@ it('throws on bad node path', function () { $cleaner = (new DompurifyCli)->configure(new DompurifyCliConfig( - 'nodeZz', - 'npm', + node: 'nodeZz', )); expect(fn () => $cleaner->exec('foo'))->toThrow(ProcessFailedException::class); }); test('setup()', function () { - $cleaner = (new Xssless)->using(new DompurifyCliConfig( - 'node', - 'npm', - )); + $cleaner = (new Xssless)->using(new DompurifyCliConfig); expect(fn () => $cleaner->setup())->not->toThrow(Exception::class); }); test('exec()', function () { - $cleaner = (new DompurifyCli)->configure(new DompurifyCliConfig( - 'node', - 'npm', - )); + $cleaner = (new DompurifyCli)->configure(new DompurifyCliConfig); $clean = $cleaner->exec('">'); @@ -37,8 +30,6 @@ test('clean()', function () { $cleaner = (new Xssless)->using(new DompurifyCliConfig( - node: 'node', - npm: 'npm', tempFolder: __DIR__, )); @@ -49,8 +40,6 @@ it('throws when cannot read cleaned file', function () { $cleaner = (new DompurifyCli)->configure(new DompurifyCliConfig( - node: 'node', - npm: 'npm', binary: __DIR__.'/js-mocks/cli-returns-bad-path.js', )); @@ -59,8 +48,6 @@ it('throws when cannot find binary file', function () { $cleaner = (new DompurifyCli)->configure(new DompurifyCliConfig( - node: 'node', - npm: 'npm', binary: __DIR__.'/js-mocks/x.js', )); @@ -69,8 +56,6 @@ it('throws when cannot locate temp folder', function () { $cleaner = (new DompurifyCli)->configure(new DompurifyCliConfig( - node: 'node', - npm: 'npm', tempFolder: __DIR__.'/x', )); diff --git a/tests/Dompurify/DompurifyServiceTest.php b/tests/Dompurify/DompurifyServiceTest.php index 1bc2751..5422165 100644 --- a/tests/Dompurify/DompurifyServiceTest.php +++ b/tests/Dompurify/DompurifyServiceTest.php @@ -7,23 +7,13 @@ use Symfony\Component\Process\Exception\ProcessFailedException; test('setup()', function () { - $cleaner = (new DompurifyService)->configure(new DompurifyServiceConfig( - 'node', - 'npm', - '127.0.0.1', - 63000, - )); + $cleaner = (new DompurifyService)->configure(new DompurifyServiceConfig); expect(fn () => $cleaner->setup())->not->toThrow(Exception::class); }); test('send()', function () { - $cleaner = (new DompurifyService)->configure(new DompurifyServiceConfig( - 'node', - 'npm', - '127.0.0.1', - 63000, - )); + $cleaner = (new DompurifyService)->configure(new DompurifyServiceConfig); $cleaner->start(); @@ -38,10 +28,7 @@ test('clean()', function () { $config = new DompurifyServiceConfig( - 'node', - 'npm', - '127.0.0.1', - 63001, + port: 63001, // for parallel tests ); $cleaner = (new Xssless)->using($config); @@ -59,10 +46,7 @@ it('throws on bad host', function () { $cleaner = (new DompurifyService)->configure(new DompurifyServiceConfig( - 'node', - 'npm', - 'a.b.c.example.com', - 63000, + host: 'a.b.c.example.com', )); $dirty = '">'; @@ -72,10 +56,7 @@ it('throws on bad node path', function () { $service = (new DompurifyService)->configure(new DompurifyServiceConfig( - 'nodeZz', - 'npm', - '127.0.0.1', - 5555555555, + node: 'nodeZz', )); expect(fn () => $service->start())->toThrow(ProcessFailedException::class); diff --git a/tests/Pest.php b/tests/Pest.php index f3d76d1..1a8db6d 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,6 +1,5 @@ in(__DIR__); +uses(TestCase::class)->in(__DIR__); diff --git a/tests/TestCase.php b/tests/TestCase.php deleted file mode 100644 index cfb05b6..0000000 --- a/tests/TestCase.php +++ /dev/null @@ -1,10 +0,0 @@ -