Skip to content

Solves sql injection vulnerability #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
},
"require-dev": {
"phpunit/phpunit": "~4.8||~5.7",
"mockery/mockery": "^0.9.9",
"mockery/mockery": "^1.1.0",
"laravel/laravel": "^5.2",
"doctrine/dbal": "^2.5",
"laravel/browser-kit-testing": "^2.0",
Expand Down
10 changes: 10 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: '3.3'
services:
db:
image: mysql:5.7
ports:
- "3306:3306"
environment:
MYSQL_DATABASE: 'spatial_test'
MYSQL_ROOT_PASSWORD: ''
MYSQL_ALLOW_EMPTY_PASSWORD: 1
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;

class BaseBuilder extends Builder
{
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("ST_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();
}
}
81 changes: 73 additions & 8 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("ST_GeomFromText('%s')", $value->toWKT()));
$this->attributes[$key] = new SpatialExpression($value);
}
}

Expand Down Expand Up @@ -89,61 +110,105 @@ 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)
{
$query->whereRaw("st_distance(`{$geometryColumn}`, ST_GeomFromText('{$geometry->toWkt()}')) <= {$distance}");
$this->isColumnAllowed($geometryColumn);

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

return $query;
}

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

$query = $this->scopeDistance($query, $geometryColumn, $geometry, $distance);

$query->whereRaw("st_distance(`{$geometryColumn}`, ST_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}`, ST_GeomFromText('{$geometry->toWkt()}')) as distance");

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

public function scopeDistanceSphere($query, $geometryColumn, $geometry, $distance)
{
$query->whereRaw("st_distance_sphere(`{$geometryColumn}`, ST_GeomFromText('{$geometry->toWkt()}')) <= {$distance}");
$this->isColumnAllowed($geometryColumn);

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

return $query;
}

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

$query = $this->scopeDistanceSphere($query, $geometryColumn, $geometry, $distance);

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

return $query;
}

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

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

if (!$columns) {
$query->select('*');
}
$query->selectRaw("st_distance_sphere(`{$geometryColumn}`, ST_GeomFromText('{$geometry->toWkt()}')) as distance");
$query->selectRaw("st_distance_sphere(`$geometryColumn`, ST_GeomFromText(?)) as distance", [
$geometry->toWkt(),
]);
}

public function scopeComparison($query, $geometryColumn, $geometry, $relationship)
{
$query->whereRaw("st_{$relationship}(`{$geometryColumn}`, ST_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
{
}