Skip to content

Commit 4c309e3

Browse files
mpetrovichmarcj
authored andcommitted
SQL injection fix: Cast limit to integer when setting via Criteria::setLimit() (#1465)
* Cast limit to integer when setting via Criteria::setLimit() This is a followup to a fix for SQL injections with LIMIT clauses in MySQL [1]. That fix only applied to the MySQL adapter, and other existing or future adapters could still be at risk. By coercing limit inputs to integers upon setting them, we can avoid SQL injection vulnerabilities with `limit()` across all database adapters. The original code comments implied that integer coercion could be problematic with 32-bit integers, but unit tests in this PR prove otherwise. Even 64-bit integers seem to work fine. [1] #1464 * Add missing tests for setOffset() * Remove note about 32-bit truncation Unit tests show that even 64-bit integers aren't truncated.
1 parent cd23d73 commit 4c309e3

File tree

2 files changed

+149
-7
lines changed

2 files changed

+149
-7
lines changed

src/Propel/Runtime/ActiveQuery/Criteria.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,8 +1259,7 @@ public function isSingleRecord()
12591259
*/
12601260
public function setLimit($limit)
12611261
{
1262-
// TODO: do we enforce int here? 32bit issue if we do
1263-
$this->limit = $limit;
1262+
$this->limit = (int) $limit;
12641263

12651264
return $this;
12661265
}
@@ -1278,8 +1277,7 @@ public function getLimit()
12781277
/**
12791278
* Set offset.
12801279
*
1281-
* @param int $offset An int with the value for offset. (Note this values is
1282-
* cast to a 32bit integer and may result in truncation)
1280+
* @param int $offset An int with the value for offset.
12831281
* @return $this|Criteria Modified Criteria object (for fluent API)
12841282
*/
12851283
public function setOffset($offset)

tests/Propel/Tests/Runtime/ActiveQuery/CriteriaTest.php

Lines changed: 147 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,16 +1064,160 @@ public function testClear()
10641064
$this->assertFalse($c->getUseTransaction(), 'useTransaction is false by default');
10651065
}
10661066

1067-
public function testLimit()
1067+
public function testDefaultLimit()
10681068
{
10691069
$c = new Criteria();
10701070
$this->assertEquals(-1, $c->getLimit(), 'Limit is -1 by default');
1071+
}
1072+
1073+
/**
1074+
* @dataProvider dataLimit
1075+
*/
1076+
public function testLimit($limit, $expected)
1077+
{
1078+
$c = new Criteria();
1079+
$c2 = $c->setLimit($limit);
10711080

1072-
$c2 = $c->setLimit(1);
1073-
$this->assertEquals(1, $c->getLimit(), 'Limit is set by setLimit');
1081+
$this->assertSame($expected, $c->getLimit(), 'Correct limit is set by setLimit()');
10741082
$this->assertSame($c, $c2, 'setLimit() returns the current Criteria');
10751083
}
10761084

1085+
public function dataLimit()
1086+
{
1087+
return array(
1088+
'Negative value' => array(
1089+
'limit' => -1,
1090+
'expected' => -1
1091+
),
1092+
'Zero' => array(
1093+
'limit' => 0,
1094+
'expected' => 0
1095+
),
1096+
1097+
'Small integer' => array(
1098+
'limit' => 38427,
1099+
'expected' => 38427
1100+
),
1101+
'Small integer as a string' => array(
1102+
'limit' => '38427',
1103+
'expected' => 38427
1104+
),
1105+
1106+
'Largest 32-bit integer' => array(
1107+
'limit' => 2147483647,
1108+
'expected' => 2147483647
1109+
),
1110+
'Largest 32-bit integer as a string' => array(
1111+
'limit' => '2147483647',
1112+
'expected' => 2147483647
1113+
),
1114+
1115+
'Largest 64-bit integer' => array(
1116+
'limit' => 9223372036854775807,
1117+
'expected' => 9223372036854775807
1118+
),
1119+
'Largest 64-bit integer as a string' => array(
1120+
'limit' => '9223372036854775807',
1121+
'expected' => 9223372036854775807
1122+
),
1123+
1124+
'Decimal value' => array(
1125+
'limit' => 123.9,
1126+
'expected' => 123
1127+
),
1128+
'Decimal value as a string' => array(
1129+
'limit' => '123.9',
1130+
'expected' => 123
1131+
),
1132+
1133+
'Non-numeric string' => array(
1134+
'limit' => 'foo',
1135+
'expected' => 0
1136+
),
1137+
'Injected SQL' => array(
1138+
'limit' => '3;DROP TABLE abc',
1139+
'expected' => 3
1140+
),
1141+
);
1142+
}
1143+
1144+
public function testDefaultOffset()
1145+
{
1146+
$c = new Criteria();
1147+
$this->assertEquals(0, $c->getOffset(), 'Offset is 0 by default');
1148+
}
1149+
1150+
/**
1151+
* @dataProvider dataOffset
1152+
*/
1153+
public function testOffset($offset, $expected)
1154+
{
1155+
$c = new Criteria();
1156+
$c2 = $c->setOffset($offset);
1157+
1158+
$this->assertSame($expected, $c->getOffset(), 'Correct offset is set by setOffset()');
1159+
$this->assertSame($c, $c2, 'setOffset() returns the current Criteria');
1160+
}
1161+
1162+
public function dataOffset()
1163+
{
1164+
return array(
1165+
'Negative value' => array(
1166+
'offset' => -1,
1167+
'expected' => -1
1168+
),
1169+
'Zero' => array(
1170+
'offset' => 0,
1171+
'expected' => 0
1172+
),
1173+
1174+
'Small integer' => array(
1175+
'offset' => 38427,
1176+
'expected' => 38427
1177+
),
1178+
'Small integer as a string' => array(
1179+
'offset' => '38427',
1180+
'expected' => 38427
1181+
),
1182+
1183+
'Largest 32-bit integer' => array(
1184+
'offset' => 2147483647,
1185+
'expected' => 2147483647
1186+
),
1187+
'Largest 32-bit integer as a string' => array(
1188+
'offset' => '2147483647',
1189+
'expected' => 2147483647
1190+
),
1191+
1192+
'Largest 64-bit integer' => array(
1193+
'offset' => 9223372036854775807,
1194+
'expected' => 9223372036854775807
1195+
),
1196+
'Largest 64-bit integer as a string' => array(
1197+
'offset' => '9223372036854775807',
1198+
'expected' => 9223372036854775807
1199+
),
1200+
1201+
'Decimal value' => array(
1202+
'offset' => 123.9,
1203+
'expected' => 123
1204+
),
1205+
'Decimal value as a string' => array(
1206+
'offset' => '123.9',
1207+
'expected' => 123
1208+
),
1209+
1210+
'Non-numeric string' => array(
1211+
'offset' => 'foo',
1212+
'expected' => 0
1213+
),
1214+
'Injected SQL' => array(
1215+
'offset' => '3;DROP TABLE abc',
1216+
'expected' => 3
1217+
),
1218+
);
1219+
}
1220+
10771221
public function testCombineAndFilterBy()
10781222
{
10791223
$params = [];

0 commit comments

Comments
 (0)