Skip to content

Commit

Permalink
Merge pull request #122 from grimzy/sql-injection-fix-mysql-5.6
Browse files Browse the repository at this point in the history
Fix MySQL vulnerability (MySQL 5.6)
  • Loading branch information
grimzy authored Mar 3, 2020
2 parents 00b8829 + 205e201 commit f3429eb
Show file tree
Hide file tree
Showing 11 changed files with 208 additions and 60 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ php:
- '5.6'
- '7.0'
- '7.1'
- '7.2'
- '7.3'

env:
- MYSQL_VERSION=5.6
Expand All @@ -17,7 +19,7 @@ services:
- docker

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

Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
],
"require": {
"php": ">=5.5.9",
"illuminate/database": "^5.2|^6.0",
"illuminate/database": "^5.2||^6.0",
"geo-io/wkb-parser": "^1.0",
"jmikola/geojson": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "~4.8||~5.7",
"mockery/mockery": "^0.9.9",
"laravel/laravel": "^5.2|^6.0",
"laravel/laravel": "^5.2||^6.0",
"codeclimate/php-test-reporter": "dev-master",
"doctrine/dbal": "^2.5",
"laravel/browser-kit-testing": "^2.0"
Expand Down
17 changes: 17 additions & 0 deletions src/Eloquent/BaseBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Grimzy\LaravelMysqlSpatial\Eloquent;

use Illuminate\Database\Query\Builder as QueryBuilder;

class BaseBuilder extends QueryBuilder
{
protected function cleanBindings(array $bindings)
{
$bindings = array_map(function ($binding) {
return $binding instanceof SpatialExpression ? $binding->getSpatialValue() : $binding;
}, $bindings);

return parent::cleanBindings($bindings);
}
}
2 changes: 1 addition & 1 deletion src/Eloquent/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ public function update(array $values)

protected function asWKT(GeometryInterface $geometry)
{
return $this->getQuery()->raw("GeomFromText('".$geometry->toWKT()."')");
return new SpatialExpression($geometry);
}
}
18 changes: 18 additions & 0 deletions src/Eloquent/SpatialExpression.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Grimzy\LaravelMysqlSpatial\Eloquent;

use Illuminate\Database\Query\Expression;

class SpatialExpression extends Expression
{
public function getValue()
{
return 'ST_GeomFromText(?)';
}

public function getSpatialValue()
{
return $this->value->toWkt();
}
}
60 changes: 55 additions & 5 deletions src/Eloquent/SpatialTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Grimzy\LaravelMysqlSpatial\Eloquent;

use Grimzy\LaravelMysqlSpatial\Exceptions\SpatialFieldsNotDefinedException;
use Grimzy\LaravelMysqlSpatial\Exceptions\UnknownSpatialRelationFunction;
use Grimzy\LaravelMysqlSpatial\Types\Geometry;
use Grimzy\LaravelMysqlSpatial\Types\GeometryInterface;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
Expand Down Expand Up @@ -37,6 +38,17 @@ trait SpatialTrait

public $geometries = [];

protected $stRelations = [
'within',
'crosses',
'contains',
'disjoint',
'equals',
'intersects',
'overlaps',
'touches',
];

/**
* Create a new Eloquent query builder for the model.
*
Expand All @@ -49,12 +61,21 @@ public function newEloquentBuilder($query)
return new Builder($query);
}

protected function newBaseQueryBuilder()
{
$connection = $this->getConnection();

return new BaseBuilder(
$connection, $connection->getQueryGrammar(), $connection->getPostProcessor()
);
}

protected function performInsert(EloquentBuilder $query, array $options = [])
{
foreach ($this->attributes as $key => $value) {
if ($value instanceof GeometryInterface) {
$this->geometries[$key] = $value; //Preserve the geometry objects prior to the insert
$this->attributes[$key] = $this->getConnection()->raw(sprintf("GeomFromText('%s')", $value->toWKT()));
$this->attributes[$key] = new SpatialExpression($value);
}
}

Expand Down Expand Up @@ -89,30 +110,59 @@ public function getSpatialFields()
}
}

public function isColumnAllowed($geometryColumn)
{
if (!in_array($geometryColumn, $this->getSpatialFields())) {
throw new SpatialFieldsNotDefinedException();
}

return true;
}

public function scopeDistance($query, $geometryColumn, $geometry, $distance, $exclude_self = false)
{
$query->whereRaw("st_distance(`{$geometryColumn}`, GeomFromText('{$geometry->toWkt()}')) <= {$distance}");
$this->isColumnAllowed($geometryColumn);

$query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?)) <= ?", [
$geometry->toWkt(),
$distance,
]);

if ($exclude_self) {
$query->whereRaw("st_distance(`{$geometryColumn}`, GeomFromText('{$geometry->toWkt()}')) != 0");
$query->whereRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?)) != 0", [
$geometry->toWkt(),
]);
}

return $query;
}

public function scopeDistanceValue($query, $geometryColumn, $geometry)
{
$this->isColumnAllowed($geometryColumn);

$columns = $query->getQuery()->columns;

if (!$columns) {
$query->select('*');
}
$query->selectRaw("st_distance(`{$geometryColumn}`, GeomFromText('{$geometry->toWkt()}')) as distance");

$query->selectRaw("st_distance(`$geometryColumn`, ST_GeomFromText(?)) as distance", [
$geometry->toWkt(),
]);
}

public function scopeComparison($query, $geometryColumn, $geometry, $relationship)
{
$query->whereRaw("st_{$relationship}(`{$geometryColumn}`, GeomFromText('{$geometry->toWkt()}'))");
$this->isColumnAllowed($geometryColumn);

if (!in_array($relationship, $this->stRelations)) {
throw new UnknownSpatialRelationFunction($relationship);
}

$query->whereRaw("st_{$relationship}(`$geometryColumn`, ST_GeomFromText(?))", [
$geometry->toWkt(),
]);

return $query;
}
Expand Down
7 changes: 7 additions & 0 deletions src/Exceptions/UnknownSpatialRelationFunction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Grimzy\LaravelMysqlSpatial\Exceptions;

class UnknownSpatialRelationFunction extends \RuntimeException
{
}
1 change: 1 addition & 0 deletions src/MysqlConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public function __construct($pdo, $database = '', $tablePrefix = '', array $conf
'multilinestring',
'multipolygon',
'geometrycollection',
'geomcollection',
];
$dbPlatform = $this->getDoctrineSchemaManager()->getDatabasePlatform();
foreach ($geometries as $type) {
Expand Down
8 changes: 8 additions & 0 deletions tests/Integration/SpatialTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ public function createApplication()
$app['config']->set('database.connections.mysql.database', 'spatial_test');
$app['config']->set('database.connections.mysql.username', 'root');
$app['config']->set('database.connections.mysql.password', '');
$app['config']->set('database.connections.mysql.modes', [
'ONLY_FULL_GROUP_BY',
'STRICT_TRANS_TABLES',
'NO_ZERO_IN_DATE',
'NO_ZERO_DATE',
'ERROR_FOR_DIVISION_BY_ZERO',
'NO_ENGINE_SUBSTITUTION',
]);

return $app;
}
Expand Down
32 changes: 11 additions & 21 deletions tests/Unit/Eloquent/BuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use BaseTestCase;
use Grimzy\LaravelMysqlSpatial\Eloquent\Builder;
use Grimzy\LaravelMysqlSpatial\Eloquent\SpatialExpression;
use Grimzy\LaravelMysqlSpatial\Eloquent\SpatialTrait;
use Grimzy\LaravelMysqlSpatial\MysqlConnection;
use Grimzy\LaravelMysqlSpatial\Types\LineString;
Expand Down Expand Up @@ -36,50 +37,39 @@ protected function setUp()

public function testUpdatePoint()
{
$this->queryBuilder
->shouldReceive('raw')
->with("GeomFromText('POINT(2 1)')")
->once();

$point = new Point(1, 2);
$this->queryBuilder
->shouldReceive('update')
->with(['point' => new SpatialExpression($point)])
->once();

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

public function testUpdateLinestring()
{
$this->queryBuilder
->shouldReceive('raw')
->with("GeomFromText('LINESTRING(0 0,1 1,2 2)')")
->once();
$linestring = new LineString([new Point(0, 0), new Point(1, 1), new Point(2, 2)]);

$this->queryBuilder
->shouldReceive('update')
->with(['linestring' => new SpatialExpression($linestring)])
->once();

$linestring = new LineString([new Point(0, 0), new Point(1, 1), new Point(2, 2)]);

$this->builder->update(['linestring' => $linestring]);
}

public function testUpdatePolygon()
{
$this->queryBuilder
->shouldReceive('raw')
->with("GeomFromText('POLYGON((0 0,1 0),(1 0,1 1),(1 1,0 0))')")
->once();

$this->queryBuilder
->shouldReceive('update')
->once();

$linestrings[] = new LineString([new Point(0, 0), new Point(0, 1)]);
$linestrings[] = new LineString([new Point(0, 1), new Point(1, 1)]);
$linestrings[] = new LineString([new Point(1, 1), new Point(0, 0)]);
$polygon = new Polygon($linestrings);

$this->queryBuilder
->shouldReceive('update')
->with(['polygon' => new SpatialExpression($polygon)])
->once();

$this->builder->update(['polygon' => $polygon]);
}
}
Expand Down
Loading

0 comments on commit f3429eb

Please sign in to comment.