X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FSQL%2FAbstract.pm;h=9e5bb5239e081905aec8663bce68e8a87e7c7d08;hb=3d86e3b17c0fc37a393b6b91936da222128ded56;hp=4802d4c770f2dd107c57acf1c5ed289e8d2c7e5a;hpb=48d9f5f846e7f9e8f51110ad418a3bb4b50c31f9;p=dbsrgits%2FSQL-Abstract.git
diff --git a/lib/SQL/Abstract.pm b/lib/SQL/Abstract.pm
index 4802d4c..9e5bb52 100644
--- a/lib/SQL/Abstract.pm
+++ b/lib/SQL/Abstract.pm
@@ -15,7 +15,7 @@ use Scalar::Util ();
# GLOBALS
#======================================================================
-our $VERSION = '1.71';
+our $VERSION = '1.72';
# This would confuse some packagers
$VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases
@@ -93,16 +93,40 @@ sub new {
# special operators
$opt{special_ops} ||= [];
+ # regexes are applied in order, thus push after user-defines
push @{$opt{special_ops}}, @BUILTIN_SPECIAL_OPS;
# unary operators
$opt{unary_ops} ||= [];
push @{$opt{unary_ops}}, @BUILTIN_UNARY_OPS;
+ # rudimentary saniy-check for user supplied bits treated as functions/operators
+ # If a purported function matches this regular expression, an exception is thrown.
+ # Literal SQL is *NOT* subject to this check, only functions (and column names
+ # when quoting is not in effect)
+
+ # FIXME
+ # need to guard against ()'s in column names too, but this will break tons of
+ # hacks... ideas anyone?
+ $opt{injection_guard} ||= qr/
+ \;
+ |
+ ^ \s* go \s
+ /xmi;
+
return bless \%opt, $class;
}
+sub _assert_pass_injection_guard {
+ if ($_[1] =~ $_[0]->{injection_guard}) {
+ my $class = ref $_[0];
+ puke "Possible SQL injection attempt '$_[1]'. If this is indeed a part of the "
+ . "desired SQL use literal SQL ( \'...' or \[ '...' ] ) or supply your own "
+ . "{injection_guard} attribute to ${class}->new()"
+ }
+}
+
#======================================================================
# INSERT methods
@@ -532,7 +556,9 @@ sub _where_unary_op {
}
}
- $self->debug("Generic unary OP: $op - recursing as function");
+ $self->_debug("Generic unary OP: $op - recursing as function");
+
+ $self->_assert_pass_injection_guard($op);
my ($sql, @bind) = $self->_SWITCH_refkind ($rhs, {
SCALAR => sub {
@@ -573,7 +599,7 @@ sub _where_op_ANDOR {
SCALARREF => sub {
puke "-$op => \\\$scalar makes little sense, use " .
- ($op =~ /^or/i
+ ($op =~ /^or/i
? '[ \$scalar, \%rest_of_conditions ] instead'
: '-and => [ \$scalar, \%rest_of_conditions ] instead'
);
@@ -581,7 +607,7 @@ sub _where_op_ANDOR {
ARRAYREFREF => sub {
puke "-$op => \\[...] makes little sense, use " .
- ($op =~ /^or/i
+ ($op =~ /^or/i
? '[ \[...], \%rest_of_conditions ] instead'
: '-and => [ \[...], \%rest_of_conditions ] instead'
);
@@ -692,6 +718,8 @@ sub _where_hashpair_HASHREF {
$op =~ s/^\s+|\s+$//g;# remove leading/trailing space
$op =~ s/\s+/ /g; # compress whitespace
+ $self->_assert_pass_injection_guard($op);
+
# so that -not_foo works correctly
$op =~ s/^not_/NOT /i;
@@ -1120,7 +1148,6 @@ sub _table {
ARRAYREF => sub {join ', ', map { $self->_quote($_) } @$from;},
SCALAR => sub {$self->_quote($from)},
SCALARREF => sub {$$from},
- ARRAYREFREF => sub {join ', ', @$from;},
});
}
@@ -1136,7 +1163,10 @@ sub _quote {
return '' unless defined $_[1];
return ${$_[1]} if ref($_[1]) eq 'SCALAR';
- return $_[1] unless $_[0]->{quote_char};
+ unless ($_[0]->{quote_char}) {
+ $_[0]->_assert_pass_injection_guard($_[1]);
+ return $_[1];
+ }
my $qref = ref $_[0]->{quote_char};
my ($l, $r);
@@ -1760,6 +1790,20 @@ so that tables and column names can be individually quoted like this:
SELECT `table`.`one_field` FROM `table` WHERE `table`.`other_field` = 1
+=item injection_guard
+
+A regular expression C that is applied to any C<-function> and unquoted
+column name specified in a query structure. This is a safety mechanism to avoid
+injection attacks when mishandling user input e.g.:
+
+ my %condition_as_column_value_pairs = get_values_from_user();
+ $sqla->select( ... , \%condition_as_column_value_pairs );
+
+If the expression matches an exception is thrown. Note that literal SQL
+supplied via C<\'...'> or C<\['...']> is B checked in any way.
+
+Defaults to checking for C<;> and the C keyword (TransactSQL)
+
=item array_datatypes
When this option is true, arrayrefs in INSERT or UPDATE are
@@ -2031,13 +2075,13 @@ To get an OR instead, you can combine it with the arrayref idea:
my %where => (
user => 'nwiger',
- priority => [ {'=', 2}, {'!=', 1} ]
+ priority => [ { '=', 2 }, { '>', 5 } ]
);
Which would generate:
- $stmt = "WHERE user = ? AND priority = ? OR priority != ?";
- @bind = ('nwiger', '2', '1');
+ $stmt = "WHERE ( priority = ? OR priority > ? ) AND user = ?";
+ @bind = ('2', '5', 'nwiger');
If you want to include literal SQL (with or without bind values), just use a
scalar reference or array reference as the value:
@@ -2695,9 +2739,9 @@ apps in under 50 lines.
=over
-=item * gitweb: L
+=item * gitweb: L
-=item * git: L
+=item * git: L
=back