Skip to content

Commit e3e770c

Browse files
jasonvargaclaude
andcommitted
Validate token names against an allowlist
Reject any token name that isn't strictly [A-Za-z0-9_-] rather than denylisting separators. This covers traversal, absolute paths, and Windows drive/UNC paths in one check, regardless of OS or encoding. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent efe7f59 commit e3e770c

2 files changed

Lines changed: 24 additions & 4 deletions

File tree

src/Tokens/FileTokenRepository.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class FileTokenRepository extends TokenRepository
1111
{
1212
public function make(?string $token, string $handler, array $data = []): TokenContract
1313
{
14-
if ($token && (str_contains($token, '/') || str_contains($token, '\\'))) {
14+
if ($token && ! $this->isValidTokenName($token)) {
1515
$token = null;
1616
}
1717

@@ -20,7 +20,7 @@ public function make(?string $token, string $handler, array $data = []): TokenCo
2020

2121
public function find(string $token): ?TokenContract
2222
{
23-
if (str_contains($token, '/') || str_contains($token, '\\')) {
23+
if (! $this->isValidTokenName($token)) {
2424
return null;
2525
}
2626

@@ -63,6 +63,11 @@ public function collectGarbage(): void
6363
->each->delete();
6464
}
6565

66+
private function isValidTokenName(string $token): bool
67+
{
68+
return (bool) preg_match('/^[A-Za-z0-9_-]+$/', $token);
69+
}
70+
6671
private function makeFromPath(string $path): FileToken
6772
{
6873
$yaml = YAML::file($path)->parse();

tests/Tokens/TokenRepositoryTest.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Facades\Statamic\Tokens\Generator;
66
use Illuminate\Support\Carbon;
77
use Illuminate\Support\Collection;
8+
use PHPUnit\Framework\Attributes\DataProvider;
89
use PHPUnit\Framework\Attributes\Test;
910
use Statamic\Contracts\Tokens\Token;
1011
use Statamic\Facades\File;
@@ -138,11 +139,25 @@ public function it_prevents_path_traversal_in_find()
138139
}
139140

140141
#[Test]
141-
public function it_prevents_path_traversal_in_make()
142+
#[DataProvider('invalidTokenNameProvider')]
143+
public function it_rejects_invalid_token_names_in_make($token)
142144
{
143145
Generator::shouldReceive('generate')->once()->andReturn('generated-token');
144146

145-
$this->assertEquals('generated-token', $this->tokens->make('../evil', 'Handler')->token());
147+
$this->assertEquals('generated-token', $this->tokens->make($token, 'Handler')->token());
148+
}
149+
150+
public static function invalidTokenNameProvider()
151+
{
152+
return [
153+
'parent traversal' => ['../evil'],
154+
'backslash traversal' => ['..\\evil'],
155+
'nested traversal' => ['foo/../../evil'],
156+
'forward slash' => ['foo/evil'],
157+
'dots only' => ['..'],
158+
'absolute path' => ['/etc/passwd'],
159+
'windows drive' => ['C:\\evil'],
160+
];
146161
}
147162

148163
#[Test]

0 commit comments

Comments
 (0)