diff options
Diffstat (limited to 'Bugzilla/CGI.pm')
-rw-r--r-- | Bugzilla/CGI.pm | 88 |
1 files changed, 78 insertions, 10 deletions
diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm index e44907161..78987ab71 100644 --- a/Bugzilla/CGI.pm +++ b/Bugzilla/CGI.pm @@ -31,6 +31,15 @@ BEGIN { *AUTOLOAD = \&CGI::AUTOLOAD; } +use constant DEFAULT_CSP => ( + default_src => [ 'self' ], + script_src => [ 'self', 'https://login.persona.org', 'unsafe-inline', 'unsafe-eval' ], + child_src => [ 'self', 'https://login.persona.org' ], + img_src => [ 'self', 'https://secure.gravatar.com' ], + style_src => [ 'self', 'unsafe-inline' ], + disable => 1, +); + sub _init_bz_cgi_globals { my $invocant = shift; # We need to disable output buffering - see bug 179174 @@ -130,6 +139,38 @@ sub target_uri { } } +sub content_security_policy { + my ($self, %add_params) = @_; + if (Bugzilla->has_feature('csp')) { + require Bugzilla::CGI::ContentSecurityPolicy; + return $self->{Bugzilla_csp} if $self->{Bugzilla_csp}; + my %params = DEFAULT_CSP; + if (%add_params) { + foreach my $key (keys %add_params) { + if (defined $add_params{$key}) { + $params{$key} = $add_params{$key}; + } + else { + delete $params{$key}; + } + } + } + return $self->{Bugzilla_csp} = Bugzilla::CGI::ContentSecurityPolicy->new(%params); + } + return undef; +} + +sub csp_nonce { + my ($self) = @_; + + if (Bugzilla->has_feature('csp')) { + my $csp = $self->content_security_policy; + return $csp->nonce if $csp->has_nonce; + } + + return ''; +} + # We want this sorted plus the ability to exclude certain params sub canonicalise_query { my ($self, @exclude) = @_; @@ -339,12 +380,20 @@ sub close_standby_message { # Override header so we can add the cookies in sub header { my $self = shift; + + my %headers; my $user = Bugzilla->user; # If there's only one parameter, then it's a Content-Type. if (scalar(@_) == 1) { - # Since we're adding parameters below, we have to name it. - unshift(@_, '-type' => shift(@_)); + %headers = ('-type' => shift(@_)); + } + else { + %headers = @_; + } + + if ($self->{'_content_disp'}) { + $headers{'-content_disposition'} = $self->{'_content_disp'}; } if (!$user->id && $user->authorizer->can_login @@ -368,10 +417,9 @@ sub header { -value => Bugzilla->github_secret, -httponly => 1); } - # Add the cookies in if we have any if (scalar(@{$self->{Bugzilla_cookie_list}})) { - unshift(@_, '-cookie' => $self->{Bugzilla_cookie_list}); + $headers{'-cookie'} = $self->{Bugzilla_cookie_list}; } # Add Strict-Transport-Security (STS) header if this response @@ -385,28 +433,36 @@ sub header { { $sts_opts .= '; includeSubDomains'; } - unshift(@_, '-strict_transport_security' => $sts_opts); + $headers{'-strict_transport_security'} = $sts_opts; } # Add X-Frame-Options header to prevent framing and subsequent # possible clickjacking problems. unless ($self->url_is_attachment_base) { - unshift(@_, '-x_frame_options' => 'SAMEORIGIN'); + $headers{'-x_frame_options'} = 'SAMEORIGIN'; } if ($self->{'_content_disp'}) { - unshift(@_, '-content_disposition' => $self->{'_content_disp'}); + $headers{'-content_disposition'} = $self->{'_content_disp'}; } # Add X-XSS-Protection header to prevent simple XSS attacks # and enforce the blocking (rather than the rewriting) mode. - unshift(@_, '-x_xss_protection' => '1; mode=block'); + $headers{'-x_xss_protection'} = '1; mode=block'; # Add X-Content-Type-Options header to prevent browsers sniffing # the MIME type away from the declared Content-Type. - unshift(@_, '-x_content_type_options' => 'nosniff'); + $headers{'-x_content_type_options'} = 'nosniff'; + + my $csp = $self->content_security_policy; + $csp->add_cgi_headers(\%headers) if defined $csp; - return $self->SUPER::header(@_) || ""; + Bugzilla::Hook::process('cgi_headers', + { cgi => $self, headers => \%headers } + ); + $self->{_header_done} = 1; + + return $self->SUPER::header(%headers) || ""; } sub param { @@ -731,6 +787,18 @@ correctly, using C<print> or the mod_perl APIs as appropriate. To remove (expire) a cookie, use C<remove_cookie>. +=item C<content_security_policy> + +Set a Content Security Policy for the current request. This is a no-op if the 'csp' feature +is not available. The arguments to this method are passed to the constructor of L<Bugzilla::CGI::ContentSecurityPolicy>, +consult that module for a list of what directives are supported. + +=item C<csp_nonce> + +Returns a CSP nonce value if CSP is available and 'nonce' is listed as a source in a CSP *_src directive. + +If there is no nonce used, or CSP is not available, this returns the empty string. + =item C<remove_cookie> This is a wrapper around send_cookie, setting an expiry date in the past, |