Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add escURL and escCSS #117

Merged
merged 1 commit into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ Security::escHTML('string');

Security::escJS('string');

Security::escURL('string');

Security::escCSS('string');

Security::isSupportedCharset('string');
```

Expand Down Expand Up @@ -53,6 +57,8 @@ Charsets shortlisted:
* escHTML(text: mixed, [charset: string = 'UTF-8']): string
* escAttr(text: mixed, [charset: string = 'UTF-8']): string
* escJS(text: mixed, [charset: string = 'UTF-8']): string
* escURL(text: mixed, [charset: string = 'UTF-8']): string
* escCSS(text: mixed, [charset: string = 'UTF-8']): string

## How to Dev
`composer ci` for php-cs-fixer and phpunit and coverage
Expand Down
45 changes: 45 additions & 0 deletions src/Security.php
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,49 @@ public static function escJS($text, string $charset = 'UTF-8'): string

return static::convertStringFromUTF8($text, $charset);
}

/**
* @param mixed $text
* @param string $charset
*
* @throws SecurityException
*
* @return string
*/
public static function escURL($text, string $charset = 'UTF-8'): string
{
$text = static::convertStringToUTF8($text, $charset);

$text = \rawurlencode($text);

return static::convertStringFromUTF8($text, $charset);
}

/**
* @param mixed $text
* @param string $charset
*
* @throws SecurityException
*
* @return string
*/
public static function escCSS($text, string $charset = 'UTF-8'): string
{
$text = static::convertStringToUTF8($text, $charset);

$text = \preg_replace_callback('/[^a-z0-9]/iSu', static function ($matches) {
$chr = $matches[0];

if (\strlen($chr) === 1) {
$ord = \ord($chr);
} else {
$chr = \mb_convert_encoding($chr, 'UTF-32BE', 'UTF-8');
$ord = \hexdec(\bin2hex($chr));
}

return \sprintf('\\%X ', $ord);
}, $text);

return static::convertStringFromUTF8($text, $charset);
}
}
117 changes: 115 additions & 2 deletions tests/SecurityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,65 @@ public function dataJS(): array
];
}

public function dataURL(): array
{
return [
'<' => ['<', '%3C'],
'>' => ['>', '%3E'],
'\'' => ['\'', '%27'],
'"' => ['"', '%22'],
'&' => ['&', '%26'],
'Ā' => ['Ā', '%C4%80'],
',' => [',', '%2C'],
'.' => ['.', '.'],
'_' => ['_', '_'],
'-' => ['-', '-'],
':' => [':', '%3A'],
';' => [';', '%3B'],
'!' => ['!', '%21'],
'a' => ['a', 'a'],
'A' => ['A', 'A'],
'z' => ['z', 'z'],
'Z' => ['Z', 'Z'],
'0' => ['0', '0'],
'9' => ['9', '9'],
"\r" => ["\r", '%0D'],
"\n" => ["\n", '%0A'],
"\t" => ["\t", '%09'],
"\0" => ["\0", '%00'],
' ' => [' ', '%20'],
'~' => ['~', '~'],
'+' => ['+', '%2B']
];
}

public function dataCSS(): array
{
return [
'<' => ['<', '\\3C '],
'>' => ['>', '\\3E '],
'\'' => ['\'', '\\27 '],
'"' => ['"', '\\22 '],
'&' => ['&', '\\26 '],
'Ā' => ['Ā', '\\100 '],
"\xF0\x90\x80\x80" => ["\xF0\x90\x80\x80", '\\10000 '],
',' => [',', '\\2C '],
'.' => ['.', '\\2E '],
'_' => ['_', '\\5F '],
'a' => ['a', 'a'],
'A' => ['A', 'A'],
'z' => ['z', 'z'],
'Z' => ['Z', 'Z'],
'0' => ['0', '0'],
'9' => ['9', '9'],
"\r" => ["\r", '\\D '],
"\n" => ["\n", '\\A '],
"\t" => ["\t", '\\9 '],
"\0" => ["\0", '\\0 '],
' ' => [' ', '\\20 '],
];
}

/**
* @dataProvider dataHTML
*
Expand Down Expand Up @@ -182,6 +241,32 @@ public function testEscJS(string $input, string $expected): void
self::assertSame($expected, Security::escJS($input));
}

/**
* @dataProvider dataURL
*
* @param string $input
* @param string $expected
*
* @throws SecurityException
*/
public function testEscURL(string $input, string $expected): void
{
self::assertSame($expected, Security::escURL($input));
}

/**
* @dataProvider dataCSS
*
* @param string $input
* @param string $expected
*
* @throws SecurityException
*/
public function testEscCSS(string $input, string $expected): void
{
self::assertSame($expected, Security::escCSS($input));
}

public function testCharsetNotSupportedException(): void
{
$countThrownExceptions = 0;
Expand All @@ -207,7 +292,21 @@ public function testCharsetNotSupportedException(): void
++$countThrownExceptions;
}

self::assertSame(3, $countThrownExceptions);
try {
Security::escURL('a', 'nope');
} catch (SecurityException $e) {
self::assertSame("Charset 'nope' is not supported", $e->getMessage());
++$countThrownExceptions;
}

try {
Security::escCSS('a', 'nope');
} catch (SecurityException $e) {
self::assertSame("Charset 'nope' is not supported", $e->getMessage());
++$countThrownExceptions;
}

self::assertSame(5, $countThrownExceptions);
}

public function testInvalidCharacter(): void
Expand Down Expand Up @@ -236,7 +335,21 @@ public function testInvalidCharacter(): void
++$countThrownExceptions;
}

self::assertSame(3, $countThrownExceptions);
try {
Security::escURL($invalidChar);
} catch (SecurityException $e) {
self::assertSame('String to convert is not valid for the specified charset', $e->getMessage());
++$countThrownExceptions;
}

try {
Security::escCSS($invalidChar);
} catch (SecurityException $e) {
self::assertSame('String to convert is not valid for the specified charset', $e->getMessage());
++$countThrownExceptions;
}

self::assertSame(5, $countThrownExceptions);
}

/**
Expand Down