Skip to content

Commit

Permalink
[SECURITY] Add a name validator matching keycloak constraints (#17)
Browse files Browse the repository at this point in the history
* [SECURITY] Add a name validator matching keycloak constraints

* Add symfony/validator as depencency

* Add closing bracket to pattern and a colon to the error message

* Remove superfluous character

* Add test

* Add example assertion

* Add tests to cgl/lint configuration

* [TASK] Enable package sorting

---------

Co-authored-by: Andreas Kienast <[email protected]>
  • Loading branch information
buchmarv and andreaskienast authored Oct 4, 2024
1 parent 3ae51cf commit 9d6fd68
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ vendor
*.idea
*.lock
/.phplint-cache
/var/log/*
2 changes: 1 addition & 1 deletion .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,5 @@
])
->setFinder(
PhpCsFixer\Finder::create()
->in(['src'])
->in(['src', 'tests'])
);
4 changes: 3 additions & 1 deletion .phplint.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
path: ./src
path:
- ./src
- ./tests
jobs: 10
extensions:
- php
Expand Down
19 changes: 19 additions & 0 deletions build/phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="../tests/bootstrap.php" stopOnError="false" stopOnFailure="false" cacheDirectory=".phpunit.cache">
<coverage/>
<php>
<ini name="error_reporting" value="16383"/>
<server name="APP_ENV" value="test" force="true"/>
<server name="SHELL_VERBOSITY" value="-1"/>
</php>
<testsuites>
<testsuite name="Project Test Suite">
<directory>../tests</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory suffix=".php">../src</directory>
</include>
</source>
</phpunit>
16 changes: 14 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,23 @@
"symfony/framework-bundle": "^5.4 || ^6.4",
"symfony/security-bundle": "^5.4 || ^6.4",
"symfony/security-core": "^5.4 || ^6.4",
"symfony/security-http": "^5.4 || ^6.4"
"symfony/security-http": "^5.4 || ^6.4",
"symfony/validator": "^5.4 || ^6.4"
},
"autoload": {
"psr-4": {
"T3G\\Bundle\\Keycloak\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"T3G\\Bundle\\Keycloak\\Tests\\": "tests/"
}
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.0",
"overtrue/phplint": "^3.0 || ^4.0",
"phpunit/phpunit": "^10.5",
"roave/security-advisories": "dev-latest"
},
"scripts": {
Expand All @@ -51,14 +58,19 @@
"t3g:test:php:lint": [
"phplint"
],
"t3g:test:php": [
"./vendor/bin/phpunit -c build/phpunit.xml.dist"
],
"t3g:test": [
"@t3g:cgl",
"@t3g:test:php",
"@t3g:test:php:lint"
]
},
"config": {
"allow-plugins": {
"php-http/discovery": true
}
},
"sort-packages": true
}
}
21 changes: 21 additions & 0 deletions src/Validator/Name.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

/*
* This file is part of the package t3g/symfony-keycloak-bundle.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/

namespace T3G\Bundle\Keycloak\Validator;

use Symfony\Component\Validator\Constraint;

#[\Attribute]
class Name extends Constraint
{
public const PATTERN = '/[<=>&"[\]%!#?§;*~|()^{}\f\r\t\v\x00-\x1F\x7F]/';
public string $message = 'The value {{ value }} is not a valid name.';
}
37 changes: 37 additions & 0 deletions src/Validator/NameValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

/*
* This file is part of the package t3g/symfony-keycloak-bundle.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/

namespace T3G\Bundle\Keycloak\Validator;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class NameValidator extends ConstraintValidator
{
public function validate(mixed $value, Constraint $constraint): void
{
if (
!is_string($value)
|| '' === $value
|| !$constraint instanceof Name
) {
return;
}

$matches = [];
preg_match_all(Name::PATTERN, $value, $matches);
if (!empty($matches[0])) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $value)
->addViolation();
}
}
}
83 changes: 83 additions & 0 deletions tests/Unit/Validator/NameValidatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

/*
* This file is part of the package t3g/symfony-keycloak-bundle.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/

namespace T3G\Bundle\Keycloak\Tests\Unit\Validator;

use PHPUnit\Framework\Attributes\DataProvider;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
use T3G\Bundle\Keycloak\Validator\Name;
use T3G\Bundle\Keycloak\Validator\NameValidator;

class NameValidatorTest extends ConstraintValidatorTestCase
{
protected function createValidator(): NameValidator
{
return new NameValidator();
}

#[DataProvider('valuesDataProvider')]
public function testNameValidator(?string $value, bool $isValid): void
{
$this->validator->validate($value, new Name());
if (true === $isValid) {
$this->assertNoViolation();
} else {
$this->buildViolation('The value {{ value }} is not a valid name.')
->setParameter('{{ value }}', $value ?? 'null')
->assertRaised();
}
}

public static function valuesDataProvider(): array
{
return [
// Basic checks
'null' => [null, true],
'empty string' => ['', true],
'Valid name' => ['Dscherêmy-Pasquàlle Gucci', true],
'Valid name 2' => ['X Æ A-12', true],
'Valid name 3' => ['all\' Arrabbiata', true],
// Regex Characters
'Smaller than' => ['You < Me', false],
'Equals' => ['People = Shit', false],
'Greater than' => ['Me > You', false],
'Ampersand' => ['Simon & Garfunkel', false],
'Quote' => ['Hans-Peter "HP"', false],
'Opening square bracket' => ['[Harald', false],
'Closing square bracket' => ['Sieglinde]', false],
'Percent' => ['100% creative', false],
'Exclamation mark' => ['My name\'s not Rick!', false],
'Hash' => ['#Snoop', false],
'Question mark' => ['Am I supposed to put my name in here?', false],
'Paragraph' => ['§ 307 StGB', false],
'Semicolon' => ['return true;', false],
'Asterisk' => ['Ein *, der deinen Namen trägt.', false],
'Tilde' => ['~~~oO Andiii Oo~~~', false],
'Pipe' => ['Roddy |er', false],
'Opening bracket' => ['(Herribert', false],
'Closing bracket' => ['Gisela)', false],
'Circumflex' => ['Ich heiße Marvin ^^', false],
'Opening curly bracket' => ['{Hugo', false],
'Closing curly bracket' => ['Jackqueline}', false],
'FORM FEED' => ["Formi\f", false],
'CARRIAGE RETURN' => ["\rPing", false],
'TAB' => ["Tele\tie", false],
'Vertical whitespace' => ["Whitespacei\n\x0B\f\r\x85\u2028\u2029", false],
'Control character' => ["Shifty\x0E", false],
'DEL character' => ["Delete\x7F", false],
// General cases
'Tags' => ['<evil-html><script>alert(\'Anyone reading this is stupid.\');</script></evil-html>', false],
'Query Strings' => ['?exposeData=true&evilParameters[]=shutdown&evilParameters%5B%5D=encoded-shutdown', false],
'SQL Queries' => ['DELETE FROM users;', false],
'Example' => ['{{7*7}}\nevil.com', false],
];
}
}
12 changes: 12 additions & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

/*
* This file is part of the package t3g/symfony-keycloak-bundle.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/

require dirname(__DIR__) . '/vendor/autoload.php';

0 comments on commit 9d6fd68

Please sign in to comment.