Skip to content

Commit 1a29bcc

Browse files
committed
more config and coverage
1 parent d95a89b commit 1a29bcc

File tree

10 files changed

+154
-38
lines changed

10 files changed

+154
-38
lines changed

README.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ The default driver aligns with [OWASP](https://cheatsheetseries.owasp.org/cheats
2121

2222
- PHP >= 8.2
2323
- ext-json
24+
- Node >= 18
25+
- NPM
2426

2527
## Installation
2628

@@ -56,14 +58,17 @@ return [
5658

5759
'drivers' => [
5860
'dompurify-cli' => new DompurifyCliConfig(
59-
env('NODE_PATH', 'node'),
60-
env('NPM_PATH', 'npm'),
61+
node: env('NODE_PATH'),
62+
npm: env('NPM_PATH'),
63+
binary: null,
64+
tempFolder: null,
6165
),
6266
'dompurify-service' => new DompurifyServiceConfig(
63-
env('NODE_PATH', 'node'),
64-
env('NPM_PATH', 'npm'),
65-
'127.0.0.1',
66-
63000,
67+
node: env('NODE_PATH'),
68+
npm: env('NPM_PATH'),
69+
host: '127.0.0.1',
70+
port: 63000,
71+
binary: null,
6772
),
6873
],
6974
];

src/Dompurify/DompurifyCli.php

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ public function exec(string $html): string
3838
$output = $process->getOutput();
3939
$cleanHtmlPath = trim($output);
4040

41+
if (! file_exists($cleanHtmlPath)) {
42+
throw new XsslessException("Could not locate the file '{$cleanHtmlPath}'");
43+
}
44+
4145
$clean = file_get_contents($cleanHtmlPath);
4246

4347
if ($clean === false) {
@@ -53,8 +57,7 @@ public function exec(string $html): string
5357

5458
private function binPath(): string
5559
{
56-
// TODO: allow config to override
57-
$binPath = __DIR__.DIRECTORY_SEPARATOR.'cli.js';
60+
$binPath = $this->config->binary ?? __DIR__.DIRECTORY_SEPARATOR.'cli.js';
5861

5962
$binAbsPath = realpath($binPath);
6063

@@ -69,6 +72,7 @@ private function saveHtml(string $value): string
6972
{
7073
$dir = $this->tempDir();
7174

75+
// ? use tempnam
7276
$fileName = mt_rand().'-'.str_replace([' ', '.'], '', microtime()).'.xss';
7377

7478
$path = $dir.DIRECTORY_SEPARATOR.$fileName;
@@ -82,16 +86,20 @@ private function saveHtml(string $value): string
8286

8387
private function tempDir(): string
8488
{
85-
// TODO: take path from config
86-
$tempDir = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR);
87-
$dir = $tempDir.DIRECTORY_SEPARATOR.'xssless';
89+
if (is_null($this->config->tempFolder)) {
90+
$dir = sys_get_temp_dir().DIRECTORY_SEPARATOR.'xssless';
8891

89-
if (! file_exists($dir)) {
90-
if (mkdir($dir, 0777, true) === false) {
92+
if (! file_exists($dir) && mkdir($dir, 0777, true) === false) {
9193
throw new XsslessException("Could not create temporary directory '{$dir}'");
9294
}
95+
96+
return $dir;
97+
}
98+
99+
if (! file_exists($this->config->tempFolder)) {
100+
throw new XsslessException("Could not locate temporary directory '{$this->config->tempFolder}'");
93101
}
94102

95-
return $dir;
103+
return $this->config->tempFolder;
96104
}
97105
}

src/Dompurify/DompurifyCliConfig.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ class DompurifyCliConfig implements ConfigInterface
99
private readonly string $class;
1010

1111
public function __construct(
12-
public string $node,
13-
public string $npm,
12+
public string $node = 'node',
13+
public string $npm = 'npm',
14+
public ?string $binary = null,
15+
public ?string $tempFolder = null,
1416
) {
1517
$this->class = DompurifyCli::class;
1618
}

src/Dompurify/DompurifyService.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public function start(): static
5353
{
5454
$this->serviceProcess = new Process([
5555
$this->config->node,
56-
__DIR__.DIRECTORY_SEPARATOR.'http.js',
56+
$this->config->binary ?? __DIR__.DIRECTORY_SEPARATOR.'http.js',
5757
$this->config->host,
5858
$this->config->port,
5959
]);
@@ -145,4 +145,6 @@ private function isSigTerm(): bool
145145
// {
146146
// return $this->serviceProcess->getTermSignal() === 1;
147147
// }
148+
149+
// 92..97, 104, 113..136
148150
}

src/Dompurify/DompurifyServiceConfig.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ class DompurifyServiceConfig implements ConfigInterface
99
public readonly string $class;
1010

1111
public function __construct(
12-
public string $node,
13-
public string $npm,
14-
public string $host,
15-
public int $port,
12+
public string $node = 'node',
13+
public string $npm = 'npm',
14+
public string $host = '127.0.0.1',
15+
public int $port = 6300,
16+
public ?string $binary = null,
1617
) {
1718
$this->class = DompurifyService::class;
1819
}

src/laravel/config/xssless.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@
88

99
'drivers' => [
1010
'dompurify-cli' => new DompurifyCliConfig(
11-
env('NODE_PATH', 'node'), // @phpstan-ignore argument.type
12-
env('NPM_PATH', 'npm'), // @phpstan-ignore argument.type
11+
node: env('NODE_PATH'), // @phpstan-ignore argument.type
12+
npm: env('NPM_PATH'), // @phpstan-ignore argument.type
13+
binary: null,
14+
tempFolder: null,
1315
),
1416
'dompurify-service' => new DompurifyServiceConfig(
15-
env('NODE_PATH', 'node'), // @phpstan-ignore argument.type
16-
env('NPM_PATH', 'npm'), // @phpstan-ignore argument.type
17-
'127.0.0.1',
18-
63000,
17+
node: env('NODE_PATH'), // @phpstan-ignore argument.type
18+
npm: env('NPM_PATH'), // @phpstan-ignore argument.type
19+
host: '127.0.0.1',
20+
port: 63000,
21+
binary: null,
1922
),
2023
],
2124
];

tests/Dompurify/DompurifyCliTest.php

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,20 @@
33
use Medilies\Xssless\Dompurify\DompurifyCli;
44
use Medilies\Xssless\Dompurify\DompurifyCliConfig;
55
use Medilies\Xssless\Xssless;
6+
use Medilies\Xssless\XsslessException;
67
use Symfony\Component\Process\Exception\ProcessFailedException;
78

8-
test('setup()', function () {
9+
it('throws on bad node path', function () {
910
$cleaner = (new DompurifyCli)->configure(new DompurifyCliConfig(
11+
'nodeZz',
12+
'npm',
13+
));
14+
15+
expect(fn () => $cleaner->exec('foo'))->toThrow(ProcessFailedException::class);
16+
});
17+
18+
test('setup()', function () {
19+
$cleaner = (new Xssless)->using(new DompurifyCliConfig(
1020
'node',
1121
'npm',
1222
));
@@ -27,20 +37,42 @@
2737

2838
test('clean()', function () {
2939
$cleaner = (new Xssless)->using(new DompurifyCliConfig(
30-
'node',
31-
'npm',
40+
node: 'node',
41+
npm: 'npm',
42+
tempFolder: __DIR__,
3243
));
3344

3445
$clean = $cleaner->clean('<IMG """><SCRIPT>alert("XSS")</SCRIPT>">');
3546

3647
expect($clean)->toBe('<img>"&gt;');
3748
})->depends('setup()');
3849

39-
it('throws on bad node path', function () {
50+
it('throws when cannot read cleaned file', function () {
4051
$cleaner = (new DompurifyCli)->configure(new DompurifyCliConfig(
41-
'nodeZz',
42-
'npm',
52+
node: 'node',
53+
npm: 'npm',
54+
binary: __DIR__.'/js-mocks/cli-returns-bad-path.js',
4355
));
4456

45-
expect(fn () => $cleaner->exec('foo'))->toThrow(ProcessFailedException::class);
46-
});
57+
expect(fn () => $cleaner->exec('foo'))->toThrow(XsslessException::class);
58+
})->depends('setup()');
59+
60+
it('throws when cannot find binary file', function () {
61+
$cleaner = (new DompurifyCli)->configure(new DompurifyCliConfig(
62+
node: 'node',
63+
npm: 'npm',
64+
binary: __DIR__.'/js-mocks/x.js',
65+
));
66+
67+
expect(fn () => $cleaner->exec('foo'))->toThrow(XsslessException::class);
68+
})->depends('setup()');
69+
70+
it('throws when cannot locate temp folder', function () {
71+
$cleaner = (new DompurifyCli)->configure(new DompurifyCliConfig(
72+
node: 'node',
73+
npm: 'npm',
74+
tempFolder: __DIR__.'/x',
75+
));
76+
77+
expect(fn () => $cleaner->exec('foo'))->toThrow(XsslessException::class);
78+
})->depends('setup()');

tests/Dompurify/DompurifyServiceTest.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@
4646

4747
$cleaner = (new Xssless)->using($config);
4848

49-
$service = (new DompurifyService)->configure($config);
50-
51-
$service->start();
49+
$service = $cleaner->start();
5250

5351
$dirty = '<IMG """><SCRIPT>alert("XSS")</SCRIPT>">';
5452

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const htmlFile = process.argv[2];
2+
3+
console.log(htmlFile + ".clean13465789");
4+
process.exit(0);

tests/XsslessTest.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
use Medilies\Xssless\CliInterface;
4+
use Medilies\Xssless\ConfigInterface;
5+
use Medilies\Xssless\Dompurify\DompurifyCliConfig;
6+
use Medilies\Xssless\Xssless;
7+
use Medilies\Xssless\XsslessException;
8+
9+
it('throws when makeCleaner() with no config', function () {
10+
$cleaner = new Xssless;
11+
12+
$cleaner->using(new class implements ConfigInterface
13+
{
14+
public function getClass(): string
15+
{
16+
return Xssless::class;
17+
}
18+
});
19+
20+
expect(fn () => $cleaner->clean('foo'))->toThrow(XsslessException::class);
21+
});
22+
23+
it('throws when makeCleaner() with no interface', function () {
24+
$cleaner = new Xssless;
25+
26+
expect(fn () => $cleaner->clean('foo'))->toThrow(XsslessException::class);
27+
});
28+
29+
it('throws when start() with CliInterface', function () {
30+
$cleaner = new Xssless;
31+
$cleaner->using(new DompurifyCliConfig);
32+
33+
expect(fn () => $cleaner->start())->toThrow(XsslessException::class);
34+
});
35+
36+
it('throws when setup() without HasSetupInterface', function () {
37+
$cleaner = new Xssless;
38+
39+
$cleaner->using(new class implements ConfigInterface
40+
{
41+
public function getClass(): string
42+
{
43+
return NoSetupDriver::class;
44+
}
45+
});
46+
47+
expect(fn () => $cleaner->setup())->toThrow(XsslessException::class);
48+
});
49+
50+
class NoSetupDriver implements CliInterface
51+
{
52+
public function configure(ConfigInterface $config): static
53+
{
54+
return $this;
55+
}
56+
57+
public function exec(string $html): string
58+
{
59+
return '';
60+
}
61+
}

0 commit comments

Comments
 (0)