line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
use strict; |
3
|
2
|
|
|
2
|
|
970
|
use warnings; |
|
2
|
|
|
|
|
10
|
|
|
2
|
|
|
|
|
55
|
|
4
|
2
|
|
|
2
|
|
11
|
|
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
79
|
|
5
|
|
|
|
|
|
|
# ABSTRACT: Validate and untaint input parameters via OpenAPI schema |
6
|
|
|
|
|
|
|
our $VERSION = '0.1.0'; # VERSION |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
use OpenAPI::Render; |
9
|
2
|
|
|
2
|
|
856
|
use parent OpenAPI::Render::; |
|
2
|
|
|
|
|
2036
|
|
|
2
|
|
|
|
|
67
|
|
10
|
2
|
|
|
2
|
|
840
|
|
|
2
|
|
|
|
|
604
|
|
|
2
|
|
|
|
|
14
|
|
11
|
|
|
|
|
|
|
use Data::Validate qw( is_integer ); |
12
|
2
|
|
|
2
|
|
1110
|
use Data::Validate::Email qw( is_email ); |
|
2
|
|
|
|
|
130853
|
|
|
2
|
|
|
|
|
182
|
|
13
|
2
|
|
|
2
|
|
1047
|
use Data::Validate::IP qw( is_ipv4 is_ipv6 ); |
|
2
|
|
|
|
|
81485
|
|
|
2
|
|
|
|
|
141
|
|
14
|
2
|
|
|
2
|
|
1193
|
use Data::Validate::URI qw( is_uri ); |
|
2
|
|
|
|
|
70120
|
|
|
2
|
|
|
|
|
206
|
|
15
|
2
|
|
|
2
|
|
1148
|
use DateTime::Format::RFC3339; |
|
2
|
|
|
|
|
1765
|
|
|
2
|
|
|
|
|
123
|
|
16
|
2
|
|
|
2
|
|
958
|
use Scalar::Util qw( blessed ); |
|
2
|
|
|
|
|
1060064
|
|
|
2
|
|
|
|
|
108
|
|
17
|
2
|
|
|
2
|
|
28
|
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
1598
|
|
18
|
|
|
|
|
|
|
{ |
19
|
|
|
|
|
|
|
my( $self, $path, $method, $input ) = @_; |
20
|
|
|
|
|
|
|
|
21
|
7
|
|
|
7
|
0
|
30692
|
# FIXME: More specific parameters override less specific ones. |
22
|
|
|
|
|
|
|
# FIXME: Request body parameters should be taken from CGI object |
23
|
|
|
|
|
|
|
# using their own specific methods. |
24
|
|
|
|
|
|
|
my $api = $self->{api}; |
25
|
|
|
|
|
|
|
my @parameters = |
26
|
7
|
|
|
|
|
26
|
grep { $_->{in} eq 'query' } |
27
|
|
|
|
|
|
|
exists $api->{paths}{$path}{parameters} |
28
|
7
|
|
|
|
|
44
|
? @{$api->{paths}{$path}{parameters}} : (), |
29
|
|
|
|
|
|
|
exists $api->{paths}{$path}{$method}{parameters} |
30
|
0
|
|
|
|
|
0
|
? @{$api->{paths}{$path}{$method}{parameters}} : (), |
31
|
|
|
|
|
|
|
exists $api->{paths}{$path}{$method}{requestBody} |
32
|
7
|
|
|
|
|
34
|
? OpenAPI::Render::RequestBody2Parameters( $api->{paths}{$path}{$method}{requestBody} ) : (); |
33
|
|
|
|
|
|
|
|
34
|
7
|
50
|
|
|
|
37
|
my $par = {}; |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
35
|
|
|
|
|
|
|
my $par_hash = $input; |
36
|
7
|
|
|
|
|
32
|
|
37
|
7
|
|
|
|
|
16
|
if( blessed $par_hash ) { |
38
|
|
|
|
|
|
|
$par_hash = { $par_hash->Vars }; # object is assumed to be CGI |
39
|
7
|
50
|
|
|
|
30
|
} |
40
|
0
|
|
|
|
|
0
|
|
41
|
|
|
|
|
|
|
for my $description (@parameters) { |
42
|
|
|
|
|
|
|
my $name = $description->{name}; |
43
|
7
|
|
|
|
|
17
|
my $schema = $description->{schema} if $description->{schema}; |
44
|
7
|
|
|
|
|
16
|
if( !exists $par_hash->{$name} ) { |
45
|
7
|
50
|
|
|
|
24
|
if( $schema && exists $schema->{default} ) { |
46
|
7
|
100
|
|
|
|
21
|
$par->{$name} = $schema->{default}; |
47
|
1
|
50
|
33
|
|
|
8
|
} |
48
|
0
|
|
|
|
|
0
|
next; |
49
|
|
|
|
|
|
|
} |
50
|
1
|
|
|
|
|
4
|
|
51
|
|
|
|
|
|
|
if( $schema && $schema->{type} eq 'array' ) { |
52
|
|
|
|
|
|
|
my @values = grep { defined $_ } |
53
|
6
|
50
|
33
|
|
|
35
|
map { validate_value( $_, $schema ) } |
54
|
0
|
|
|
|
|
0
|
ref $par_hash->{$name} eq 'ARRAY' |
55
|
0
|
|
|
|
|
0
|
? @{$par_hash->{$name}} |
56
|
|
|
|
|
|
|
: split "\0", $par_hash->{$name}; |
57
|
0
|
|
|
|
|
0
|
$par->{$name} = \@values if @values; |
58
|
0
|
0
|
|
|
|
0
|
} else { |
59
|
0
|
0
|
|
|
|
0
|
my $value = validate_value( $par_hash->{$name}, $schema ); |
60
|
|
|
|
|
|
|
$par->{$name} = $value if defined $value; |
61
|
6
|
|
|
|
|
20
|
} |
62
|
6
|
100
|
|
|
|
29
|
} |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
return $par; |
65
|
|
|
|
|
|
|
} |
66
|
7
|
|
|
|
|
24
|
|
67
|
|
|
|
|
|
|
{ |
68
|
|
|
|
|
|
|
my( $value, $schema ) = @_; |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
my $format = $schema->{format} if $schema; |
71
|
6
|
|
|
6
|
0
|
38
|
|
72
|
|
|
|
|
|
|
# FIXME: Maybe employ a proper JSON Schema validator? Not sure |
73
|
6
|
50
|
|
|
|
23
|
# if it untaints, though. |
74
|
|
|
|
|
|
|
if( !defined $format ) { |
75
|
|
|
|
|
|
|
# nothing to do here |
76
|
|
|
|
|
|
|
} elsif( $format eq 'date-time' ) { |
77
|
6
|
50
|
|
|
|
31
|
my $parser = DateTime::Format::RFC3339->new; |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
78
|
|
|
|
|
|
|
$value = $parser->format_datetime( $parser->parse_datetime( $value ) ); |
79
|
|
|
|
|
|
|
} elsif( $format eq 'email' ) { |
80
|
1
|
|
|
|
|
11
|
$value = is_email $value; |
81
|
1
|
|
|
|
|
16
|
} elsif( $format eq 'integer' ) { |
82
|
|
|
|
|
|
|
$value = is_integer $value; |
83
|
0
|
|
|
|
|
0
|
} elsif( $format eq 'ipv4' ) { |
84
|
|
|
|
|
|
|
$value = is_ipv4 $value; |
85
|
5
|
|
|
|
|
131
|
} elsif( $format eq 'ipv6' ) { |
86
|
|
|
|
|
|
|
$value = is_ipv6 $value; |
87
|
0
|
|
|
|
|
0
|
} elsif( $format eq 'uri' ) { |
88
|
|
|
|
|
|
|
$value = is_uri $value; |
89
|
0
|
|
|
|
|
0
|
} elsif( $format eq 'uuid' ) { |
90
|
|
|
|
|
|
|
# Regex taken from Data::Validate::UUID. Module is not used as |
91
|
0
|
|
|
|
|
0
|
# it does not untaint the value. |
92
|
|
|
|
|
|
|
if( $value =~ /^([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/i ) { |
93
|
|
|
|
|
|
|
$value = $1; |
94
|
|
|
|
|
|
|
} else { |
95
|
0
|
0
|
|
|
|
0
|
return; |
96
|
0
|
|
|
|
|
0
|
} |
97
|
|
|
|
|
|
|
} |
98
|
0
|
|
|
|
|
0
|
|
99
|
|
|
|
|
|
|
return unless defined $value; |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
if( $schema && $schema->{enum} ) { |
102
|
6
|
100
|
|
|
|
2545
|
( $value ) = grep { $value eq $_ } @{$schema->{enum}}; |
103
|
|
|
|
|
|
|
return unless defined $value; |
104
|
4
|
50
|
33
|
|
|
29
|
} |
105
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
106
|
0
|
0
|
|
|
|
0
|
if( $schema && $schema->{pattern} ) { |
107
|
|
|
|
|
|
|
return unless $value =~ /^($schema->{pattern})$/; |
108
|
|
|
|
|
|
|
$value = $1; |
109
|
4
|
50
|
33
|
|
|
23
|
} |
110
|
0
|
0
|
|
|
|
0
|
|
111
|
0
|
|
|
|
|
0
|
## Not sure this is appropriate here |
112
|
|
|
|
|
|
|
# if( defined $value && $value eq '' && |
113
|
|
|
|
|
|
|
# ( !exists $description->{allowEmptyValue} || |
114
|
|
|
|
|
|
|
# $description->{allowEmptyValue} eq 'false' ) ) { |
115
|
|
|
|
|
|
|
# return; # nothing to do |
116
|
|
|
|
|
|
|
# } |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
return $value; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
4
|
|
|
|
|
13
|
1; |