From 2bd479a523ac5c669dca4a9371ec16905d4785ee Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Fri, 18 Oct 2024 17:43:57 -0300 Subject: [PATCH 1/3] **fix(tests): adjust sanitized value assertions to include 'value' key** - Fixed tests that failed due to direct comparison of sanitized values by adding assertions for the 'value' key in returned arrays. - Updated tests `Sanitize processes object properties`, `Sanitize handles exceptions and uses fallback value`, and `Sanitize handles multiple processors for single property` to ensure consistency in result verification. --- composer.lock | 64 +++++++-------- src/Attribute/Sanitize.php | 24 +++--- src/Sanitizer.php | 24 +++--- tests/Attribute/SanitizeTest.php | 64 +++++++-------- tests/SanitizerTest.php | 52 ++++++------- tests/application.php | 130 ++++++++++++++----------------- 6 files changed, 168 insertions(+), 190 deletions(-) diff --git a/composer.lock b/composer.lock index 55a09a1..bfe453a 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "kariricode/contract", - "version": "v2.7.5", + "version": "v2.7.8", "source": { "type": "git", "url": "https://github.com/KaririCode-Framework/kariricode-contract.git", - "reference": "253787b27e8fa170ef5e2b12e69a20557701a899" + "reference": "43d56250770789e08b8573491d3abdee2c31e9b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-contract/zipball/253787b27e8fa170ef5e2b12e69a20557701a899", - "reference": "253787b27e8fa170ef5e2b12e69a20557701a899", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-contract/zipball/43d56250770789e08b8573491d3abdee2c31e9b3", + "reference": "43d56250770789e08b8573491d3abdee2c31e9b3", "shasum": "" }, "require": { @@ -66,7 +66,7 @@ "issues": "https://github.com/KaririCode-Framework/kariricode-contract/issues", "source": "https://github.com/KaririCode-Framework/kariricode-contract" }, - "time": "2024-10-15T16:33:53+00:00" + "time": "2024-10-18T17:00:16+00:00" }, { "name": "kariricode/data-structure", @@ -144,16 +144,16 @@ }, { "name": "kariricode/processor-pipeline", - "version": "v1.1.4", + "version": "v1.1.5", "source": { "type": "git", "url": "https://github.com/KaririCode-Framework/kariricode-processor-pipeline.git", - "reference": "07c645a1c45f47ffd748e6bb8231806b465bd7bc" + "reference": "6bc3e747f254c56b5fc0cbdf22b1d3ce1497a7d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-processor-pipeline/zipball/07c645a1c45f47ffd748e6bb8231806b465bd7bc", - "reference": "07c645a1c45f47ffd748e6bb8231806b465bd7bc", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-processor-pipeline/zipball/6bc3e747f254c56b5fc0cbdf22b1d3ce1497a7d0", + "reference": "6bc3e747f254c56b5fc0cbdf22b1d3ce1497a7d0", "shasum": "" }, "require": { @@ -199,20 +199,20 @@ "issues": "https://github.com/KaririCode-Framework/kariricode-processor-pipeline/issues", "source": "https://github.com/KaririCode-Framework/kariricode-processor-pipeline" }, - "time": "2024-10-15T14:03:33+00:00" + "time": "2024-10-18T18:33:05+00:00" }, { "name": "kariricode/property-inspector", - "version": "v1.0.5", + "version": "v1.1.3", "source": { "type": "git", "url": "https://github.com/KaririCode-Framework/kariricode-property-inspector.git", - "reference": "30c6e85ac70fb3351ce16c05ba7ec0baf6ab98f0" + "reference": "3c274722ac3260382a992dd06ee9dc8f4bfaf50f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-property-inspector/zipball/30c6e85ac70fb3351ce16c05ba7ec0baf6ab98f0", - "reference": "30c6e85ac70fb3351ce16c05ba7ec0baf6ab98f0", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-property-inspector/zipball/3c274722ac3260382a992dd06ee9dc8f4bfaf50f", + "reference": "3c274722ac3260382a992dd06ee9dc8f4bfaf50f", "shasum": "" }, "require": { @@ -263,7 +263,7 @@ "issues": "https://github.com/KaririCode-Framework/kariricode-property-inspector/issues", "source": "https://github.com/KaririCode-Framework/kariricode-property-inspector" }, - "time": "2024-10-15T16:42:44+00:00" + "time": "2024-10-18T19:21:06+00:00" } ], "packages-dev": [ @@ -962,16 +962,16 @@ }, { "name": "guzzlehttp/promises", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8" + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", - "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", "shasum": "" }, "require": { @@ -1025,7 +1025,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.3" + "source": "https://github.com/guzzle/promises/tree/2.0.4" }, "funding": [ { @@ -1041,7 +1041,7 @@ "type": "tidelift" } ], - "time": "2024-07-18T10:29:17+00:00" + "time": "2024-10-17T10:06:22+00:00" }, { "name": "guzzlehttp/psr7", @@ -1397,16 +1397,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.6", + "version": "1.12.7", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "dc4d2f145a88ea7141ae698effd64d9df46527ae" + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc4d2f145a88ea7141ae698effd64d9df46527ae", - "reference": "dc4d2f145a88ea7141ae698effd64d9df46527ae", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", "shasum": "" }, "require": { @@ -1451,7 +1451,7 @@ "type": "github" } ], - "time": "2024-10-06T15:03:59+00:00" + "time": "2024-10-18T11:12:07+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2935,16 +2935,16 @@ }, { "name": "sebastian/comparator", - "version": "6.1.0", + "version": "6.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa37b9e2ca618cb051d71b60120952ee8ca8b03d" + "reference": "5ef523a49ae7a302b87b2102b72b1eda8918d686" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa37b9e2ca618cb051d71b60120952ee8ca8b03d", - "reference": "fa37b9e2ca618cb051d71b60120952ee8ca8b03d", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5ef523a49ae7a302b87b2102b72b1eda8918d686", + "reference": "5ef523a49ae7a302b87b2102b72b1eda8918d686", "shasum": "" }, "require": { @@ -3000,7 +3000,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.1.0" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.1.1" }, "funding": [ { @@ -3008,7 +3008,7 @@ "type": "github" } ], - "time": "2024-09-11T15:42:56+00:00" + "time": "2024-10-18T15:00:48+00:00" }, { "name": "sebastian/complexity", diff --git a/src/Attribute/Sanitize.php b/src/Attribute/Sanitize.php index fafee38..fb220de 100644 --- a/src/Attribute/Sanitize.php +++ b/src/Attribute/Sanitize.php @@ -1,27 +1,29 @@ processors = array_filter($processors, fn ($v) => !is_null($v) && false !== $v); + $this->messages = $messages ?? []; } public function getProcessors(): array { - return $this->sanitizers; + return $this->processors; } - public function getFallbackValue(): mixed + public function getMessage($processorName): ?string { - return $this->fallbackValue; + return $this->messages[$processorName] ?? null; } } diff --git a/src/Sanitizer.php b/src/Sanitizer.php index 6b35b63..773d8ee 100644 --- a/src/Sanitizer.php +++ b/src/Sanitizer.php @@ -1,7 +1,5 @@ propertyInspector->inspect($object, $this->attributeHandler); - $this->attributeHandler->applyChanges($object); - - return $sanitizedValues; - } catch (PropertyInspectionException $e) { - return []; - } + $this->propertyInspector->inspect($object, $this->attributeHandler); + $this->attributeHandler->applyChanges($object); + + return [ + 'sanitizedValues' => $this->attributeHandler->getProcessedValues(), + 'messages' => $this->attributeHandler->getProcessingMessages(), + 'errors' => $this->attributeHandler->getProcessingErrors(), + 'object' => $object, + ]; } } diff --git a/tests/Attribute/SanitizeTest.php b/tests/Attribute/SanitizeTest.php index 41227d1..721ba49 100644 --- a/tests/Attribute/SanitizeTest.php +++ b/tests/Attribute/SanitizeTest.php @@ -4,7 +4,8 @@ namespace KaririCode\Sanitizer\Tests\Attribute; -use KaririCode\Contract\Processor\ProcessableAttribute; +use KaririCode\Contract\Processor\Attribute\CustomizableMessageAttribute; +use KaririCode\Contract\Processor\Attribute\ProcessableAttribute; use KaririCode\Sanitizer\Attribute\Sanitize; use PHPUnit\Framework\TestCase; @@ -12,7 +13,14 @@ final class SanitizeTest extends TestCase { public function testSanitizeImplementsProcessableAttribute(): void { - $this->assertInstanceOf(ProcessableAttribute::class, new Sanitize([])); + $sanitize = new Sanitize([]); + $this->assertInstanceOf(ProcessableAttribute::class, $sanitize); + } + + public function testSanitizeImplementsCustomizableMessageAttribute(): void + { + $sanitize = new Sanitize([]); + $this->assertInstanceOf(CustomizableMessageAttribute::class, $sanitize); } public function testSanitizeIsAttribute(): void @@ -25,54 +33,42 @@ public function testSanitizeIsAttribute(): void $this->assertSame([\Attribute::TARGET_PROPERTY], $attributes[0]->getArguments()); } - public function testConstructorSetsSanitizers(): void + public function testConstructorFiltersInvalidProcessors(): void { - $sanitizers = ['trim', 'htmlspecialchars']; - $sanitize = new Sanitize($sanitizers); + $processors = ['trim', null, false, 'htmlspecialchars']; + $expectedProcessors = ['trim', 'htmlspecialchars']; + $sanitize = new Sanitize($processors); - $this->assertSame($sanitizers, $sanitize->sanitizers); + $this->assertSame(array_values($expectedProcessors), array_values($sanitize->getProcessors())); } - public function testConstructorSetsFallbackValue(): void + public function testGetProcessorsReturnsProcessors(): void { - $fallbackValue = 'default'; - $sanitize = new Sanitize([], $fallbackValue); + $processors = ['trim', 'htmlspecialchars']; + $sanitize = new Sanitize($processors); - $this->assertSame($fallbackValue, $sanitize->fallbackValue); + $this->assertSame($processors, $sanitize->getProcessors()); } - public function testConstructorSetsNullFallbackValueByDefault(): void + public function testGetMessageReturnsNullWhenNoMessagesProvided(): void { - $sanitize = new Sanitize([]); - - $this->assertNull($sanitize->fallbackValue); + $sanitize = new Sanitize(['trim']); + $this->assertNull($sanitize->getMessage('trim')); } - public function testGetProcessorsReturnsSanitizers(): void + public function testGetMessageReturnsCustomMessage(): void { - $sanitizers = ['trim', 'htmlspecialchars']; - $sanitize = new Sanitize($sanitizers); + $messages = ['trim' => 'Trim applied']; + $sanitize = new Sanitize(['trim'], $messages); - $this->assertSame($sanitizers, $sanitize->getProcessors()); + $this->assertSame('Trim applied', $sanitize->getMessage('trim')); } - public function testGetFallbackValueReturnsFallbackValue(): void + public function testGetMessageReturnsNullForUnknownProcessor(): void { - $fallbackValue = 'default'; - $sanitize = new Sanitize([], $fallbackValue); + $messages = ['trim' => 'Trim applied']; + $sanitize = new Sanitize(['trim'], $messages); - $this->assertSame($fallbackValue, $sanitize->getFallbackValue()); - } - - public function testSanitizeWithMultipleArguments(): void - { - $sanitizers = ['trim', 'htmlspecialchars']; - $fallbackValue = 'default'; - $sanitize = new Sanitize($sanitizers, $fallbackValue); - - $this->assertSame($sanitizers, $sanitize->sanitizers); - $this->assertSame($fallbackValue, $sanitize->fallbackValue); - $this->assertSame($sanitizers, $sanitize->getProcessors()); - $this->assertSame($fallbackValue, $sanitize->getFallbackValue()); + $this->assertNull($sanitize->getMessage('htmlspecialchars')); } } diff --git a/tests/SanitizerTest.php b/tests/SanitizerTest.php index f2645ed..11e3d0b 100644 --- a/tests/SanitizerTest.php +++ b/tests/SanitizerTest.php @@ -25,24 +25,24 @@ protected function setUp(): void public function testSanitizeProcessesObjectProperties(): void { $testObject = new class { - #[Sanitize(sanitizers: ['trim'])] - public string $name = ' John Doe '; + #[Sanitize(processors: ['trim'])] + public string $name = ' Walmir Silva '; - #[Sanitize(sanitizers: ['email'])] - public string $email = 'john.doe@example..com'; + #[Sanitize(processors: ['email'])] + public string $email = 'walmir.silva@example..com'; }; $trimProcessor = $this->createMock(Processor::class); $trimProcessor->expects($this->once()) ->method('process') - ->with(' John Doe ') - ->willReturn('John Doe'); + ->with(' Walmir Silva ') + ->willReturn('Walmir Silva'); $emailProcessor = $this->createMock(Processor::class); $emailProcessor->expects($this->once()) ->method('process') - ->with('john.doe@example..com') - ->willReturn('john.doe@example.com'); + ->with('walmir.silva@example..com') + ->willReturn('walmir.silva@example.com'); $this->registry->expects($this->exactly(2)) ->method('get') @@ -53,18 +53,18 @@ public function testSanitizeProcessesObjectProperties(): void $sanitizedValues = $this->sanitizer->sanitize($testObject); - $this->assertSame('John Doe', $testObject->name); - $this->assertSame('john.doe@example.com', $testObject->email); - $this->assertArrayHasKey('name', $sanitizedValues); - $this->assertArrayHasKey('email', $sanitizedValues); - $this->assertSame(['John Doe'], $sanitizedValues['name']); - $this->assertSame(['john.doe@example.com'], $sanitizedValues['email']); + $this->assertSame('Walmir Silva', $testObject->name); + $this->assertSame('walmir.silva@example.com', $testObject->email); + $this->assertArrayHasKey('name', $sanitizedValues['sanitizedValues']); + $this->assertArrayHasKey('email', $sanitizedValues['sanitizedValues']); + $this->assertSame('Walmir Silva', $sanitizedValues['sanitizedValues']['name']['value']); + $this->assertSame('walmir.silva@example.com', $sanitizedValues['sanitizedValues']['email']['value']); } public function testSanitizeHandlesNonProcessableAttributes(): void { $testObject = new class { - #[Sanitize(sanitizers: ['trim'])] + #[Sanitize(processors: ['trim'])] public string $processable = ' trim me '; public string $nonProcessable = 'leave me alone'; @@ -85,14 +85,14 @@ public function testSanitizeHandlesNonProcessableAttributes(): void $this->assertSame('trim me', $testObject->processable); $this->assertSame('leave me alone', $testObject->nonProcessable); - $this->assertArrayHasKey('processable', $sanitizedValues); - $this->assertArrayNotHasKey('nonProcessable', $sanitizedValues); + $this->assertArrayHasKey('processable', $sanitizedValues['sanitizedValues']); + $this->assertArrayNotHasKey('nonProcessable', $sanitizedValues['sanitizedValues']); } public function testSanitizeHandlesExceptionsAndUsesFallbackValue(): void { $testObject = new class { - #[Sanitize(sanitizers: ['problematic'], fallbackValue: 'fallback')] + #[Sanitize(processors: ['problematic'], messages: ['fallback' => 'Processing failed'])] public string $problematic = 'cause problem'; }; @@ -109,16 +109,16 @@ public function testSanitizeHandlesExceptionsAndUsesFallbackValue(): void $sanitizedValues = $this->sanitizer->sanitize($testObject); $this->assertSame('cause problem', $testObject->problematic); - $this->assertEmpty($sanitizedValues); + $this->assertArrayNotHasKey('problematic', $sanitizedValues['sanitizedValues']); } public function testSanitizeHandlesPrivateAndProtectedProperties(): void { $testObject = new class { - #[Sanitize(sanitizers: ['trim'])] + #[Sanitize(processors: ['trim'])] private string $privateProp = ' private '; - #[Sanitize(sanitizers: ['trim'])] + #[Sanitize(processors: ['trim'])] protected string $protectedProp = ' protected '; public function getPrivateProp(): string @@ -149,14 +149,14 @@ public function getProtectedProp(): string $this->assertSame('private', $testObject->getPrivateProp()); $this->assertSame('protected', $testObject->getProtectedProp()); - $this->assertArrayHasKey('privateProp', $sanitizedValues); - $this->assertArrayHasKey('protectedProp', $sanitizedValues); + $this->assertArrayHasKey('privateProp', $sanitizedValues['sanitizedValues']); + $this->assertArrayHasKey('protectedProp', $sanitizedValues['sanitizedValues']); } public function testSanitizeHandlesMultipleProcessorsForSingleProperty(): void { $testObject = new class { - #[Sanitize(sanitizers: ['trim', 'uppercase'])] + #[Sanitize(processors: ['trim', 'uppercase'])] public string $multiProcessed = ' hello world '; }; @@ -182,7 +182,7 @@ public function testSanitizeHandlesMultipleProcessorsForSingleProperty(): void $sanitizedValues = $this->sanitizer->sanitize($testObject); $this->assertSame('HELLO WORLD', $testObject->multiProcessed); - $this->assertArrayHasKey('multiProcessed', $sanitizedValues); - $this->assertSame(['HELLO WORLD'], $sanitizedValues['multiProcessed']); + $this->assertArrayHasKey('multiProcessed', $sanitizedValues['sanitizedValues']); + $this->assertSame('HELLO WORLD', $sanitizedValues['sanitizedValues']['multiProcessed']['value']); } } diff --git a/tests/application.php b/tests/application.php index 2232d2b..6b164a1 100644 --- a/tests/application.php +++ b/tests/application.php @@ -20,16 +20,43 @@ class UserProfile { - #[Sanitize(sanitizers: ['trim', 'html_purifier', 'xss_sanitizer', 'html_special_chars'])] + #[Sanitize( + processors: ['trim', 'html_purifier', 'xss_sanitizer', 'html_special_chars'], + messages: [ + 'trim' => 'Name was trimmed', + 'html_purifier' => 'HTML was purified in name', + 'xss_sanitizer' => 'XSS attempt was removed from name', + 'html_special_chars' => 'Special characters were escaped in name', + ] + )] private string $name = ''; - #[Sanitize(sanitizers: ['trim', 'normalize_line_breaks'])] + #[Sanitize( + processors: ['trim', 'normalize_line_breaks'], + messages: [ + 'trim' => 'Email was trimmed', + 'normalize_line_breaks' => 'Line breaks in email were normalized', + ] + )] private string $email = ''; - #[Sanitize(sanitizers: ['trim', 'strip_tags'])] + #[Sanitize( + processors: ['trim', 'strip_tags'], + messages: [ + 'trim' => 'Age was trimmed', + 'strip_tags' => 'HTML tags were removed from age', + ] + )] private string $age = ''; - #[Sanitize(sanitizers: ['trim', 'html_purifier', 'markdown'], fallbackValue: 'No bio provided')] + #[Sanitize( + processors: ['trim', 'html_purifier', 'markdown'], + messages: [ + 'trim' => 'Bio was trimmed', + 'html_purifier' => 'HTML was purified in bio', + 'markdown' => 'Markdown in bio was processed', + ] + )] private string $bio = ''; public function getName(): string @@ -73,54 +100,6 @@ public function setBio(string $bio): void } } -class UserPreferences -{ - #[Sanitize(sanitizers: ['json'])] - private string $preferences = ''; - - public function getPreferences(): string - { - return $this->preferences; - } - - public function setPreferences(string $preferences): void - { - $this->preferences = $preferences; - } -} - -class UserAvatar -{ - #[Sanitize(sanitizers: ['filename'])] - private string $avatarFilename = ''; - - public function getAvatarFilename(): string - { - return $this->avatarFilename; - } - - public function setAvatarFilename(string $avatarFilename): void - { - $this->avatarFilename = $avatarFilename; - } -} - -class UserSearch -{ - #[Sanitize(sanitizers: ['sql_injection'])] - private string $searchQuery = ''; - - public function getSearchQuery(): string - { - return $this->searchQuery; - } - - public function setSearchQuery(string $searchQuery): void - { - $this->searchQuery = $searchQuery; - } -} - $registry = new ProcessorRegistry(); $registry->register('sanitizer', 'trim', new TrimSanitizer()); $registry->register('sanitizer', 'html_special_chars', new HtmlSpecialCharsSanitizer()); @@ -142,17 +121,7 @@ public function setSearchQuery(string $searchQuery): void $userProfile->setAge(' 35 '); $userProfile->setBio("# Hello\n\n

I'm Walmir!

"); -$userPreferences = new UserPreferences(); -$userPreferences->setPreferences('{"theme": "dark", "notifications": true}'); - -$userAvatar = new UserAvatar(); -$userAvatar->setAvatarFilename('my avatar!.jpg'); - -$userSearch = new UserSearch(); -$userSearch->setSearchQuery("users'; DROP TABLE users; --"); - // Function to display original and sanitized values - function displayValues($object, $sanitizer) { echo "Original values:\n"; @@ -165,28 +134,43 @@ function displayValues($object, $sanitizer) } } - $sanitizer->sanitize($object); + $result = $sanitizer->sanitize($object); echo "\nSanitized values:\n"; foreach ($reflection->getProperties() as $property) { $propertyName = $property->getName(); $getter = 'get' . ucfirst($propertyName); if (method_exists($object, $getter)) { - echo ucfirst($propertyName) . ': "' . str_replace("\n", '\n', $object->$getter()) . "\"\n"; + echo ucfirst($propertyName) . ': "' . str_replace("\n", '\n', $result['object']->$getter()) . "\"\n"; } } + + if (!empty($result['sanitizedValues'])) { + echo "\nSanitized values details:\n"; + foreach ($result['sanitizedValues'] as $property => $data) { + echo ucfirst($property) . ":\n"; + if (!empty($data['messages'])) { + foreach ($data['messages'] as $processorName => $message) { + echo " - [$processorName] $message\n"; + } + } + echo ' Value: ' . json_encode($data['value']) . "\n"; + } + } + + if (!empty($result['errors'])) { + echo "\nErrors:\n"; + foreach ($result['errors'] as $property => $errors) { + echo ucfirst($property) . ":\n"; + foreach ($errors as $error) { + echo " - $error\n"; + } + } + } + echo "\n"; } // Display and sanitize values for each object echo "User Profile:\n"; displayValues($userProfile, $autoSanitizer); - -echo "User Preferences:\n"; -displayValues($userPreferences, $autoSanitizer); - -echo "User Avatar:\n"; -displayValues($userAvatar, $autoSanitizer); - -echo "User Search:\n"; -displayValues($userSearch, $autoSanitizer); From 36e47d93d9dbdceb07979db7dfce2f44c565e8b3 Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Fri, 18 Oct 2024 18:14:41 -0300 Subject: [PATCH 2/3] feat(sanitizer): refactor Sanitize attribute to extend BaseProcessorAttribute - Refactored the Sanitize attribute class to extend the BaseProcessorAttribute for improved code reusability and maintainability. - Removed redundant methods that are now inherited from the base class. --- composer.lock | 10 +++++----- src/Attribute/Sanitize.php | 23 ++--------------------- 2 files changed, 7 insertions(+), 26 deletions(-) diff --git a/composer.lock b/composer.lock index bfe453a..dc32722 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "kariricode/contract", - "version": "v2.7.8", + "version": "v2.7.9", "source": { "type": "git", "url": "https://github.com/KaririCode-Framework/kariricode-contract.git", - "reference": "43d56250770789e08b8573491d3abdee2c31e9b3" + "reference": "2174dd69812320a2ed88ab7ed0679eb6a42399fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-contract/zipball/43d56250770789e08b8573491d3abdee2c31e9b3", - "reference": "43d56250770789e08b8573491d3abdee2c31e9b3", + "url": "https://api.github.com/repos/KaririCode-Framework/kariricode-contract/zipball/2174dd69812320a2ed88ab7ed0679eb6a42399fb", + "reference": "2174dd69812320a2ed88ab7ed0679eb6a42399fb", "shasum": "" }, "require": { @@ -66,7 +66,7 @@ "issues": "https://github.com/KaririCode-Framework/kariricode-contract/issues", "source": "https://github.com/KaririCode-Framework/kariricode-contract" }, - "time": "2024-10-18T17:00:16+00:00" + "time": "2024-10-18T21:09:48+00:00" }, { "name": "kariricode/data-structure", diff --git a/src/Attribute/Sanitize.php b/src/Attribute/Sanitize.php index fb220de..893f175 100644 --- a/src/Attribute/Sanitize.php +++ b/src/Attribute/Sanitize.php @@ -2,28 +2,9 @@ namespace KaririCode\Sanitizer\Attribute; -use KaririCode\Contract\Processor\Attribute\CustomizableMessageAttribute; -use KaririCode\Contract\Processor\Attribute\ProcessableAttribute; +use KaririCode\Contract\Processor\Attribute\BaseProcessorAttribute; #[\Attribute(\Attribute::TARGET_PROPERTY)] -final class Sanitize implements ProcessableAttribute, CustomizableMessageAttribute +final class Sanitize extends BaseProcessorAttribute { - private readonly array $processors; - private readonly array $messages; - - public function __construct(array $processors, ?array $messages = null) - { - $this->processors = array_filter($processors, fn ($v) => !is_null($v) && false !== $v); - $this->messages = $messages ?? []; - } - - public function getProcessors(): array - { - return $this->processors; - } - - public function getMessage($processorName): ?string - { - return $this->messages[$processorName] ?? null; - } } From fc2d1f3efbe370f9bdf62603c67cfb675f04a671 Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Fri, 18 Oct 2024 18:27:02 -0300 Subject: [PATCH 3/3] feat(sanitizer): refactor Sanitize attribute to extend BaseProcessorAttribute - Refactored the Sanitize attribute class to extend the BaseProcessorAttribute for improved code reusability and maintainability. - Removed redundant methods that are now inherited from the base class. --- README.md | 217 ++++++++++++++++++++++++++++++++++---------- README.pt-br.md | 235 +++++++++++++++++++++++++++++++++++++----------- tests/post.php | 184 +++++++++++++++++++++++++++++++++++++ 3 files changed, 537 insertions(+), 99 deletions(-) create mode 100644 tests/post.php diff --git a/README.md b/README.md index 08487af..bf802b6 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,12 @@ A robust and flexible data sanitization component for PHP, part of the KaririCod - [Installation](#installation) - [Usage](#usage) - [Basic Usage](#basic-usage) - - [Advanced Usage](#advanced-usage) + - [Advanced Usage: Blog Post Sanitization](#advanced-usage-blog-post-sanitization) - [Available Sanitizers](#available-sanitizers) +- [Configuration](#configuration) - [Integration with Other KaririCode Components](#integration-with-other-kariricode-components) - [Development and Testing](#development-and-testing) +- [Contributing](#contributing) - [License](#license) - [Support and Community](#support-and-community) @@ -27,6 +29,7 @@ A robust and flexible data sanitization component for PHP, part of the KaririCod - Configurable processors for customized sanitization logic - Support for fallback values in case of sanitization failures - Extensible architecture allowing custom sanitizers +- Robust error handling and reporting ## Installation @@ -52,10 +55,10 @@ use KaririCode\Sanitizer\Attribute\Sanitize; class UserProfile { - #[Sanitize(sanitizers: ['trim', 'html_special_chars'])] + #[Sanitize(processors: ['trim', 'html_special_chars'])] private string $name = ''; - #[Sanitize(sanitizers: ['trim', 'normalize_line_breaks'])] + #[Sanitize(processors: ['trim', 'normalize_line_breaks'])] private string $email = ''; // Getters and setters... @@ -79,74 +82,184 @@ $registry->register('sanitizer', 'normalize_line_breaks', new NormalizeLineBreak $sanitizer = new Sanitizer($registry); $userProfile = new UserProfile(); -$userProfile->setName(" Walmir Silva "); -$userProfile->setEmail("walmir.silva@example.com\r\n"); +$userProfile->setName(" John Doe "); +$userProfile->setEmail("john.doe@example.com\r\n"); -$sanitizer->sanitize($userProfile); +$result = $sanitizer->sanitize($userProfile); -echo $userProfile->getName(); // Output: "Walmir Silva" -echo $userProfile->getEmail(); // Output: "walmir.silva@example.com\n" +echo $userProfile->getName(); // Output: "John Doe" +echo $userProfile->getEmail(); // Output: "john.doe@example.com\n" + +// Access sanitization results +print_r($result['sanitizedValues']); +print_r($result['messages']); +print_r($result['errors']); ``` -### Advanced Usage +### Advanced Usage: Blog Post Sanitization -You can create custom sanitizers by implementing the `Processor` or `ConfigurableProcessor` interfaces: +Here's a more comprehensive example demonstrating how to use the KaririCode Sanitizer in a real-world scenario, such as sanitizing blog post content: ```php -use KaririCode\Contract\Processor\ConfigurableProcessor; -use KaririCode\Sanitizer\Processor\AbstractSanitizerProcessor; +option = $options['custom_option'] ?? 'default'; - } - - public function process(mixed $input): string - { - $input = $this->guardAgainstNonString($input); - // Custom sanitization logic here - return $input; - } -} +declare(strict_types=1); -// Register and use the custom sanitizer -$registry->register('sanitizer', 'custom', new CustomSanitizer()); +require_once __DIR__ . '/../vendor/autoload.php'; -class AdvancedProfile +use KaririCode\ProcessorPipeline\ProcessorRegistry; +use KaririCode\Sanitizer\Attribute\Sanitize; +use KaririCode\Sanitizer\Processor\Domain\HtmlPurifierSanitizer; +use KaririCode\Sanitizer\Processor\Domain\MarkdownSanitizer; +use KaririCode\Sanitizer\Processor\Input\HtmlSpecialCharsSanitizer; +use KaririCode\Sanitizer\Processor\Input\NormalizeLineBreaksSanitizer; +use KaririCode\Sanitizer\Processor\Input\StripTagsSanitizer; +use KaririCode\Sanitizer\Processor\Input\TrimSanitizer; +use KaririCode\Sanitizer\Processor\Security\XssSanitizer; +use KaririCode\Sanitizer\Sanitizer; + +class BlogPost { - #[Sanitize(sanitizers: ['custom' => ['custom_option' => 'value']])] - private string $customField = ''; + #[Sanitize( + processors: ['trim', 'html_special_chars', 'xss_sanitizer'], + messages: [ + 'trim' => 'Title was trimmed', + 'html_special_chars' => 'Special characters in title were escaped', + 'xss_sanitizer' => 'XSS attempt was removed from title', + ] + )] + private string $title = ''; + + #[Sanitize( + processors: ['trim', 'normalize_line_breaks'], + messages: [ + 'trim' => 'Slug was trimmed', + 'normalize_line_breaks' => 'Line breaks in slug were normalized', + ] + )] + private string $slug = ''; + + #[Sanitize( + processors: ['trim', 'markdown', 'html_purifier'], + messages: [ + 'trim' => 'Content was trimmed', + 'markdown' => 'Markdown in content was processed', + 'html_purifier' => 'HTML in content was purified', + ] + )] + private string $content = ''; + + #[Sanitize( + processors: ['trim', 'strip_tags', 'html_special_chars'], + messages: [ + 'trim' => 'Author name was trimmed', + 'strip_tags' => 'HTML tags were removed from author name', + 'html_special_chars' => 'Special characters in author name were escaped', + ] + )] + private string $authorName = ''; + + // Getters and setters... } + +// Set up the sanitizer +$registry = new ProcessorRegistry(); +$registry->register('sanitizer', 'trim', new TrimSanitizer()); +$registry->register('sanitizer', 'html_special_chars', new HtmlSpecialCharsSanitizer()); +$registry->register('sanitizer', 'normalize_line_breaks', new NormalizeLineBreaksSanitizer()); +$registry->register('sanitizer', 'strip_tags', new StripTagsSanitizer()); +$registry->register('sanitizer', 'markdown', new MarkdownSanitizer()); +$registry->register('sanitizer', 'xss_sanitizer', new XssSanitizer()); + +// Configure HTML Purifier with specific settings for blog content +$htmlPurifier = new HtmlPurifierSanitizer(); +$htmlPurifier->configure([ + 'allowedTags' => ['p', 'br', 'strong', 'em', 'u', 'ol', 'ul', 'li', 'a', 'img', 'h2', 'h3', 'blockquote'], + 'allowedAttributes' => ['href' => ['a'], 'src' => ['img'], 'alt' => ['img']], +]); +$registry->register('sanitizer', 'html_purifier', $htmlPurifier); + +$sanitizer = new Sanitizer($registry); + +// Simulating form submission with potentially unsafe data +$blogPost = new BlogPost(); +$blogPost->setTitle(" Exploring KaririCode: A Modern PHP Framework "); +$blogPost->setSlug(" exploring-kariricode-a-modern-php-framework \r\n"); +$blogPost->setContent(" +# Introduction + +KaririCode is a **powerful** and _flexible_ PHP framework designed for modern web development. + + + +## Key Features + +1. Robust sanitization +2. Efficient routing +3. Powerful ORM + +Check out our [official website](https://kariricode.org) for more information! + + +"); +$blogPost->setAuthorName("John Doe "); + +$result = $sanitizer->sanitize($blogPost); + +// Access sanitized data +echo $blogPost->getTitle(); // Sanitized title +echo $blogPost->getContent(); // Sanitized content + +// Access sanitization details +print_r($result['sanitizedValues']); +print_r($result['messages']); +print_r($result['errors']); ``` +This example demonstrates how to use the KaririCode Sanitizer to clean and secure blog post data, including handling of Markdown content, HTML purification, and protection against XSS attacks. + ## Available Sanitizers The Sanitizer component provides various built-in sanitizers: ### Input Sanitizers -- TrimSanitizer -- HtmlSpecialCharsSanitizer -- NormalizeLineBreaksSanitizer -- StripTagsSanitizer +- TrimSanitizer: Removes whitespace from the beginning and end of a string +- HtmlSpecialCharsSanitizer: Converts special characters to HTML entities +- NormalizeLineBreaksSanitizer: Standardizes line breaks across different operating systems +- StripTagsSanitizer: Removes HTML and PHP tags from a string ### Domain Sanitizers -- HtmlPurifierSanitizer -- JsonSanitizer -- MarkdownSanitizer +- HtmlPurifierSanitizer: Sanitizes HTML content using the HTML Purifier library +- JsonSanitizer: Validates and prettifies JSON strings +- MarkdownSanitizer: Sanitizes Markdown content ### Security Sanitizers -- FilenameSanitizer -- SqlInjectionSanitizer -- XssSanitizer +- FilenameSanitizer: Ensures filenames are safe for use in file systems +- SqlInjectionSanitizer: Protects against SQL injection attacks +- XssSanitizer: Prevents Cross-Site Scripting (XSS) attacks + +For detailed information on each sanitizer, including configuration options and usage examples, please refer to the [documentation](https://kariricode.org/docs/sanitizer). + +## Configuration -Each sanitizer is designed to handle specific types of data and security concerns. For detailed information on each sanitizer, please refer to the [documentation](https://kariricode.org/docs/sanitizer). +The Sanitizer component can be configured globally or per-sanitizer basis. Here's an example of how to configure the `HtmlPurifierSanitizer`: + +```php +use KaririCode\Sanitizer\Processor\Domain\HtmlPurifierSanitizer; + +$htmlPurifier = new HtmlPurifierSanitizer(); +$htmlPurifier->configure([ + 'allowedTags' => ['p', 'br', 'strong', 'em'], + 'allowedAttributes' => ['href' => ['a'], 'src' => ['img']], +]); + +$registry->register('sanitizer', 'html_purifier', $htmlPurifier); +``` + +For global configuration options, refer to the `Sanitizer` class constructor. ## Integration with Other KaririCode Components @@ -229,6 +342,19 @@ For a full list of available commands, run: make help ``` +## Contributing + +We welcome contributions to the KaririCode Sanitizer component! Here's how you can contribute: + +1. Fork the repository +2. Create a new branch for your feature or bug fix +3. Write tests for your changes +4. Implement your changes +5. Run the test suite and ensure all tests pass +6. Submit a pull request with a clear description of your changes + +Please read our [Contributing Guide](CONTRIBUTING.md) for more details on our code of conduct and development process. + ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. @@ -237,7 +363,8 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file - **Documentation**: [https://kariricode.org/docs/sanitizer](https://kariricode.org/docs/sanitizer) - **Issue Tracker**: [GitHub Issues](https://github.com/KaririCode-Framework/kariricode-sanitizer/issues) -- **Community**: [KaririCode Club Community](https://kariricode.club) +- **Community Forum**: [KaririCode Club Community](https://kariricode.club) +- **Stack Overflow**: Tag your questions with `kariricode-sanitizer` --- diff --git a/README.pt-br.md b/README.pt-br.md index e176f99..b07a41c 100644 --- a/README.pt-br.md +++ b/README.pt-br.md @@ -1,4 +1,4 @@ -# Framework KaririCode: Componente Sanitizer +# Framework KaririCode: Sanitizer Component [![en](https://img.shields.io/badge/lang-en-red.svg)](README.md) [![pt-br](https://img.shields.io/badge/lang-pt--br-green.svg)](README.pt-br.md) @@ -12,10 +12,12 @@ Um componente robusto e flexível de sanitização de dados para PHP, parte do F - [Instalação](#instalação) - [Uso](#uso) - [Uso Básico](#uso-básico) - - [Uso Avançado](#uso-avançado) + - [Uso Avançado: Sanitização de Post de Blog](#uso-avançado-sanitização-de-post-de-blog) - [Sanitizadores Disponíveis](#sanitizadores-disponíveis) +- [Configuração](#configuração) - [Integração com Outros Componentes KaririCode](#integração-com-outros-componentes-kariricode) - [Desenvolvimento e Testes](#desenvolvimento-e-testes) +- [Contribuindo](#contribuindo) - [Licença](#licença) - [Suporte e Comunidade](#suporte-e-comunidade) @@ -25,8 +27,9 @@ Um componente robusto e flexível de sanitização de dados para PHP, parte do F - Conjunto abrangente de sanitizadores integrados para casos de uso comuns - Fácil integração com outros componentes KaririCode - Processadores configuráveis para lógica de sanitização personalizada -- Suporte para valores de fallback em caso de falhas na sanitização +- Suporte a valores de fallback em caso de falhas na sanitização - Arquitetura extensível permitindo sanitizadores personalizados +- Tratamento e relatório de erros robusto ## Instalação @@ -52,10 +55,10 @@ use KaririCode\Sanitizer\Attribute\Sanitize; class PerfilUsuario { - #[Sanitize(sanitizers: ['trim', 'html_special_chars'])] + #[Sanitize(processors: ['trim', 'html_special_chars'])] private string $nome = ''; - #[Sanitize(sanitizers: ['trim', 'normalize_line_breaks'])] + #[Sanitize(processors: ['trim', 'normalize_line_breaks'])] private string $email = ''; // Getters e setters... @@ -79,78 +82,188 @@ $registry->register('sanitizer', 'normalize_line_breaks', new NormalizeLineBreak $sanitizer = new Sanitizer($registry); $perfilUsuario = new PerfilUsuario(); -$perfilUsuario->setNome(" Walmir Silva "); -$perfilUsuario->setEmail("walmir.silva@exemplo.com\r\n"); +$perfilUsuario->setNome(" João Silva "); +$perfilUsuario->setEmail("joao.silva@exemplo.com\r\n"); -$sanitizer->sanitize($perfilUsuario); +$resultado = $sanitizer->sanitize($perfilUsuario); -echo $perfilUsuario->getNome(); // Saída: "Walmir Silva" -echo $perfilUsuario->getEmail(); // Saída: "walmir.silva@exemplo.com\n" +echo $perfilUsuario->getNome(); // Saída: "João Silva" +echo $perfilUsuario->getEmail(); // Saída: "joao.silva@exemplo.com\n" + +// Acesse os resultados da sanitização +print_r($resultado['sanitizedValues']); +print_r($resultado['messages']); +print_r($resultado['errors']); ``` -### Uso Avançado +### Uso Avançado: Sanitização de Post de Blog -Você pode criar sanitizadores personalizados implementando as interfaces `Processor` ou `ConfigurableProcessor`: +Aqui está um exemplo mais abrangente demonstrando como usar o Sanitizer do KaririCode em um cenário do mundo real, como sanitizar o conteúdo de um post de blog: ```php -use KaririCode\Contract\Processor\ConfigurableProcessor; -use KaririCode\Sanitizer\Processor\AbstractSanitizerProcessor; +opcao = $options['opcao_personalizada'] ?? 'padrao'; - } - - public function process(mixed $input): string - { - $input = $this->guardAgainstNonString($input); - // Lógica de sanitização personalizada aqui - return $input; - } -} +declare(strict_types=1); -// Registre e use o sanitizador personalizado -$registry->register('sanitizer', 'personalizado', new SanitizadorPersonalizado()); +require_once __DIR__ . '/../vendor/autoload.php'; -class PerfilAvancado +use KaririCode\ProcessorPipeline\ProcessorRegistry; +use KaririCode\Sanitizer\Attribute\Sanitize; +use KaririCode\Sanitizer\Processor\Domain\HtmlPurifierSanitizer; +use KaririCode\Sanitizer\Processor\Domain\MarkdownSanitizer; +use KaririCode\Sanitizer\Processor\Input\HtmlSpecialCharsSanitizer; +use KaririCode\Sanitizer\Processor\Input\NormalizeLineBreaksSanitizer; +use KaririCode\Sanitizer\Processor\Input\StripTagsSanitizer; +use KaririCode\Sanitizer\Processor\Input\TrimSanitizer; +use KaririCode\Sanitizer\Processor\Security\XssSanitizer; +use KaririCode\Sanitizer\Sanitizer; + +class PostBlog { - #[Sanitize(sanitizers: ['personalizado' => ['opcao_personalizada' => 'valor']])] - private string $campoPersonalizado = ''; + #[Sanitize( + processors: ['trim', 'html_special_chars', 'xss_sanitizer'], + messages: [ + 'trim' => 'Título foi aparado', + 'html_special_chars' => 'Caracteres especiais no título foram escapados', + 'xss_sanitizer' => 'Tentativa de XSS foi removida do título', + ] + )] + private string $titulo = ''; + + #[Sanitize( + processors: ['trim', 'normalize_line_breaks'], + messages: [ + 'trim' => 'Slug foi aparado', + 'normalize_line_breaks' => 'Quebras de linha no slug foram normalizadas', + ] + )] + private string $slug = ''; + + #[Sanitize( + processors: ['trim', 'markdown', 'html_purifier'], + messages: [ + 'trim' => 'Conteúdo foi aparado', + 'markdown' => 'Markdown no conteúdo foi processado', + 'html_purifier' => 'HTML no conteúdo foi purificado', + ] + )] + private string $conteudo = ''; + + #[Sanitize( + processors: ['trim', 'strip_tags', 'html_special_chars'], + messages: [ + 'trim' => 'Nome do autor foi aparado', + 'strip_tags' => 'Tags HTML foram removidas do nome do autor', + 'html_special_chars' => 'Caracteres especiais no nome do autor foram escapados', + ] + )] + private string $nomeAutor = ''; + + // Getters e setters... } + +// Configurar o sanitizador +$registry = new ProcessorRegistry(); +$registry->register('sanitizer', 'trim', new TrimSanitizer()); +$registry->register('sanitizer', 'html_special_chars', new HtmlSpecialCharsSanitizer()); +$registry->register('sanitizer', 'normalize_line_breaks', new NormalizeLineBreaksSanitizer()); +$registry->register('sanitizer', 'strip_tags', new StripTagsSanitizer()); +$registry->register('sanitizer', 'markdown', new MarkdownSanitizer()); +$registry->register('sanitizer', 'xss_sanitizer', new XssSanitizer()); + +// Configurar HTML Purifier com configurações específicas para conteúdo de blog +$htmlPurifier = new HtmlPurifierSanitizer(); +$htmlPurifier->configure([ + 'allowedTags' => ['p', 'br', 'strong', 'em', 'u', 'ol', 'ul', 'li', 'a', 'img', 'h2', 'h3', 'blockquote'], + 'allowedAttributes' => ['href' => ['a'], 'src' => ['img'], 'alt' => ['img']], +]); +$registry->register('sanitizer', 'html_purifier', $htmlPurifier); + +$sanitizer = new Sanitizer($registry); + +// Simulando submissão de formulário com dados potencialmente inseguros +$postBlog = new PostBlog(); +$postBlog->setTitulo(" Explorando KaririCode: Um Framework PHP Moderno "); +$postBlog->setSlug(" explorando-kariricode-um-framework-php-moderno \r\n"); +$postBlog->setConteudo(" +# Introdução + +KaririCode é um framework PHP **poderoso** e _flexível_ projetado para desenvolvimento web moderno. + + + +## Características Principais + +1. Sanitização robusta +2. Roteamento eficiente +3. ORM poderoso + +Confira nosso [site oficial](https://kariricode.org) para mais informações! + + +"); +$postBlog->setNomeAutor("João Silva "); + +$resultado = $sanitizer->sanitize($postBlog); + +// Acessar dados sanitizados +echo $postBlog->getTitulo(); // Título sanitizado +echo $postBlog->getConteudo(); // Conteúdo sanitizado + +// Acessar detalhes da sanitização +print_r($resultado['sanitizedValues']); +print_r($resultado['messages']); +print_r($resultado['errors']); ``` +Este exemplo demonstra como usar o Sanitizer do KaririCode para limpar e proteger dados de posts de blog, incluindo o tratamento de conteúdo Markdown, purificação de HTML e proteção contra ataques XSS. + ## Sanitizadores Disponíveis O componente Sanitizer fornece vários sanitizadores integrados: ### Sanitizadores de Entrada -- TrimSanitizer -- HtmlSpecialCharsSanitizer -- NormalizeLineBreaksSanitizer -- StripTagsSanitizer +- TrimSanitizer: Remove espaços em branco do início e fim de uma string +- HtmlSpecialCharsSanitizer: Converte caracteres especiais em entidades HTML +- NormalizeLineBreaksSanitizer: Padroniza quebras de linha entre diferentes sistemas operacionais +- StripTagsSanitizer: Remove tags HTML e PHP de uma string ### Sanitizadores de Domínio -- HtmlPurifierSanitizer -- JsonSanitizer -- MarkdownSanitizer +- HtmlPurifierSanitizer: Sanitiza conteúdo HTML usando a biblioteca HTML Purifier +- JsonSanitizer: Valida e formata strings JSON +- MarkdownSanitizer: Sanitiza conteúdo Markdown ### Sanitizadores de Segurança -- FilenameSanitizer -- SqlInjectionSanitizer -- XssSanitizer +- FilenameSanitizer: Garante que nomes de arquivos sejam seguros para uso em sistemas de arquivos +- SqlInjectionSanitizer: Protege contra ataques de injeção SQL +- XssSanitizer: Previne ataques de Cross-Site Scripting (XSS) + +Para informações detalhadas sobre cada sanitizador, incluindo opções de configuração e exemplos de uso, consulte a [documentação](https://kariricode.org/docs/sanitizer). + +## Configuração -Cada sanitizador é projetado para lidar com tipos específicos de dados e preocupações de segurança. Para informações detalhadas sobre cada sanitizador, consulte a [documentação](https://kariricode.org/docs/sanitizer). +O componente Sanitizer pode ser configurado globalmente ou por sanitizador. Aqui está um exemplo de como configurar o `HtmlPurifierSanitizer`: + +```php +use KaririCode\Sanitizer\Processor\Domain\HtmlPurifierSanitizer; + +$htmlPurifier = new HtmlPurifierSanitizer(); +$htmlPurifier->configure([ + 'allowedTags' => ['p', 'br', 'strong', 'em'], + 'allowedAttributes' => ['href' => ['a'], 'src' => ['img']], +]); + +$registry->register('sanitizer', 'html_purifier', $htmlPurifier); +``` + +Para opções de configuração global, consulte o construtor da classe `Sanitizer`. ## Integração com Outros Componentes KaririCode -O componente Sanitizer foi projetado para trabalhar perfeitamente com outros componentes KaririCode: +O componente Sanitizer é projetado para funcionar perfeitamente com outros componentes KaririCode: - **KaririCode\Contract**: Fornece interfaces e contratos para integração consistente de componentes. - **KaririCode\ProcessorPipeline**: Utilizado para construir e executar pipelines de sanitização. @@ -167,7 +280,7 @@ use KaririCode\PropertyInspector\Utility\PropertyInspector; use KaririCode\Sanitizer\Sanitizer; $registry = new ProcessorRegistry(); -// Registre os sanitizadores... +// Registrar sanitizadores... $builder = new ProcessorBuilder($registry); $attributeHandler = new AttributeHandler('sanitizer', $builder); @@ -178,7 +291,7 @@ $sanitizer = new Sanitizer($registry); ## Desenvolvimento e Testes -Para fins de desenvolvimento e teste, este pacote usa Docker e Docker Compose para garantir consistência em diferentes ambientes. Um Makefile é fornecido para conveniência. +Para fins de desenvolvimento e testes, este pacote usa Docker e Docker Compose para garantir consistência em diferentes ambientes. Um Makefile é fornecido para conveniência. ### Pré-requisitos @@ -201,7 +314,7 @@ Para fins de desenvolvimento e teste, este pacote usa Docker e Docker Compose pa make setup-env ``` -3. Inicie os contêineres Docker: +3. Inicie os containers Docker: ```bash make up @@ -215,12 +328,12 @@ Para fins de desenvolvimento e teste, este pacote usa Docker e Docker Compose pa ### Comandos Make Disponíveis - `make up`: Inicia todos os serviços em segundo plano -- `make down`: Para e remove todos os contêineres +- `make down`: Para e remove todos os containers - `make build`: Constrói imagens Docker -- `make shell`: Acessa o shell do contêiner PHP +- `make shell`: Acessa o shell do container PHP - `make test`: Executa testes - `make coverage`: Executa cobertura de testes com formatação visual -- `make cs-fix`: Executa PHP CS Fixer para corrigir o estilo do código +- `make cs-fix`: Executa PHP CS Fixer para corrigir o estilo de código - `make quality`: Executa todos os comandos de qualidade (cs-check, test, security-check) Para uma lista completa de comandos disponíveis, execute: @@ -229,6 +342,19 @@ Para uma lista completa de comandos disponíveis, execute: make help ``` +## Contribuindo + +Agradecemos contribuições para o componente Sanitizer do KaririCode! Aqui está como você pode contribuir: + +1. Faça um fork do repositório +2. Crie um novo branch para sua feature ou correção de bug +3. Escreva testes para suas alterações +4. Implemente suas alterações +5. Execute a suite de testes e garanta que todos os testes passem +6. Envie um pull request com uma descrição clara de suas alterações + +Por favor, leia nosso [Guia de Contribuição](CONTRIBUTING.md) para mais detalhes sobre nosso código de conduta e processo de desenvolvimento. + ## Licença Este projeto está licenciado sob a Licença MIT - veja o arquivo [LICENSE](LICENSE) para detalhes. @@ -237,7 +363,8 @@ Este projeto está licenciado sob a Licença MIT - veja o arquivo [LICENSE](LICE - **Documentação**: [https://kariricode.org/docs/sanitizer](https://kariricode.org/docs/sanitizer) - **Rastreador de Problemas**: [GitHub Issues](https://github.com/KaririCode-Framework/kariricode-sanitizer/issues) -- **Comunidade**: [Comunidade KaririCode Club](https://kariricode.club) +- **Fórum da Comunidade**: [Comunidade KaririCode Club](https://kariricode.club) +- **Stack Overflow**: Marque suas perguntas com `kariricode-sanitizer` --- diff --git a/tests/post.php b/tests/post.php new file mode 100644 index 0000000..68480ff --- /dev/null +++ b/tests/post.php @@ -0,0 +1,184 @@ + 'Title was trimmed', + 'html_special_chars' => 'Special characters in title were escaped', + 'xss_sanitizer' => 'XSS attempt was removed from title', + ] + )] + private string $title = ''; + + #[Sanitize( + processors: ['trim', 'normalize_line_breaks'], + messages: [ + 'trim' => 'Slug was trimmed', + 'normalize_line_breaks' => 'Line breaks in slug were normalized', + ] + )] + private string $slug = ''; + + #[Sanitize( + processors: ['trim', 'markdown', 'html_purifier'], + messages: [ + 'trim' => 'Content was trimmed', + 'markdown' => 'Markdown in content was processed', + 'html_purifier' => 'HTML in content was purified', + ] + )] + private string $content = ''; + + #[Sanitize( + processors: ['trim', 'strip_tags', 'html_special_chars'], + messages: [ + 'trim' => 'Author name was trimmed', + 'strip_tags' => 'HTML tags were removed from author name', + 'html_special_chars' => 'Special characters in author name were escaped', + ] + )] + private string $authorName = ''; + + // Getters and setters... + + public function getTitle(): string + { + return $this->title; + } + + public function setTitle(string $title): void + { + $this->title = $title; + } + + public function getSlug(): string + { + return $this->slug; + } + + public function setSlug(string $slug): void + { + $this->slug = $slug; + } + + public function getContent(): string + { + return $this->content; + } + + public function setContent(string $content): void + { + $this->content = $content; + } + + public function getAuthorName(): string + { + return $this->authorName; + } + + public function setAuthorName(string $authorName): void + { + $this->authorName = $authorName; + } +} + +// Set up the sanitizer +$registry = new ProcessorRegistry(); +$registry->register('sanitizer', 'trim', new TrimSanitizer()); +$registry->register('sanitizer', 'html_special_chars', new HtmlSpecialCharsSanitizer()); +$registry->register('sanitizer', 'normalize_line_breaks', new NormalizeLineBreaksSanitizer()); +$registry->register('sanitizer', 'strip_tags', new StripTagsSanitizer()); +$registry->register('sanitizer', 'markdown', new MarkdownSanitizer()); +$registry->register('sanitizer', 'xss_sanitizer', new XssSanitizer()); + +// Configure HTML Purifier with specific settings for blog content +$htmlPurifier = new HtmlPurifierSanitizer(); +$htmlPurifier->configure([ + 'allowedTags' => ['p', 'br', 'strong', 'em', 'u', 'ol', 'ul', 'li', 'a', 'img', 'h2', 'h3', 'blockquote'], + 'allowedAttributes' => ['href' => ['a'], 'src' => ['img'], 'alt' => ['img']], +]); +$registry->register('sanitizer', 'html_purifier', $htmlPurifier); + +$sanitizer = new Sanitizer($registry); + +// Simulating form submission with potentially unsafe data +$blogPost = new BlogPost(); +$blogPost->setTitle(" Exploring KaririCode: A Modern PHP Framework "); +$blogPost->setSlug(" exploring-kariricode-a-modern-php-framework \r\n"); +$blogPost->setContent(" +# Introduction + +KaririCode is a **powerful** and _flexible_ PHP framework designed for modern web development. + + + +## Key Features + +1. Robust sanitization +2. Efficient routing +3. Powerful ORM + +Check out our [official website](https://kariricode.org) for more information! + + +"); + +$blogPost->setAuthorName("John Doe "); + +// Function to display original and sanitized values +function displayBlogPost(BlogPost $post, Sanitizer $sanitizer) +{ + echo "Original Blog Post:\n"; + echo "Title: \"{$post->getTitle()}\"\n"; + echo "Slug: \"{$post->getSlug()}\"\n"; + echo "Content: \"{$post->getContent()}\"\n"; + echo "Author: \"{$post->getAuthorName()}\"\n\n"; + + $result = $sanitizer->sanitize($post); + + echo "Sanitized Blog Post:\n"; + echo "Title: \"{$result['object']->getTitle()}\"\n"; + echo "Slug: \"{$result['object']->getSlug()}\"\n"; + echo "Content: \"{$result['object']->getContent()}\"\n"; + echo "Author: \"{$result['object']->getAuthorName()}\"\n\n"; + + if (!empty($result['sanitizedValues'])) { + echo "Sanitization Details:\n"; + foreach ($result['sanitizedValues'] as $property => $data) { + echo ucfirst($property) . ":\n"; + foreach ($data['messages'] as $processorName => $message) { + echo " - [$processorName] $message\n"; + } + } + } + + if (!empty($result['errors'])) { + echo "\nErrors:\n"; + foreach ($result['errors'] as $property => $errors) { + echo ucfirst($property) . ":\n"; + foreach ($errors as $error) { + echo " - $error\n"; + } + } + } +} + +// Display and sanitize the blog post +displayBlogPost($blogPost, $sanitizer);