]> Sergey Matveev's repositories - public-inbox.git/commitdiff
Merge branch 'charclass'
authorEric Wong <e@80x24.org>
Tue, 4 Jun 2019 10:38:20 +0000 (10:38 +0000)
committerEric Wong <e@80x24.org>
Tue, 4 Jun 2019 10:38:20 +0000 (10:38 +0000)
* charclass: (24 commits)
  www: require ASCII word characters for CSS filenames
  www: require ASCII range for mbox downloads
  githttpbackend: require ASCII in path
  require ASCII digits for local FS items
  www: require ASCII digit for git epoch
  solver|viewdiff: restrict digit matches to ASCII
  inbox: require ASCII digits for feedmax var
  filter/rubylang: require ASCII digit for mailcount
  msgtime: require ASCII digits for parsing dates
  searchview: do not allow non-ASCII offsets and limits
  githttpbackend: require Range:, Status: to be ASCII digits
  view: require YYYYmmDD(HHMMSS) timestamps to be ASCII
  newswww: only accept ASCII digits as article numbers
  config: do not accept non-ASCII digits in cgitrc params
  www: require ASCII filenames in git blob downloads
  www: only emit ASCII chars in attachment filenames
  wwwattach: only pass the charset through if ASCII
  wwwlisting: require ASCII digit for port number
  http: require SERVER_PORT to be ASCII digit
  feed: only accept ASCII digits for ref~$N
  ...

24 files changed:
lib/PublicInbox/Config.pm
lib/PublicInbox/Feed.pm
lib/PublicInbox/Filter/RubyLang.pm
lib/PublicInbox/GitHTTPBackend.pm
lib/PublicInbox/HTTP.pm
lib/PublicInbox/Hval.pm
lib/PublicInbox/Inbox.pm
lib/PublicInbox/Linkify.pm
lib/PublicInbox/MID.pm
lib/PublicInbox/MsgTime.pm
lib/PublicInbox/NNTP.pm
lib/PublicInbox/NewsWWW.pm
lib/PublicInbox/Search.pm
lib/PublicInbox/SearchView.pm
lib/PublicInbox/SolverGit.pm
lib/PublicInbox/V2Writable.pm
lib/PublicInbox/View.pm
lib/PublicInbox/ViewDiff.pm
lib/PublicInbox/WWW.pm
lib/PublicInbox/WwwAttach.pm
lib/PublicInbox/WwwListing.pm
lib/PublicInbox/Xapcmd.pm
script/public-inbox-purge
t/linkify.t

index 09f9179b085a4dd90aed20e0d8b277ede66cc870..6e85750a7832fc927631847199fd5d447d76574a 100644 (file)
@@ -307,7 +307,7 @@ sub parse_cgitrc {
                        }
                } elsif (m!\Ainclude=(.+)\z!) {
                        parse_cgitrc($self, $1, $nesting + 1);
-               } elsif (m!\A(scan-hidden-path|remove-suffix)=(\d+)\z!) {
+               } elsif (m!\A(scan-hidden-path|remove-suffix)=([0-9]+)\z!) {
                        my ($k, $v) = ($1, $2);
                        $k =~ tr/-/_/;
                        $self->{"-cgit_$k"} = $v;
index a04838a1a608948cbc096af61413337bd0f9444e..ae07189504327530290eb986bede736ae25c25cd 100644 (file)
@@ -102,7 +102,7 @@ sub recent_msgs {
        my $hex = '[a-f0-9]';
        my $addmsg = qr!^:000000 100644 \S+ (\S+) A\t${hex}{2}/${hex}{38}$!;
        my $delmsg = qr!^:100644 000000 (\S+) \S+ D\t(${hex}{2}/${hex}{38})$!;
-       my $refhex = qr/(?:HEAD|${hex}{4,40})(?:~\d+)?/;
+       my $refhex = qr/(?:HEAD|${hex}{4,40})(?:~[0-9]+)?/;
 
        # revision ranges may be specified
        my $range = 'HEAD';
index a43d67a9352e4ea6ab71aad954b3fe44933d55e3..d40705b74e0757f4772b399da73a3c66bda8c55a 100644 (file)
@@ -50,7 +50,7 @@ sub scrub {
                my @v = $hdr->header_raw('X-Mail-Count');
                my $n;
                foreach (@v) {
-                       /\A\s*(\d+)\s*\z/ or next;
+                       /\A\s*([0-9]+)\s*\z/ or next;
                        $n = $1;
                        last;
                }
index 09411048db25a8d4fcb41237d4285fa46abd9960..a2a81f8ea66a2dbe3ed81f885a5fda945e71bb38 100644 (file)
@@ -51,8 +51,8 @@ sub serve {
 
        # Documentation/technical/http-protocol.txt in git.git
        # requires one and exactly one query parameter:
-       if ($env->{QUERY_STRING} =~ /\Aservice=git-\w+-pack\z/ ||
-                               $path =~ /\Agit-\w+-pack\z/) {
+       if ($env->{QUERY_STRING} =~ /\Aservice=git-[A-Za-z0-9_]+-pack\z/ ||
+                               $path =~ /\Agit-[A-Za-z0-9_]+-pack\z/) {
                my $ok = serve_smart($env, $git, $path);
                return $ok if $ok;
        }
@@ -90,7 +90,7 @@ sub static_result ($$$$) {
        my $len = $size;
        my $code = 200;
        push @$h, 'Content-Type', $type;
-       if (($env->{HTTP_RANGE} || '') =~ /\bbytes=(\d*)-(\d*)\z/) {
+       if (($env->{HTTP_RANGE} || '') =~ /\bbytes=([0-9]*)-([0-9]*)\z/) {
                ($code, $len) = prepare_range($env, $in, $h, $1, $2, $size);
                if ($code == 416) {
                        push @$h, 'Content-Range', "bytes */$size";
@@ -260,7 +260,7 @@ sub parse_cgi_headers {
        foreach my $l (split(/\r?\n/, $h)) {
                my ($k, $v) = split(/:\s*/, $l, 2);
                if ($k =~ /\AStatus\z/i) {
-                       ($code) = ($v =~ /\b(\d+)\b/);
+                       ($code) = ($v =~ /\b([0-9]+)\b/);
                } else {
                        push @h, $k, $v;
                }
index 10e6d6a43b518115c7eece8a5f11e67ebe613fb2..977614b489d80143c75ebe366513194e5d4977cb 100644 (file)
@@ -142,7 +142,7 @@ sub app_dispatch {
        $env->{REMOTE_ADDR} = $self->{remote_addr};
        $env->{REMOTE_PORT} = $self->{remote_port};
        if (my $host = $env->{HTTP_HOST}) {
-               $host =~ s/:(\d+)\z// and $env->{SERVER_PORT} = $1;
+               $host =~ s/:([0-9]+)\z// and $env->{SERVER_PORT} = $1;
                $env->{SERVER_NAME} = $host;
        }
        if (defined $input) {
index 95a0f7095b59f2a6683b3921f06cfb07b13939f0..2b4439703f59b692e9c6c80a628c809185c103fa 100644 (file)
@@ -13,6 +13,9 @@ our @EXPORT_OK = qw/ascii_html obfuscate_addrs to_filename src_escape
                to_attr from_attr/;
 my $enc_ascii = find_encoding('us-ascii');
 
+# safe-ish acceptable filename pattern for portability
+our $FN = '[a-zA-Z0-9][a-zA-Z0-9_\-\.]+[a-zA-Z0-9]'; # needs \z anchor
+
 sub new {
        my ($class, $raw, $href) = @_;
 
index b3178b987a7a031dc96d134fa7be13423fda1866..c93303322194e024b8125b8d8b64ca014fe00483 100644 (file)
@@ -74,7 +74,7 @@ sub _set_uint ($$$) {
        my $val = $opts->{$field};
        if (defined $val) {
                $val = $val->[-1] if ref($val) eq 'ARRAY';
-               $val = undef if $val !~ /\A\d+\z/;
+               $val = undef if $val !~ /\A[0-9]+\z/;
        }
        $opts->{$field} = $val || $default;
 }
@@ -87,7 +87,7 @@ sub _set_limiter ($$$) {
                my $mkey = $pfx.'max';
                my $val = $self->{$mkey} or return;
                my $lim;
-               if ($val =~ /\A\d+\z/) {
+               if ($val =~ /\A[0-9]+\z/) {
                        require PublicInbox::Qspawn;
                        $lim = PublicInbox::Qspawn::Limiter->new($val);
                } elsif ($val =~ /\A[a-z][a-z0-9]*\z/) {
@@ -161,7 +161,7 @@ sub max_git_part {
                if (opendir my $dh, $gits) {
                        my $max = -1;
                        while (defined(my $git_dir = readdir($dh))) {
-                               $git_dir =~ m!\A(\d+)\.git\z! or next;
+                               $git_dir =~ m!\A([0-9]+)\.git\z! or next;
                                $max = $1 if $1 > $max;
                        }
                        $part = $self->{-max_git_part} = $max if $max >= 0;
index d4778e7de371ecc5c3059363bb0d0ec1cc334686..84960a98889fd4c53cc68aa5fce72eac1094084f 100644 (file)
@@ -13,6 +13,7 @@ package PublicInbox::Linkify;
 use strict;
 use warnings;
 use Digest::SHA qw/sha1_hex/;
+use PublicInbox::Hval qw(ascii_html);
 
 my $SALT = rand;
 my $LINK_RE = qr{([\('!])?\b((?:ftps?|https?|nntps?|gopher)://
@@ -61,12 +62,12 @@ sub linkify_1 {
                        $end = ')';
                }
 
+               $url = ascii_html($url); # for IDN
+
                # salt this, as this could be exploited to show
                # links in the HTML which don't show up in the raw mail.
                my $key = sha1_hex($url . $SALT);
 
-               # only escape ampersands, others do not match LINK_RE
-               $url =~ s/&/&#38;/g;
                $_[0]->{$key} = $url;
                $beg . 'PI-LINK-'. $key . $end;
        ^ge;
index 7f1ab15ea731db8f0f70304662afe338754b5c6c..6904d61a0303bb0edd2b17b479c85e6bd13e76ff 100644 (file)
@@ -26,11 +26,11 @@ sub mid_clean {
        $mid;
 }
 
-# this is idempotent
+# this is idempotent, used for HTML anchor/ids and such
 sub id_compress {
        my ($id, $force) = @_;
 
-       if ($force || $id =~ /[^\w\-]/ || length($id) > MID_MAX) {
+       if ($force || $id =~ /[^a-zA-Z0-9_\-]/ || length($id) > MID_MAX) {
                utf8::encode($id);
                return sha1_hex($id);
        }
index 62160233f78d627d0bfc9c2e24b72f634d42005b..12412825800952c35f1d189841758fe0e7a02128 100644 (file)
@@ -44,8 +44,9 @@ sub msg_received_at ($) {
        my @recvd = $hdr->header_raw('Received');
        my ($ts);
        foreach my $r (@recvd) {
-               $r =~ /\s*(\d+\s+[[:alpha:]]+\s+\d{2,4}\s+
-                       \d+\D\d+(?:\D\d+)\s+([\+\-]\d+))/sx or next;
+               $r =~ /\s*([0-9]+\s+[a-zA-Z]+\s+[0-9]{2,4}\s+
+                       [0-9]+[^0-9][0-9]+(?:[^0-9][0-9]+)
+                       \s+([\+\-][0-9]+))/sx or next;
                $ts = eval { str2date_zone($1) } and return $ts;
                my $mid = $hdr->header_raw('Message-ID');
                warn "no date in $mid Received: $r\n";
@@ -59,7 +60,7 @@ sub msg_date_only ($) {
        my ($ts);
        foreach my $d (@date) {
                # Y2K problems: 3-digit years
-               $d =~ s!([A-Za-z]{3}) (\d{3}) (\d\d:\d\d:\d\d)!
+               $d =~ s!([A-Za-z]{3}) ([0-9]{3}) ([0-9]{2}:[0-9]{2}:[0-9]{2})!
                        my $yyyy = $2 + 1900; "$1 $yyyy $3"!e;
                $ts = eval { str2date_zone($d) } and return $ts;
                if ($@) {
index 8cb6c56de82ba765a1fa2474408fb3002de16fb8..be80560ffa4a965a02e597796790ad7082d08f4d 100644 (file)
@@ -121,7 +121,7 @@ sub args_ok ($$) {
 # returns 1 if we can continue, 0 if not due to buffered writes or disconnect
 sub process_line ($$) {
        my ($self, $l) = @_;
-       my ($req, @args) = split(/\s+/, $l);
+       my ($req, @args) = split(/[ \t]/, $l);
        return 1 unless defined($req); # skip blank line
        $req = lc($req);
        $req = eval {
@@ -437,7 +437,7 @@ sub set_nntp_headers ($$$$$) {
        # clobber some
        my $xref = xref($self, $ng, $n, $mid);
        $hdr->header_set('Xref', $xref);
-       $xref =~ s/:\d+//g;
+       $xref =~ s/:[0-9]+//g;
        $hdr->header_set('Newsgroups', (split(/ /, $xref, 2))[1]);
        header_append($hdr, 'List-Post', "<mailto:$ng->{-primary_address}>");
        if (my $url = $ng->base_url) {
@@ -453,7 +453,7 @@ sub art_lookup ($$$) {
        my ($n, $mid);
        my $err;
        if (defined $art) {
-               if ($art =~ /\A\d+\z/o) {
+               if ($art =~ /\A[0-9]+\z/) {
                        $err = '423 no such article number in this group';
                        $n = int($art);
                        goto find_mid;
@@ -508,7 +508,7 @@ sub simple_body_write ($$) {
 
 sub set_art {
        my ($self, $art) = @_;
-       $self->{article} = $art if defined $art && $art =~ /\A\d+\z/;
+       $self->{article} = $art if defined $art && $art =~ /\A[0-9]+\z/;
 }
 
 sub _header ($) {
@@ -576,11 +576,11 @@ sub get_range ($$) {
        defined $range or return '420 No article(s) selected';
        my ($beg, $end);
        my ($min, $max) = $ng->mm->minmax;
-       if ($range =~ /\A(\d+)\z/) {
+       if ($range =~ /\A([0-9]+)\z/) {
                $beg = $end = $1;
-       } elsif ($range =~ /\A(\d+)-\z/) {
+       } elsif ($range =~ /\A([0-9]+)-\z/) {
                ($beg, $end) = ($1, $max);
-       } elsif ($range =~ /\A(\d+)-(\d+)\z/) {
+       } elsif ($range =~ /\A([0-9]+)-([0-9]+)\z/) {
                ($beg, $end) = ($1, $2);
        } else {
                return r501;
@@ -959,7 +959,7 @@ sub event_read {
                $self->{rbuf} .= $$buf;
        }
        my $r = 1;
-       while ($r > 0 && $self->{rbuf} =~ s/\A\s*([^\r\n]*)\r?\n//) {
+       while ($r > 0 && $self->{rbuf} =~ s/\A[ \t\r\n]*([^\r\n]*)\r?\n//) {
                my $line = $1;
                return $self->close if $line =~ /[[:cntrl:]]/s;
                my $t0 = now();
index 8626cf96eaba44acee92a10a2aa3d84fcb7ab788..80bb488622f41095fc619bdcc5ee0b5682513ffe 100644 (file)
@@ -47,7 +47,7 @@ sub call {
        if (my $ibx = $pi_config->lookup_newsgroup($ng)) {
                my $url = PublicInbox::Hval::prurl($env, $ibx->{url});
                my $code = 301;
-               if (defined $article && $article =~ /\A\d+\z/) {
+               if (defined $article && $article =~ /\A[0-9]+\z/) {
                        my $mid = eval { $ibx->mm->mid_for($article) };
                        if (defined $mid) {
                                # article IDs are not stable across clones,
index c054a87497c400f3a2b2053b94f828eff78400d0..9903f427013bebeee9bbd1f427965a8c6cd84ede 100644 (file)
@@ -144,7 +144,7 @@ sub _xdb ($) {
        my $qpf = \($self->{qp_flags} ||= $QP_FLAGS);
        if ($self->{version} >= 2) {
                foreach my $part (<$dir/*>) {
-                       -d $part && $part =~ m!/\d+\z! or next;
+                       -d $part && $part =~ m!/[0-9]+\z! or next;
                        my $sub = Search::Xapian::Database->new($part);
                        if ($xdb) {
                                $xdb->add_database($sub);
index 6592b3b28e27363c769c381d510d3e318535ce90..b089de9c91412cb1e4e42a431256b97c8ebafa63 100644 (file)
@@ -308,12 +308,12 @@ sub new {
        my ($class, $qp) = @_;
 
        my $r = $qp->{r};
-       my ($l) = (($qp->{l} || '') =~ /(\d+)/);
+       my ($l) = (($qp->{l} || '') =~ /([0-9]+)/);
        $l = $LIM if !$l || $l > $LIM;
        bless {
                q => $qp->{'q'},
                x => $qp->{x} || '',
-               o => (($qp->{o} || '0') =~ /(\d+)/),
+               o => (($qp->{o} || '0') =~ /([0-9]+)/),
                l => $l,
                r => (defined $r && $r ne '0'),
        }, $class;
index 3841c56719932a7c5ea4988b38bed549c3c9200d..81f99025cb08d7a881f60d72c2467889c5c94d61 100644 (file)
@@ -206,7 +206,7 @@ sub find_extract_diff ($$$) {
        }
 
        my $msgs = $srch->query($q, { relevance => 1 });
-       my $re = qr/\Aindex ($pre[a-f0-9]*)\.\.($post[a-f0-9]*)(?: (\d+))?/;
+       my $re = qr/\Aindex ($pre[a-f0-9]*)\.\.($post[a-f0-9]*)(?: ([0-9]+))?/;
 
        my $di;
        foreach my $smsg (@$msgs) {
index 76844cd45a0f046b91a35263e9de046899f37f77..a8c33ef4f8f1d09848e6e12f3a9b625a0d1baa85 100644 (file)
@@ -48,7 +48,7 @@ sub count_partitions ($) {
        # due to -compact
        if (-d $xpfx) {
                foreach my $part (<$xpfx/*>) {
-                       -d $part && $part =~ m!/\d+\z! or next;
+                       -d $part && $part =~ m!/[0-9]+\z! or next;
                        eval {
                                Search::Xapian::Database->new($part)->close;
                                $nparts++;
@@ -574,7 +574,7 @@ sub git_dir_latest {
        my $latest;
        opendir my $dh, $pfx or die "opendir $pfx: $!\n";
        while (defined(my $git_dir = readdir($dh))) {
-               $git_dir =~ m!\A(\d+)\.git\z! or next;
+               $git_dir =~ m!\A([0-9]+)\.git\z! or next;
                if ($1 > $$max) {
                        $$max = $1;
                        $latest = "$pfx/$git_dir";
index 09afdaf1bd15aa728766eb3104ff32d43057567c..1b52bf86c550e889f996f0b380640f76dd1124ac 100644 (file)
@@ -528,7 +528,7 @@ sub attach_link ($$$$;$) {
        $desc = $fn unless defined $desc;
        $desc = '' unless defined $desc;
        my $sfn;
-       if (defined $fn && $fn =~ /\A[[:alnum:]][\w\.-]+[[:alnum:]]\z/) {
+       if (defined $fn && $fn =~ /\A$PublicInbox::Hval::FN\z/o) {
                $sfn = $fn;
        } elsif ($ct eq 'text/plain') {
                $sfn = 'a.txt';
@@ -1160,8 +1160,8 @@ sub paginate_recent ($$) {
        # Xapian uses '..' but '-' is perhaps friendier to URL linkifiers
        # if only $after exists "YYYYMMDD.." because "." could be skipped
        # if interpreted as an end-of-sentence
-       $t =~ s/\A(\d{8,14})-// and $after = str2ts($1);
-       $t =~ /\A(\d{8,14})\z/ and $before = str2ts($1);
+       $t =~ s/\A([0-9]{8,14})-// and $after = str2ts($1);
+       $t =~ /\A([0-9]{8,14})\z/ and $before = str2ts($1);
 
        my $ibx = $ctx->{-inbox};
        my $msgs = $ibx->recent($opts, $after, $before);
index 411ed2bb7d4fb53d1b811a2bcf29c76c4265ae01..b7dab81946efe9372fa0a433740d1bd331b206a1 100644 (file)
@@ -55,12 +55,12 @@ sub diff_hunk ($$$$) {
        (defined($spfx) && defined($oid_a) && defined($oid_b)) or
                return "@@ $ca $cb @@";
 
-       my ($n) = ($ca =~ /^-(\d+)/);
+       my ($n) = ($ca =~ /^-([0-9]+)/);
        $n = defined($n) ? do { ++$n; "#n$n" } : '';
 
        my $rv = qq(@@ <a\nhref="$spfx$oid_a/s/$dctx->{Q}$n">$ca</a>);
 
-       ($n) = ($cb =~ /^\+(\d+)/);
+       ($n) = ($cb =~ /^\+([0-9]+)/);
        $n = defined($n) ? do { ++$n; "#n$n" } : '';
 
        $rv .= qq( <a\nhref="$spfx$oid_b/s/$dctx->{Q}$n">$cb</a> @@);
index b6f18f8d8140e130afdc637b4af277b10dc446c2..7ea982041b7daede1c9ddb567995136f12fa7a50 100644 (file)
@@ -28,7 +28,7 @@ use PublicInbox::UserContent;
 our $INBOX_RE = qr!\A/([\w\-][\w\.\-]*)!;
 our $MID_RE = qr!([^/]+)!;
 our $END_RE = qr!(T/|t/|t\.mbox(?:\.gz)?|t\.atom|raw|)!;
-our $ATTACH_RE = qr!(\d[\.\d]*)-([[:alnum:]][\w\.-]+[[:alnum:]])!i;
+our $ATTACH_RE = qr!([0-9][0-9\.]*)-($PublicInbox::Hval::FN)!;
 our $OID_RE = qr![a-f0-9]{7,40}!;
 
 sub new {
@@ -74,7 +74,8 @@ sub call {
        my $method = $env->{REQUEST_METHOD};
 
        if ($method eq 'POST') {
-               if ($path_info =~ m!$INBOX_RE/(?:(\d+)/)?(git-upload-pack)\z!) {
+               if ($path_info =~ m!$INBOX_RE/(?:([0-9]+)/)?
+                                       (git-upload-pack)\z!x) {
                        my ($part, $path) = ($2, $3);
                        return invalid_inbox($ctx, $1) ||
                                serve_git($ctx, $part, $path);
@@ -97,11 +98,11 @@ sub call {
                invalid_inbox($ctx, $1) || get_atom($ctx);
        } elsif ($path_info =~ m!$INBOX_RE/new\.html\z!o) {
                invalid_inbox($ctx, $1) || get_new($ctx);
-       } elsif ($path_info =~ m!$INBOX_RE/(?:(\d+)/)?
+       } elsif ($path_info =~ m!$INBOX_RE/(?:([0-9]+)/)?
                                ($PublicInbox::GitHTTPBackend::ANY)\z!ox) {
                my ($part, $path) = ($2, $3);
                invalid_inbox($ctx, $1) || serve_git($ctx, $part, $path);
-       } elsif ($path_info =~ m!$INBOX_RE/([\w-]+).mbox\.gz\z!o) {
+       } elsif ($path_info =~ m!$INBOX_RE/([a-zA-Z0-9_\-]+).mbox\.gz\z!o) {
                serve_mbox_range($ctx, $1, $2);
        } elsif ($path_info =~ m!$INBOX_RE/$MID_RE/$END_RE\z!o) {
                msg_page($ctx, $1, $2, $3);
@@ -123,11 +124,12 @@ sub call {
                r301($ctx, $1, $2);
        } elsif ($path_info =~ m!$INBOX_RE/_/text(?:/(.*))?\z!o) {
                get_text($ctx, $1, $2);
-       } elsif ($path_info =~ m!$INBOX_RE/([\w\-\.]+)\.css\z!o) {
+       } elsif ($path_info =~ m!$INBOX_RE/([a-zA-Z0-9_\-\.]+)\.css\z!o) {
                get_css($ctx, $1, $2);
        } elsif ($path_info =~ m!$INBOX_RE/($OID_RE)/s/\z!o) {
                get_vcs_object($ctx, $1, $2);
-       } elsif ($path_info =~ m!$INBOX_RE/($OID_RE)/s/([\w\.\-]+)\z!o) {
+       } elsif ($path_info =~ m!$INBOX_RE/($OID_RE)/s/
+                               ($PublicInbox::Hval::FN)\z!ox) {
                get_vcs_object($ctx, $1, $2, $3);
        } elsif ($path_info =~ m!$INBOX_RE/($OID_RE)/s\z!o) {
                r301($ctx, $1, $2, 's/');
@@ -534,11 +536,15 @@ sub stylesheets_prepare ($$) {
                        $inline_ok = 0;
                } else {
                        my $fn = $_;
+                       my ($key) = (m!([^/]+?)(?:\.css)?\z!i);
+                       if ($key !~ /\A[a-zA-Z0-9_\-\.]+\z/) {
+                               warn "ignoring $fn, non-ASCII word character\n";
+                               next;
+                       }
                        open(my $fh, '<', $fn) or do {
                                warn "failed to open $fn: $!\n";
                                next;
                        };
-                       my ($key) = (m!([^/]+?)(?:\.css)?\z!i);
                        my $ctime = 0;
                        my $local = do { local $/; <$fh> };
                        if ($local =~ /\S/) {
index d690ce411921b5fa40820b98923b53d23c1f001a..96103cb028e09b014e4bf29fb7e711c4e567ec08 100644 (file)
@@ -27,7 +27,7 @@ sub get_attach ($$$) {
                if ($ct && (($ct->{discrete} || '') eq 'text')) {
                        # display all text as text/plain:
                        my $cset = $ct->{attributes}->{charset};
-                       if ($cset && ($cset =~ /\A[\w-]+\z/)) {
+                       if ($cset && ($cset =~ /\A[a-zA-Z0-9_\-]+\z/)) {
                                $res->[1]->[1] .= qq(; charset=$cset);
                        }
                } else { # TODO: allow user to configure safe types
index e8dad4b892f50aceac866ef0956338edbb569ab0..e1473b3deec7135b4aa9557a65371ce9dd469d7a 100644 (file)
@@ -24,8 +24,8 @@ sub list_match_domain ($$) {
        my ($self, $env) = @_;
        my @list;
        my $host = $env->{HTTP_HOST} // $env->{SERVER_NAME};
-       $host =~ s/:\d+\z//;
-       my $re = qr!\A(?:https?:)?//\Q$host\E(?::\d+)?/!i;
+       $host =~ s/:[0-9]+\z//;
+       my $re = qr!\A(?:https?:)?//\Q$host\E(?::[0-9]+)?/!i;
        $self->{pi_config}->each_inbox(sub {
                my ($ibx) = @_;
                push @list, $ibx if !$ibx->{-hide}->{www} && $ibx->{url} =~ $re;
index 906723104d553d2cb25465c4dfd488e213914a0f..dad080c83bd724ff4998cfd459465a58b2b0b2f3 100644 (file)
@@ -150,7 +150,7 @@ sub run {
        } else {
                opendir my $dh, $old or die "Failed to opendir $old: $!\n";
                while (defined(my $dn = readdir($dh))) {
-                       if ($dn =~ /\A\d+\z/) {
+                       if ($dn =~ /\A[0-9]+\z/) {
                                my $tmpl = "$dn-XXXXXXXX";
                                my $dst = tempdir($tmpl, DIR => $old);
                                same_fs_or_die($old, $dst);
@@ -200,7 +200,7 @@ sub progress_pfx ($) {
        my @p = split('/', $_[0]);
 
        # return "xap15/0" for v2, or "xapian15" for v1:
-       ($p[-1] =~ /\A\d+\z/) ? "$p[-2]/$p[-1]" : $p[-1];
+       ($p[-1] =~ /\A[0-9]+\z/) ? "$p[-2]/$p[-1]" : $p[-1];
 }
 
 # xapian-compact wrapper
@@ -276,7 +276,7 @@ sub cpdb ($$) {
                        $dst->set_metadata('last_commit', $lc) if $lc;
 
                        # only the first xapian partition (0) gets 'indexlevel'
-                       if ($old =~ m!(?:xapian\d+|xap\d+/0)\z!) {
+                       if ($old =~ m!(?:xapian[0-9]+|xap[0-9]+/0)\z!) {
                                my $l = $src->get_metadata('indexlevel');
                                if ($l eq 'medium') {
                                        $dst->set_metadata('indexlevel', $l);
index 381826dc440c8ca43546aa823dd0b4aab93e39c2..25e6cc9b782451a5774acde97d7454d70f8d5a3e 100755 (executable)
@@ -91,7 +91,7 @@ foreach my $ibx (@inboxes) {
        my $xdir_ro = $ibx->{search}->xdir(1);
        my $npart = 0;
        foreach my $part (<$xdir_ro/*>) {
-               if (-d $part && $part =~ m!/\d+\z!) {
+               if (-d $part && $part =~ m!/[0-9]+\z!) {
                        my $bytes = 0;
                        $bytes += -s $_ foreach glob("$part/*");
                        $npart++ if $bytes;
index fe218b91f95c4a89e0d3eaea0e6a2d6052b39c53..c492358257525fab99487f8e53bde3e55e209896 100644 (file)
@@ -132,4 +132,16 @@ use PublicInbox::Linkify;
                'punctuation with unpaired ) OK')
 }
 
+if ('IDN example: <ACDB98F4-178C-43C3-99C4-A1D03DD6A8F5@sb.org>') {
+       my $hc = '&#26376;';
+       my $u = "http://www.\x{6708}.example.com/";
+       my $s = $u;
+       my $l = PublicInbox::Linkify->new;
+       $s = $l->linkify_1($s);
+       $s = $l->linkify_2($s);
+       my $expect = qq{<a
+href="http://www.$hc.example.com/">http://www.$hc.example.com/</a>};
+       is($s, $expect, 'IDN message escaped properly');
+}
+
 done_testing();