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..9f8d688 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 @@ -135,18 +136,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..95e64c8 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,8 @@ "xss", "sanitizer", "purifier", - "html" + "html", + "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/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 @@ -