Skip to content

Commit

Permalink
Fix warning, rsync URL validation
Browse files Browse the repository at this point in the history
Remove the quotation marks from ` HTTP::Body '1.08'` to fix a warning
new in Perl 5.40. I apparently have a lot of code that incorrectly
quotes versions.

Add a custom validation function to the jQuery Validation plugin to
validate rsync URLs. Apparently at some point the url validator included
in jQuery Validation was limited to checking http and ftp URLs. This
prevents the mirror form from failing to submit because of an incorrect
validation error. So crib from the [url validation] to create an rsync
URL validator.

Discovered after fixing the Content Security Policy header on
manager.pgxn.org. Apparently inline JS hasn't worked since introducing
the header some time ago, because JS code in `<script>` elements was
prevented from executing. Fixing the header is what broke the mirror
form.

So add comments to the README on how to properly set the CSP header, and
how to generate the appropriate SHA-256 digests for it.

Add a hint to the README for building Compress::Raw::Lzma, which I've
had to figure out several times now. Hopefully this will save a little
time in the future.

  [url validation]: https://github.com/jquery-validation/jquery-validation/blob/master/src/additional/url2.js
  • Loading branch information
theory committed Aug 15, 2024
1 parent c87db4a commit 2e91563
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 8 deletions.
5 changes: 5 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ Revision history for Perl extension PGXN::Manager
0.32.1
- Updated `doc/spec.txt` to v1.0.1, from it new home in
https://github.com/pgxn/pgxn-meta-spec.
- Fixed import quoted version warning with Perl 5.40.
- Fixed validation of rsync URLs in the mirror editor.
- Added Content Security Policy notes to the README to ensure that
inline JavaScript works properly.
- Added a type about building Compress::Raw::Lzma to the README.

0.32.0 2024-02-17T17:25:24Z
- Combined the pgxn_consumer PID number and file location into one
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ Installation

./Build installdeps

If Compress-Raw-Lzma fails to install because it cannot find `lzma.h`,
even with `PERL_MM_OPT` and `PERL_MB_OPT` pointing to the correct
directory, set `LIBLZMA_INCLUDE` instead:

env LIBLZMA_INCLUDE=/opt/homebrew/include ./Build installdeps
# or
env LIBLZMA_INCLUDE=/opt/homebrew/include cpanm -v --notest Compress::Raw::Lzma

* Configure the PostgreSQL server to pre-load modules used by PL/Perl
functions. Just add these lines to the end of your `postgresql.conf` file:

Expand Down Expand Up @@ -230,6 +238,30 @@ Here's how to run PGXN::Manager behind a reverse proxy server:
them. Also, be sure to disable `merge_slashes` or else the mirror management
interface will not work.

* If the proxy service includes a [content security
policy](https://content-security-policy.com) header (a.k.a. CSP), it will
need the following configuration to allow any images, local JavaScript,
and specific inline JavaScript in `<script>` elements to function:

```
Content-Security-Policy: default-src 'self'; img-src *; script-src 'self' 'sha256-C3v/abgU7GuNO8EfzYDFmryoploCskBljphPWnpJ0po=' 'sha256-QELwtyyu4lxId+yoW1ljK+y138CYTwHC227Tc8LvVgQ=' 'sha256-q0V4Ot8L8YlUzZm2BytfHTK0KQLzCyqZrdSpnyAci3E=' 'sha256-RXOpCJp6UcKxmxWS3RRnMIv2fHaWnBuOwWmfvAHGnHo=' 'sha256-n9f9UVAruXN1NnhRiCfpqgcPxLngJaTutVulqcDjmr8=' 'sha256-yqqMPK8onoDpDcwg2+lqdbbOlOg8LH8MuO7NHEgua2c='
```

This header was created by the following shell script:

``` sh
printf "Content-Security-Policy: default-src 'self'; img-src *; script-src 'self' "
for js in "PGXN.validate_form('#reqform')" \
"PGXN.validate_form('#accform')" \
"PGXN.validate_form('#passform')" \
"PGXN.validate_form('#mirrorform')" \
"PGXN.init_moderate()" \
"PGXN.init_mirrors()"; do
printf "'sha256-%s' " "$(echo -n "$js" | openssl sha256 -binary | openssl base64)"
done
printf "\n"
```

* Install
[Plack::Middleware::ReverseProxy](https://metacpan.org/pod/Plack::Middleware::ReverseProxy)
from CPAN:
Expand Down
2 changes: 1 addition & 1 deletion lib/PGXN/Manager/Request.pm
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use parent 'Plack::Request';
use Plack::Response;
use HTTP::Negotiate;
use PGXN::Manager;
use HTTP::Body '1.08'; # required for proper upload mime type detection.
use HTTP::Body 1.08; # required for proper upload mime type detection.
use namespace::autoclean;
use Encode;

Expand Down
6 changes: 3 additions & 3 deletions lib/PGXN/Manager/Templates.pm
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ BEGIN { create_wrapper wrapper => sub {
}
if (my $id = $args->{validate_form}) {
script {
# https://github.com/jquery-validation/jquery-validation/releases/tag/
# https://github.com/jquery-validation/jquery-validation/releases/
type is 'text/javascript';
src is $req->uri_for('/ui/js/jquery.validate.min.js');
};
Expand Down Expand Up @@ -1295,8 +1295,8 @@ template show_mirror => sub {
[qw(location Location text), 'city, (area?, )country, continent (lon lat)', T('Where can we find this mirror, geographically speaking?'), 'required' ],
['timezone', 'TZ', 'text', 'area/Location zoneinfo tz', T('In what time zone can we find the mirror?'), 'required' ],
[qw(bandwidth Bandwidth text), '1Gbps, 100Mbps, DSL, etc.', T('How big is the pipe?'), 'required' ],
[qw(src Source url), 'rsync://from.which.host/is/this/site/mirroring/from/', T('From what source is the mirror syncing?'), 'required' ],
[qw(rsync Rsync url), 'rsync://where.your.host/is/offering/a/mirror/', T('Is there a public rsync interface from which other hosts can mirror?') ],
[qw(src Source rsync), 'rsync://from.which.host/is/this/site/mirroring/from/', T('From what source is the mirror syncing?'), 'required' ],
[qw(rsync Rsync rsync), 'rsync://where.your.host/is/offering/a/mirror/', T('Is there a public rsync interface from which other hosts can mirror?') ],
) {
label {
attr { for => $spec->[0], title => $spec->[4] };
Expand Down
4 changes: 2 additions & 2 deletions t/edit_mirror.t
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ test_psgi +PGXN::Manager::Router->app => sub {
id => 'src',
title => $mt->maketext('From what source is the mirror syncing?'),
label => $mt->maketext('Source'),
type => 'url',
type => 'rsync',
phold => 'rsync://from.which.host/is/this/site/mirroring/from/',
class => 'required',
value => 'rsync://master.pgxn.org/pgxn/',
Expand All @@ -233,7 +233,7 @@ test_psgi +PGXN::Manager::Router->app => sub {
id => 'rsync',
title => $mt->maketext('Is there a public rsync interface from which other hosts can mirror?'),
label => $mt->maketext('Rsync'),
type => 'url',
type => 'rsync',
phold => 'rsync://where.your.host/is/offering/a/mirror/',
class => '',
value => 'rsync://pgxn.kineticode.com/pgxn/',
Expand Down
4 changes: 2 additions & 2 deletions t/new_mirror.t
Original file line number Diff line number Diff line change
Expand Up @@ -184,15 +184,15 @@ test_psgi +PGXN::Manager::Router->app => sub {
id => 'src',
title => $mt->maketext('From what source is the mirror syncing?'),
label => $mt->maketext('Source'),
type => 'url',
type => 'rsync',
phold => 'rsync://from.which.host/is/this/site/mirroring/from/',
class => 'required',
},
{
id => 'rsync',
title => $mt->maketext('Is there a public rsync interface from which other hosts can mirror?'),
label => $mt->maketext('Rsync'),
type => 'url',
type => 'rsync',
phold => 'rsync://where.your.host/is/offering/a/mirror/',
class => '',
},
Expand Down
4 changes: 4 additions & 0 deletions www/ui/js/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ PGXN.init_moderate = function () {

PGXN.validate_form = function(form) {
$(document).ready(function() {
jQuery.validator.addMethod("rsync", function(value, element) {
return this.optional( element ) || /^(?:rsync:\/\/)(?:(?:[^\]\[?\/<~#`!@$^&*()+=}|:";',>{ ]|%[0-9A-Fa-f]{2})+(?::(?:[^\]\[?\/<~#`!@$^&*()+=}|:";',>{ ]|%[0-9A-Fa-f]{2})*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?)|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff])|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62}\.)))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test( value );
}, "Please specify a valid rsync URL");

$(form).validate({
errorClass: 'invalid',
wrapper: 'div',
Expand Down

0 comments on commit 2e91563

Please sign in to comment.