Skip to content

Commit f3429eb

Browse files
authored
Merge pull request #122 from grimzy/sql-injection-fix-mysql-5.6
Fix MySQL vulnerability (MySQL 5.6)
2 parents 00b8829 + 205e201 commit f3429eb

11 files changed

+208
-60
lines changed

.travis.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ php:
55
- '5.6'
66
- '7.0'
77
- '7.1'
8+
- '7.2'
9+
- '7.3'
810

911
env:
1012
- MYSQL_VERSION=5.6
@@ -17,7 +19,7 @@ services:
1719
- docker
1820

1921
before_install:
20-
- echo "memory_limit=2G" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
22+
- echo "memory_limit=3G" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
2123
- sudo /etc/init.d/mysql stop
2224
- make start_db V=$MYSQL_VERSION
2325

composer.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
],
1212
"require": {
1313
"php": ">=5.5.9",
14-
"illuminate/database": "^5.2|^6.0",
14+
"illuminate/database": "^5.2||^6.0",
1515
"geo-io/wkb-parser": "^1.0",
1616
"jmikola/geojson": "^1.0"
1717
},
1818
"require-dev": {
1919
"phpunit/phpunit": "~4.8||~5.7",
2020
"mockery/mockery": "^0.9.9",
21-
"laravel/laravel": "^5.2|^6.0",
21+
"laravel/laravel": "^5.2||^6.0",
2222
"codeclimate/php-test-reporter": "dev-master",
2323
"doctrine/dbal": "^2.5",
2424
"laravel/browser-kit-testing": "^2.0"

src/Eloquent/BaseBuilder.php

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Grimzy\LaravelMysqlSpatial\Eloquent;
4+
5+
use Illuminate\Database\Query\Builder as QueryBuilder;
6+
7+
class BaseBuilder extends QueryBuilder
8+
{
9+
protected function cleanBindings(array $bindings)
10+
{
11+
$bindings = array_map(function ($binding) {
12+
return $binding instanceof SpatialExpression ? $binding->getSpatialValue() : $binding;
13+
}, $bindings);
14+
15+
return parent::cleanBindings($bindings);
16+
}
17+
}

src/Eloquent/Builder.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ public function update(array $values)
2020

2121
protected function asWKT(GeometryInterface $geometry)
2222
{
23-
return $this->getQuery()->raw("GeomFromText('".$geometry->toWKT()."')");
23+
return new SpatialExpression($geometry);
2424
}
2525
}

src/Eloquent/SpatialExpression.php

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Grimzy\LaravelMysqlSpatial\Eloquent;
4+
5+
use Illuminate\Database\Query\Expression;
6+
7+
class SpatialExpression extends Expression
8+
{
9+
public function getValue()
10+
{
11+
return 'ST_GeomFromText(?)';
12+
}
13+
14+
public function getSpatialValue()
15+
{
16+
return $this->value->toWkt();
17+
}
18+
}

src/Eloquent/SpatialTrait.php

+55-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Grimzy\LaravelMysqlSpatial\Eloquent;
44

55
use Grimzy\LaravelMysqlSpatial\Exceptions\SpatialFieldsNotDefinedException;
6+
use Grimzy\LaravelMysqlSpatial\Exceptions\UnknownSpatialRelationFunction;
67
use Grimzy\LaravelMysqlSpatial\Types\Geometry;
78
use Grimzy\LaravelMysqlSpatial\Types\GeometryInterface;
89
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
@@ -37,6 +38,17 @@ trait SpatialTrait
3738

3839
public $geometries = [];
3940

41+
protected $stRelations = [
42+
'within',
43+
'crosses',
44+
'contains',
45+
'disjoint',
46+
'equals',
47+
'intersects',
48+
'overlaps',
49+
'touches',
50+
];
51+
4052
/**
4153
* Create a new Eloquent query builder for the model.
4254
*
@@ -49,12 +61,21 @@ public function newEloquentBuilder($query)
4961
return new Builder($query);
5062
}
5163

64+
protected function newBaseQueryBuilder()
65+
{
66+
$connection = $this->getConnection();
67+
68+
return new BaseBuilder(
69+
$connection, $connection->getQueryGrammar(), $connection->getPostProcessor()
70+
);
71+
}
72+
5273
protected function performInsert(EloquentBuilder $query, array $options = [])
5374
{
5475
foreach ($this->attributes as $key => $value) {
5576
if ($value instanceof GeometryInterface) {
5677
$this->geometries[$key] = $value; //Preserve the geometry objects prior to the insert
57-
$this->attributes[$key] = $this->getConnection()->raw(sprintf("GeomFromText('%s')", $value->toWKT()));
78+
$this->attributes[$key] = new SpatialExpression($value);
5879
}
5980
}
6081

@@ -89,30 +110,59 @@ public function getSpatialFields()
89110
}
90111
}
91112

113+
public function isColumnAllowed($geometryColumn)
114+
{
115+
if (!in_array($geometryColumn, $this->getSpatialFields())) {
116+
throw new SpatialFieldsNotDefinedException();
117+
}
118+
119+
return true;
120+
}
121+
92122
public function scopeDistance($query, $geometryColumn, $geometry, $distance, $exclude_self = false)
93123
{
94-
$query->whereRaw("st_distance(`{$geometryColumn}`, GeomFromText('{$geometry->toWkt()}')) <= {$distance}");
124+
$this->isColumnAllowed($geometryColumn);
125+
126+
$query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?)) <= ?", [
127+
$geometry->toWkt(),
128+
$distance,
129+
]);
95130

96131
if ($exclude_self) {
97-
$query->whereRaw("st_distance(`{$geometryColumn}`, GeomFromText('{$geometry->toWkt()}')) != 0");
132+
$query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?)) != 0", [
133+
$geometry->toWkt(),
134+
]);
98135
}
99136

100137
return $query;
101138
}
102139

103140
public function scopeDistanceValue($query, $geometryColumn, $geometry)
104141
{
142+
$this->isColumnAllowed($geometryColumn);
143+
105144
$columns = $query->getQuery()->columns;
106145

107146
if (!$columns) {
108147
$query->select('*');
109148
}
110-
$query->selectRaw("st_distance(`{$geometryColumn}`, GeomFromText('{$geometry->toWkt()}')) as distance");
149+
150+
$query->selectRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?)) as distance", [
151+
$geometry->toWkt(),
152+
]);
111153
}
112154

113155
public function scopeComparison($query, $geometryColumn, $geometry, $relationship)
114156
{
115-
$query->whereRaw("st_{$relationship}(`{$geometryColumn}`, GeomFromText('{$geometry->toWkt()}'))");
157+
$this->isColumnAllowed($geometryColumn);
158+
159+
if (!in_array($relationship, $this->stRelations)) {
160+
throw new UnknownSpatialRelationFunction($relationship);
161+
}
162+
163+
$query->whereRaw("st_{$relationship}(`$geometryColumn`, ST_GeomFromText(?))", [
164+
$geometry->toWkt(),
165+
]);
116166

117167
return $query;
118168
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Grimzy\LaravelMysqlSpatial\Exceptions;
4+
5+
class UnknownSpatialRelationFunction extends \RuntimeException
6+
{
7+
}

src/MysqlConnection.php

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public function __construct($pdo, $database = '', $tablePrefix = '', array $conf
2424
'multilinestring',
2525
'multipolygon',
2626
'geometrycollection',
27+
'geomcollection',
2728
];
2829
$dbPlatform = $this->getDoctrineSchemaManager()->getDatabasePlatform();
2930
foreach ($geometries as $type) {

tests/Integration/SpatialTest.php

+8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ public function createApplication()
2929
$app['config']->set('database.connections.mysql.database', 'spatial_test');
3030
$app['config']->set('database.connections.mysql.username', 'root');
3131
$app['config']->set('database.connections.mysql.password', '');
32+
$app['config']->set('database.connections.mysql.modes', [
33+
'ONLY_FULL_GROUP_BY',
34+
'STRICT_TRANS_TABLES',
35+
'NO_ZERO_IN_DATE',
36+
'NO_ZERO_DATE',
37+
'ERROR_FOR_DIVISION_BY_ZERO',
38+
'NO_ENGINE_SUBSTITUTION',
39+
]);
3240

3341
return $app;
3442
}

tests/Unit/Eloquent/BuilderTest.php

+11-21
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use BaseTestCase;
66
use Grimzy\LaravelMysqlSpatial\Eloquent\Builder;
7+
use Grimzy\LaravelMysqlSpatial\Eloquent\SpatialExpression;
78
use Grimzy\LaravelMysqlSpatial\Eloquent\SpatialTrait;
89
use Grimzy\LaravelMysqlSpatial\MysqlConnection;
910
use Grimzy\LaravelMysqlSpatial\Types\LineString;
@@ -36,50 +37,39 @@ protected function setUp()
3637

3738
public function testUpdatePoint()
3839
{
39-
$this->queryBuilder
40-
->shouldReceive('raw')
41-
->with("GeomFromText('POINT(2 1)')")
42-
->once();
43-
40+
$point = new Point(1, 2);
4441
$this->queryBuilder
4542
->shouldReceive('update')
43+
->with(['point' => new SpatialExpression($point)])
4644
->once();
4745

48-
$this->builder->update(['point' => new Point(1, 2)]);
46+
$this->builder->update(['point' => $point]);
4947
}
5048

5149
public function testUpdateLinestring()
5250
{
53-
$this->queryBuilder
54-
->shouldReceive('raw')
55-
->with("GeomFromText('LINESTRING(0 0,1 1,2 2)')")
56-
->once();
51+
$linestring = new LineString([new Point(0, 0), new Point(1, 1), new Point(2, 2)]);
5752

5853
$this->queryBuilder
5954
->shouldReceive('update')
55+
->with(['linestring' => new SpatialExpression($linestring)])
6056
->once();
6157

62-
$linestring = new LineString([new Point(0, 0), new Point(1, 1), new Point(2, 2)]);
63-
6458
$this->builder->update(['linestring' => $linestring]);
6559
}
6660

6761
public function testUpdatePolygon()
6862
{
69-
$this->queryBuilder
70-
->shouldReceive('raw')
71-
->with("GeomFromText('POLYGON((0 0,1 0),(1 0,1 1),(1 1,0 0))')")
72-
->once();
73-
74-
$this->queryBuilder
75-
->shouldReceive('update')
76-
->once();
77-
7863
$linestrings[] = new LineString([new Point(0, 0), new Point(0, 1)]);
7964
$linestrings[] = new LineString([new Point(0, 1), new Point(1, 1)]);
8065
$linestrings[] = new LineString([new Point(1, 1), new Point(0, 0)]);
8166
$polygon = new Polygon($linestrings);
8267

68+
$this->queryBuilder
69+
->shouldReceive('update')
70+
->with(['polygon' => new SpatialExpression($polygon)])
71+
->once();
72+
8373
$this->builder->update(['polygon' => $polygon]);
8474
}
8575
}

0 commit comments

Comments
 (0)