File Coverage

blib/lib/Data/Validate/OpenAPI.pm
Criterion Covered Total %
statement 58 79 73.4
branch 21 52 40.3
condition 4 12 33.3
subroutine 12 12 100.0
pod 0 2 0.0
total 95 157 60.5


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;