Skip to content

Commit cb328d3

Browse files
authored
Merge pull request #137 from simon-mundy/fix-invalid-parameter-name
Fix bindParametersFromContainer() to prevent invalid parameter name characters
2 parents 0c68dac + bd2dd9b commit cb328d3

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

src/Adapter/Driver/Pdo/Statement.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
use function is_array;
2323
use function is_int;
2424
use function ltrim;
25+
use function preg_match;
26+
use function sprintf;
2527

2628
class Statement implements StatementInterface, PdoDriverAwareInterface, ProfilerAwareInterface
2729
{
@@ -223,8 +225,19 @@ protected function bindParametersFromContainer(): void
223225
};
224226
}
225227

226-
// parameter is named or positional, value is reference
227-
$parameter = is_int($name) ? $name + 1 : ':' . ltrim($name, ':');
228+
if (is_int($name)) {
229+
$parameter = $name + 1;
230+
} else {
231+
if (! preg_match('/^:?[a-zA-Z0-9_]+$/', $name)) {
232+
throw new Exception\RuntimeException(sprintf(
233+
'The PDO param "%s" contains invalid characters.'
234+
. ' Only alphabetic characters, digits, and underscores (_) are allowed.',
235+
$name
236+
));
237+
}
238+
$parameter = ':' . ltrim($name, ':');
239+
}
240+
228241
$this->resource->bindParam($parameter, $value, $type);
229242
}
230243

test/unit/Adapter/Driver/Pdo/StatementTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
use Override;
88
use PhpDb\Adapter\Driver\Pdo\Result;
99
use PhpDb\Adapter\Driver\Pdo\Statement;
10+
use PhpDb\Adapter\Exception\RuntimeException;
1011
use PhpDb\Adapter\ParameterContainer;
1112
use PhpDbTest\Adapter\Driver\Pdo\TestAsset\TestConnection;
1213
use PhpDbTest\Adapter\Driver\Pdo\TestAsset\TestPdo;
1314
use PHPUnit\Framework\Attributes\CoversMethod;
15+
use PHPUnit\Framework\Attributes\DataProvider;
1416
use PHPUnit\Framework\TestCase;
1517

1618
#[CoversMethod(Statement::class, 'setDriver')]
@@ -22,6 +24,7 @@
2224
#[CoversMethod(Statement::class, 'prepare')]
2325
#[CoversMethod(Statement::class, 'isPrepared')]
2426
#[CoversMethod(Statement::class, 'execute')]
27+
#[CoversMethod(Statement::class, 'bindParametersFromContainer')]
2528
final class StatementTest extends TestCase
2629
{
2730
protected Statement $statement;
@@ -111,4 +114,29 @@ public function testExecute(): void
111114
$this->statement->prepare('SELECT 1');
112115
self::assertInstanceOf(Result::class, $this->statement->execute());
113116
}
117+
118+
/** @return array<string, array{string}> */
119+
public static function invalidParameterNameProvider(): array
120+
{
121+
return [
122+
'dollar sign' => ['tz$'],
123+
'with colon' => [':tz$'],
124+
'hyphen' => ['my-param'],
125+
'space' => ['my param'],
126+
'dot' => ['my.param'],
127+
'at sign' => ['param@name'],
128+
];
129+
}
130+
131+
#[DataProvider('invalidParameterNameProvider')]
132+
public function testExecuteThrowsOnInvalidParameterName(string $name): void
133+
{
134+
$this->statement->setDriver(new TestPdo(new TestConnection($pdo = new TestAsset\SqliteMemoryPdo())));
135+
$this->statement->initialize($pdo);
136+
$this->statement->prepare('SELECT 1');
137+
138+
$this->expectException(RuntimeException::class);
139+
$this->expectExceptionMessage('contains invalid characters');
140+
$this->statement->execute([$name => 'value']);
141+
}
114142
}

0 commit comments

Comments
 (0)