788
# ======================================================================
789
# input validation and dispatch
790
791
+# Various hash size-related values.
792
+my $sha1_len = 40;
793
+my $sha256_extra_len = 24;
794
+my $sha256_len = $sha1_len + $sha256_extra_len;
795
+
796
+# A regex matching $len hex characters. $len may be a range (e.g. 7,64).
797
+sub oid_nlen_regex {
798
+ my $len = shift;
799
+ my $hchr = qr/[0-9a-fA-F]/;
800
+ return qr/(?:(?:$hchr){$len})/;
801
+}
802
+
803
+# A regex matching two sets of $nlen hex characters, prefixed by the literal
804
+# string $prefix and with the literal string $infix between them.
805
+sub oid_nlen_prefix_infix_regex {
806
+ my $nlen = shift;
807
+ my $prefix = shift;
808
+ my $infix = shift;
809
+
810
+ my $rx = oid_nlen_regex($nlen);
811
+
812
+ return qr/^\Q$prefix\E$rx\Q$infix\E$rx$/;
813
+}
814
+
815
+# A regex matching a valid object ID.
816
+our $oid_regex;
817
+{
818
+ my $x = oid_nlen_regex($sha1_len);
819
+ my $y = oid_nlen_regex($sha256_extra_len);
820
+ $oid_regex = qr/(?:$x(?:$y)?)/;
821
+}
822
+
823
# input parameters can be collected from a variety of sources (presently, CGI
824
# and PATH_INFO), so we define an %input_params hash that collects them all
825
# together during validation: this allows subsequent uses (e.g. href()) to be
1548
1549
return undef unless defined $input;
1550
# textual hashes are O.K.
1519
- if ($input =~ m/^[0-9a-fA-F]{40}$/) {
1551
+ if ($input =~ m/^$oid_regex$/) {
1552
return 1;
1553
}
1554
# it must be correct pathname
2060
sub format_log_line_html {
2061
my $line = shift;
2062
2063
+ # Potentially abbreviated OID.
2064
+ my $regex = oid_nlen_regex("7,64");
2065
+
2066
$line = esc_html($line, -nbsp=>1);
2067
$line =~ s{
2068
\b
2072
(?<!-) # see strbuf_check_tag_ref(). Tags can't start with -
2073
[A-Za-z0-9.-]+
2074
(?!\.) # refs can't end with ".", see check_refname_format()
2040
- -g[0-9a-fA-F]{7,40}
2075
+ -g$regex
2076
|
2077
# Just a normal looking Git SHA1
2043
- [0-9a-fA-F]{7,40}
2078
+ $regex
2079
)
2080
\b
2081
}{
2321
')</span>';
2322
}
2323
# match <hash>
2289
- if ($line =~ m/^index [0-9a-fA-F]{40},[0-9a-fA-F]{40}/) {
2324
+ if ($line =~ oid_nlen_prefix_infix_regex($sha1_len, "index ", ",") |
2325
+ $line =~ oid_nlen_prefix_infix_regex($sha256_len, "index ", ",")) {
2326
# can match only for combined diff
2327
$line = 'index ';
2328
for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
2344
$line .= '0' x 7;
2345
}
2346
2311
- } elsif ($line =~ m/^index [0-9a-fA-F]{40}..[0-9a-fA-F]{40}/) {
2347
+ } elsif ($line =~ oid_nlen_prefix_infix_regex($sha1_len, "index ", "..") |
2348
+ $line =~ oid_nlen_prefix_infix_regex($sha256_len, "index ", "..")) {
2349
# can match only for ordinary diff
2350
my ($from_link, $to_link);
2351
if ($from->{'href'}) {
2871
}
2872
2873
#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
2837
- $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/;
2874
+ $line =~ m/^([0-9]+) (.+) ($oid_regex)\t/;
2875
if (defined $type && $type ne $2) {
2876
# type doesn't match
2877
return undef;
3370
3371
while (my $line = <$fd>) {
3372
chomp $line;
3336
- if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type.*)$!) {
3373
+ if ($line =~ m!^($oid_regex)\srefs/($type.*)$!) {
3374
if (defined $refs{$1}) {
3375
push @{$refs{$1}}, $2;
3376
} else {
3444
$tag{'id'} = $tag_id;
3445
while (my $line = <$fd>) {
3446
chomp $line;
3410
- if ($line =~ m/^object ([0-9a-fA-F]{40})$/) {
3447
+ if ($line =~ m/^object ($oid_regex)$/) {
3448
$tag{'object'} = $1;
3449
} elsif ($line =~ m/^type (.+)$/) {
3450
$tag{'type'} = $1;
3488
}
3489
3490
my $header = shift @commit_lines;
3454
- if ($header !~ m/^[0-9a-fA-F]{40}/) {
3491
+ if ($header !~ m/^$oid_regex/) {
3492
return;
3493
}
3494
($co{'id'}, my @parents) = split ' ', $header;
3495
while (my $line = shift @commit_lines) {
3496
last if $line eq "\n";
3460
- if ($line =~ m/^tree ([0-9a-fA-F]{40})$/) {
3497
+ if ($line =~ m/^tree ($oid_regex)$/) {
3498
$co{'tree'} = $1;
3462
- } elsif ((!defined $withparents) && ($line =~ m/^parent ([0-9a-fA-F]{40})$/)) {
3499
+ } elsif ((!defined $withparents) && ($line =~ m/^parent ($oid_regex)$/)) {
3500
push @parents, $1;
3501
} elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) {
3502
$co{'author'} = to_utf8($1);
3628
3629
# ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M ls-files.c'
3630
# ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M rev-tree.c'
3594
- if ($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/) {
3631
+ if ($line =~ m/^:([0-7]{6}) ([0-7]{6}) ($oid_regex) ($oid_regex) (.)([0-9]{0,3})\t(.*)$/) {
3632
$res{'from_mode'} = $1;
3633
$res{'to_mode'} = $2;
3634
$res{'from_id'} = $3;
3643
}
3644
# '::100755 100755 100755 60e79ca1b01bc8b057abe17ddab484699a7f5fdb 94067cc5f73388f33722d52ae02f44692bc07490 94067cc5f73388f33722d52ae02f44692bc07490 MR git-gui/git-gui.sh'
3645
# combined diff (for merge commit)
3609
- elsif ($line =~ s/^(::+)((?:[0-7]{6} )+)((?:[0-9a-fA-F]{40} )+)([a-zA-Z]+)\t(.*)$//) {
3646
+ elsif ($line =~ s/^(::+)((?:[0-7]{6} )+)((?:$oid_regex )+)([a-zA-Z]+)\t(.*)$//) {
3647
$res{'nparents'} = length($1);
3648
$res{'from_mode'} = [ split(' ', $2) ];
3649
$res{'to_mode'} = pop @{$res{'from_mode'}};
3653
$res{'to_file'} = unquote($5);
3654
}
3655
# 'c512b523472485aef4fff9e57b229d9d243c967f'
3619
- elsif ($line =~ m/^([0-9a-fA-F]{40})$/) {
3656
+ elsif ($line =~ m/^($oid_regex)$/) {
3657
$res{'commit'} = $1;
3658
}
3659
3681
3682
if ($opts{'-l'}) {
3683
#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa 16717 panic.c'
3647
- $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40}) +(-|[0-9]+)\t(.+)$/s;
3684
+ $line =~ m/^([0-9]+) (.+) ($oid_regex) +(-|[0-9]+)\t(.+)$/s;
3685
3686
$res{'mode'} = $1;
3687
$res{'type'} = $2;
3694
}
3695
} else {
3696
#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
3660
- $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s;
3697
+ $line =~ m/^([0-9]+) (.+) ($oid_regex)\t(.+)$/s;
3698
3699
$res{'mode'} = $1;
3700
$res{'type'} = $2;
4836
sub is_deleted {
4837
my $diffinfo = shift;
4838
4802
- return $diffinfo->{'to_id'} eq ('0' x 40);
4839
+ return $diffinfo->{'to_id'} eq ('0' x 40) || $diffinfo->{'to_id'} eq ('0' x 64);
4840
}
4841
4842
# does patch correspond to [previous] difftree raw line
6322
-class => "list subject"},
6323
chop_and_escape_str($co{'title'}, 50) . "<br/>");
6324
} elsif (defined $set{'to_id'}) {
6288
- next if ($set{'to_id'} =~ m/^0{40}$/);
6325
+ next if is_deleted(\%set);
6326
6327
print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
6328
hash=>$set{'to_id'}, file_name=>$set{'to_file'}),
6866
# the header: <SHA-1> <src lineno> <dst lineno> [<lines in group>]
6867
# no <lines in group> for subsequent lines in group of lines
6868
my ($full_rev, $orig_lineno, $lineno, $group_size) =
6832
- ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/);
6869
+ ($line =~ /^($oid_regex) (\d+) (\d+)(?: (\d+))?$/);
6870
if (!exists $metainfo{$full_rev}) {
6871
$metainfo{$full_rev} = { 'nprevious' => 0 };
6872
}
6916
}
6917
# 'previous' <sha1 of parent commit> <filename at commit>
6918
if (exists $meta->{'previous'} &&
6882
- $meta->{'previous'} =~ /^([a-fA-F0-9]{40}) (.*)$/) {
6919
+ $meta->{'previous'} =~ /^($oid_regex) (.*)$/) {
6920
$meta->{'parent'} = $1;
6921
$meta->{'file_parent'} = unquote($2);
6922
}
7033
} else {
7034
die_error(400, "No file name defined");
7035
}
6999
- } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) {
7036
+ } elsif ($hash =~ m/^$oid_regex$/) {
7037
# blobs defined by non-textual hash id's can be cached
7038
$expires = "+1d";
7039
}
7094
} else {
7095
die_error(400, "No file name defined");
7096
}
7060
- } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) {
7097
+ } elsif ($hash =~ m/^$oid_regex$/) {
7098
# blobs defined by non-textual hash id's can be cached
7099
$expires = "+1d";
7100
}
7552
7553
# non-textual hash id's can be cached
7554
my $expires;
7518
- if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
7555
+ if ($hash =~ m/^$oid_regex$/) {
7556
$expires = "+1d";
7557
}
7558
my $refs = git_get_references();
7646
close $fd;
7647
7648
#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
7612
- unless ($line && $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/) {
7649
+ unless ($line && $line =~ m/^([0-9]+) (.+) ($oid_regex)\t/) {
7650
die_error(404, "File or directory for given base does not exist");
7651
}
7652
$type = $2;
7686
or die_error(404, "Blob diff not found");
7687
7688
} elsif (defined $hash &&
7652
- $hash =~ /[0-9a-fA-F]{40}/) {
7689
+ $hash =~ $oid_regex) {
7690
# try to find filename from $hash
7691
7692
# read filtered raw output
7696
@difftree =
7697
# ':100644 100644 03b21826... 3b93d5e7... M ls-files.c'
7698
# $hash == to_id
7662
- grep { /^:[0-7]{6} [0-7]{6} [0-9a-fA-F]{40} $hash/ }
7699
+ grep { /^:[0-7]{6} [0-7]{6} $oid_regex $hash/ }
7700
map { chomp; $_ } <$fd>;
7701
close $fd
7702
or die_error(404, "Reading git-diff-tree failed");
7719
$hash ||= $diffinfo{'to_id'};
7720
7721
# non-textual hash id's can be cached
7685
- if ($hash_base =~ m/^[0-9a-fA-F]{40}$/ &&
7686
- $hash_parent_base =~ m/^[0-9a-fA-F]{40}$/) {
7722
+ if ($hash_base =~ m/^$oid_regex$/ &&
7723
+ $hash_parent_base =~ m/^$oid_regex$/) {
7724
$expires = '+1d';
7725
}
7726
7856
$hash_parent ne '-c' && $hash_parent ne '--cc') {
7857
# commitdiff with two commits given
7858
my $hash_parent_short = $hash_parent;
7822
- if ($hash_parent =~ m/^[0-9a-fA-F]{40}$/) {
7859
+ if ($hash_parent =~ m/^$oid_regex$/) {
7860
$hash_parent_short = substr($hash_parent, 0, 7);
7861
}
7862
$formats_nav .=
7965
7966
# non-textual hash id's can be cached
7967
my $expires;
7931
- if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
7968
+ if ($hash =~ m/^$oid_regex$/) {
7969
$expires = "+1d";
7970
}
7971