Skip to content

Commit

Permalink
Merge pull request #32 from hind-sagar-biswas:dev
Browse files Browse the repository at this point in the history
Restricted keywords sanitization
  • Loading branch information
hind-sagar-biswas committed Apr 21, 2024
2 parents 994d4cd + 4d9b2fd commit cbb3162
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 9 deletions.
7 changes: 4 additions & 3 deletions src/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,16 @@ public static function delete(string $table): QueryDelete
// Flatten an array or string by separating elements with a comma
public static function flattenByComma(array|string $data): string
{
if (!is_array($data)) return $data;
return implode(', ', $data);
if (!is_array($data)) return SanitizeWord::run($data);
return implode(', ', array_map(fn (string $d): string => SanitizeWord::run($d), $data));
}

// Flatten an associative array for SET clauses in SQL UPDATE statements
public static function flattenByEqual(array $data): string
{
$parsed_data = array_map(
fn (string $k, $d): string => ($d === null) ? "$k = NULL" : "$k = '$d'",
array_keys($data),
array_map(fn (string $d): string => SanitizeWord::run($d), array_keys($data)),
array_values($data)
);
return implode(', ', $parsed_data);
Expand All @@ -81,6 +81,7 @@ public static function buildCondition(
string $comparison = '=',
int|string|null|bool $secondValue = null
): string {
$column = SanitizeWord::run($column);
$sqlComp = match ($comparison) {
'gt', '>' => '>',
'gte', '>=' => '>=',
Expand Down
7 changes: 4 additions & 3 deletions src/Query/QueryInsert.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Hindbiswas\QueBee\Query;

use Hindbiswas\QueBee\Query;
use Hindbiswas\QueBee\SanitizeWord;
use Hindbiswas\QueBee\Query\QueryStruct;

class QueryInsert implements QueryStruct
Expand Down Expand Up @@ -56,14 +57,14 @@ public function build(): string
$sql = 'INSERT INTO ';

if ($this->multiple) {
$columns = array_keys($this->data[0]);
$values = array_map(function ($d) { return Query::flattenForValues($d); }, $this->data);
$columns = array_map(fn ($d) =>SanitizeWord::run($d), array_keys($this->data[0]));
$values = array_map(fn ($d) => Query::flattenForValues($d), $this->data);

// Construct the SQL query for multiple data insertion
$sql .= $this->table . '(' . implode(', ', $columns) . ') VALUES ';
$sql .= implode(', ', $values) . ';';
} else {
$columns = array_keys($this->data);
$columns = array_map(fn ($d) =>SanitizeWord::run($d), array_keys($this->data));
$values = Query::flattenForValues($this->data);

// Construct the SQL query for single data insertion
Expand Down
7 changes: 5 additions & 2 deletions src/Query/QuerySelect.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

namespace Hindbiswas\QueBee\Query;

use Hindbiswas\QueBee\Clause\GroupClause;
use Hindbiswas\QueBee\SanitizeWord;
use Hindbiswas\QueBee\Clause\JoinClause;
use Hindbiswas\QueBee\Query\QueryStruct;
use Hindbiswas\QueBee\Clause\GroupClause;
use Hindbiswas\QueBee\Clause\LimitClause;
use Hindbiswas\QueBee\Clause\OrderClause;
use Hindbiswas\QueBee\Clause\WhereClause;
use Hindbiswas\QueBee\Query\QueryStruct;

class QuerySelect implements QueryStruct
{
Expand All @@ -29,6 +30,8 @@ public function __construct(array $columns)
if (!array_is_list($columns)) {
$columnStrings = [];
foreach ($columns as $alias => $columnName) {
$columnName = SanitizeWord::run($columnName);
if (!is_numeric($alias)) $alias = SanitizeWord::run($alias);
$columnStrings[] = ($alias !== $columnName and !is_numeric($alias)) ? "$columnName AS $alias" : $columnName;
}
$columns = $columnStrings;
Expand Down
244 changes: 244 additions & 0 deletions src/SanitizeWord.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
<?php

declare(strict_types=1);

namespace Hindbiswas\QueBee;

class SanitizeWord
{
public static function sanitize(string $w): string
{
$reservedKeywords = [
'ADD',
'ALL',
'ALTER',
'ANALYZE',
'AND',
'AS',
'ASC',
'BEFORE',
'BETWEEN',
'BY',
'CALL',
'CASCADE',
'CASE',
'CHANGE',
'CHECK',
'COLLATE',
'COLUMN',
'CONDITION',
'CONSTRAINT',
'CONTINUE',
'CONVERT',
'CREATE',
'CROSS',
'CURRENT_DATE',
'CURRENT_TIME',
'CURRENT_TIMESTAMP',
'CURRENT_USER',
'CURSOR',
'DATABASE',
'DATABASES',
'DAY_HOUR',
'DAY_MICROSECOND',
'DAY_MINUTE',
'DAY_SECOND',
'DEC',
'DECIMAL',
'DECLARE',
'DEFAULT',
'DELAYED',
'DELETE',
'DESC',
'DESCRIBE',
'DETERMINISTIC',
'DISTINCT',
'DISTINCTROW',
'DIV',
'DOUBLE',
'DROP',
'DUAL',
'EACH',
'ELSE',
'ELSEIF',
'ENCLOSED',
'ESCAPED',
'EXISTS',
'EXIT',
'EXPLAIN',
'FALSE',
'FETCH',
'FLOAT',
'FLOAT4',
'FLOAT8',
'FOR',
'FORCE',
'FOREIGN',
'FROM',
'FULLTEXT',
'GOTO',
'GRANT',
'GROUP',
'HAVING',
'HIGH_PRIORITY',
'HOUR_MICROSECOND',
'HOUR_MINUTE',
'HOUR_SECOND',
'IF',
'IGNORE',
'IN',
'INDEX',
'INFILE',
'INNER',
'INOUT',
'INSENSITIVE',
'INSERT',
'INT',
'INT1',
'INT2',
'INT3',
'INT4',
'INT8',
'INTEGER',
'INTERVAL',
'INTO',
'IS',
'ITERATE',
'JOIN',
'KEY',
'KEYS',
'KILL',
'LEADING',
'LEAVE',
'LEFT',
'LIKE',
'LIMIT',
'LINEAR',
'LINES',
'LOAD',
'LOCALTIME',
'LOCALTIMESTAMP',
'LOCK',
'LONG',
'LONGBLOB',
'LONGTEXT',
'LOOP',
'LOW_PRIORITY',
'MASTER_SSL_VERIFY_SERVER_CERT',
'MATCH',
'MAXVALUE',
'MEDIUMBLOB',
'MEDIUMINT',
'MEDIUMTEXT',
'MIDDLEINT',
'MINUTE_MICROSECOND',
'MINUTE_SECOND',
'MOD',
'MODIFIES',
'NATURAL',
'NOT',
'NO_WRITE_TO_BINLOG',
'NULL',
'NUMERIC',
'ON',
'OPTIMIZE',
'OPTION',
'OPTIONALLY',
'OR',
'ORDER',
'OUT',
'OUTER',
'OUTFILE',
'PRECISION',
'PRIMARY',
'PROCEDURE',
'PURGE',
'RANGE',
'RANK',
'READ',
'READS',
'READ_WRITE',
'REAL',
'REFERENCES',
'REGEXP',
'RELEASE',
'RENAME',
'REPEAT',
'REPLACE',
'REQUIRE',
'RESTRICT',
'RETURN',
'REVOKE',
'RIGHT',
'RLIKE',
'SCHEMA',
'SCHEMAS',
'SECOND_MICROSECOND',
'SELECT',
'SENSITIVE',
'SEPARATOR',
'SET',
'SHOW',
'SMALLINT',
'SPATIAL',
'SPECIFIC',
'SQL',
'SQLEXCEPTION',
'SQLSTATE',
'SQLWARNING',
'SQL_BIG_RESULT',
'SQL_CALC_FOUND_ROWS',
'SQL_SMALL_RESULT',
'SSL',
'STARTING',
'STRAIGHT_JOIN',
'TABLE',
'TERMINATED',
'THEN',
'TINYBLOB',
'TINYINT',
'TINYTEXT',
'TO',
'TRAILING',
'TRIGGER',
'TRUE',
'UNDO',
'UNION',
'UNIQUE',
'UNLOCK',
'UNSIGNED',
'UPDATE',
'USAGE',
'USE',
'USING',
'UTC_DATE',
'UTC_TIME',
'UTC_TIMESTAMP',
'VALUES',
'VARBINARY',
'VARCHAR',
'VARCHARACTER',
'VARYING',
'WHEN',
'WHERE',
'WHILE',
'WITH',
'WRITE',
'XOR',
'YEAR_MONTH',
'ZEROFILL'
];

return in_array(strtoupper($w), $reservedKeywords) ? "`$w`" : $w;
}

public static function run(string $word): string
{
if (strpos($word, '.')) {
$words = explode('.', $word);
$words = array_map(fn (string $w): string => self::sanitize($w), $words);
return implode('.', $words);
}
return self::sanitize($word);
}
}
3 changes: 3 additions & 0 deletions src/Stmt/CubeStmt.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Hindbiswas\QueBee\Stmt;

use Hindbiswas\QueBee\SanitizeWord;
use Hindbiswas\QueBee\Stmt\StmtStruct;

class CubeStmt implements StmtStruct
Expand All @@ -15,6 +16,8 @@ public function __construct(string ...$columns)
if (!array_is_list($columns)) {
$columnStrings = [];
foreach ($columns as $alias => $columnName) {
$columnName = SanitizeWord::run($columnName);
if (!is_numeric($alias)) $alias = SanitizeWord::run($alias);
$columnStrings[] = "$columnName AS $alias";
}
$columns = $columnStrings;
Expand Down
3 changes: 2 additions & 1 deletion src/Stmt/Set.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Hindbiswas\QueBee\Stmt;

use Hindbiswas\QueBee\SanitizeWord;
use Hindbiswas\QueBee\Stmt\StmtStruct;

class Set implements StmtStruct
Expand All @@ -12,7 +13,7 @@ class Set implements StmtStruct

public function __construct(string ...$columns)
{
$this->columns = implode(', ', $columns);
$this->columns = implode(', ', array_map(fn (string $c): string => SanitizeWord::run($c), $columns));
}

public function build(): string
Expand Down
10 changes: 10 additions & 0 deletions tests/Query/InsertQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ public function test_multiple_insert_query_build()
$expected = "INSERT INTO users(id, name, age) VALUES ('1', 'John', '30');";
$query = Query::insertMultiple($data)->into('users')->build();
$this->assertSame($expected, $query);

// For restricted keys
$expected = "INSERT INTO office(name, `rank`, phone, types) VALUES ('Liza', 'COMPUTER TECHNICIAN', '01406727506', 'lab');";
$query = Query::insert([
'name' => 'Liza',
'rank' => 'COMPUTER TECHNICIAN',
'phone' => '01406727506',
'types' => 'lab',
])->into('office')->build();
$this->assertSame($expected, $query);
}

// Test method to ensure an exception is thrown when trying to insert empty data
Expand Down

0 comments on commit cbb3162

Please sign in to comment.