Skip to content
Open
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
11 changes: 6 additions & 5 deletions app/lib/Core/Sql/Data.pm
Original file line number Diff line number Diff line change
Expand Up @@ -757,19 +757,20 @@ sub list_for_api {
my $method = $args{admin} ? '_list' : 'list';

# Validate fields against structure to prevent SELECT injection.
# Complex expressions (containing SQL syntax like parentheses, wildcards or dots)
# are treated as trusted internal code and passed through unchanged.
# Trust is decided by the CLASS (trusted_fields_passthrough), never by the
# shape of the value itself - `fields` may come straight from an HTTP query
# string, so a class must opt in explicitly (and must guarantee it never
# forwards a client-supplied `fields` value unchanged) to bypass sanitizing.
my $fields;
if ( $args{fields} && $args{fields} ne '*' && $self->can('structure') ) {
if ( $args{fields} =~ /[().*]/ ) {
# Internal trusted SQL expression — pass through unchanged
if ( $self->can('trusted_fields_passthrough') && $self->trusted_fields_passthrough ) {
$fields = $args{fields};
} else {
my %structure = %{ $self->structure };
my @safe = grep { exists $structure{$_} } split /\s*,\s*/, $args{fields};
$fields = @safe ? join(', ', map { "`$_`" } @safe) : undef;
}
} elsif ( $args{fields} ) {
} elsif ( $args{fields} && ( $args{fields} eq '*' || !$self->can('structure') ) ) {
$fields = $args{fields};
}

Expand Down
2 changes: 2 additions & 0 deletions app/lib/Core/USObject.pm
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ sub _list {
return $self->SUPER::_list( %args );
}

sub trusted_fields_passthrough { return 1 }

sub list_for_api {
my $self = shift;
my %args = (
Expand Down