gitweb: add "e-mail privacy" feature to redact e-mail addresses

Gitweb extracts content from the Git log and makes it accessible over HTTP. As a result, e-mail addresses found in commits are exposed to web crawlers and they may not respect robots.txt. This can result in unsolicited messages. Introduce an 'email-privacy' feature which redacts e-mail addresses from the generated HTML content. Specifically, obscure addresses retrieved from the the author/committer and comment sections of the Git log. The feature is off by default. This feature does not prevent someone from downloading the unredacted commit log, e.g., by cloning the repository, and extracting information from it. It aims to hinder the low- effort, bulk collection of e-mail addresses by web crawlers. Signed-off-by: Georgios Kontaxis <geko1702+commits@99rst.org> Acked-by: Eric Wong <e@80x24.org> Acked-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Georgios Kontaxis committed Mar 28, 2021 at 23:26 UTC 0996dd3d6dbcc132cf212f3420b89947fd9cc2ba
2 files changed +38 -7
Documentation/gitweb.conf.txt
+11
@@ -751,6 +751,17 @@ default font sizes or lineheights are changed (e.g. via adding extra
751 CSS stylesheet in `@stylesheets`), it may be appropriate to change
752 these values.
753
754 +email-privacy::
755 + Redact e-mail addresses from the generated HTML, etc. content.
756 + This obscures e-mail addresses retrieved from the author/committer
757 + and comment sections of the Git log.
758 + It is meant to hinder web crawlers that harvest and abuse addresses.
759 + Such crawlers may not respect robots.txt.
760 + Note that users and user tools also see the addresses as redacted.
761 + If Gitweb is not the final step in a workflow then subsequent steps
762 + may misbehave because of the redacted information they receive.
763 + Disabled by default.
764 +
765 highlight::
766 Server-side syntax highlight support in "blob" view. It requires
767 `$highlight_bin` program to be available (see the description of
gitweb/gitweb.perl
+27 -7
@@ -569,6 +569,15 @@ our %feature = (
569 'sub' => \&feature_extra_branch_refs,
570 'override' => 0,
571 'default' => []},
572 +
573 + # Redact e-mail addresses.
574 +
575 + # To enable system wide have in $GITWEB_CONFIG
576 + # $feature{'email-privacy'}{'default'} = [1];
577 + 'email-privacy' => {
578 + 'sub' => sub { feature_bool('email-privacy', @_) },
579 + 'override' => 1,
580 + 'default' => [0]},
581 );
582
583 sub gitweb_get_feature {
@@ -3449,6 +3458,13 @@ sub parse_date {
3458 return %date;
3459 }
3460
3461 +sub hide_mailaddrs_if_private {
3462 + my $line = shift;
3463 + return $line unless gitweb_check_feature('email-privacy');
3464 + $line =~ s/<[^@>]+@[^>]+>/<redacted>/g;
3465 + return $line;
3466 +}
3467 +
3468 sub parse_tag {
3469 my $tag_id = shift;
3470 my %tag;
@@ -3465,7 +3481,7 @@ sub parse_tag {
3481 } elsif ($line =~ m/^tag (.+)$/) {
3482 $tag{'name'} = $1;
3483 } elsif ($line =~ m/^tagger (.*) ([0-9]+) (.*)$/) {
3468 - $tag{'author'} = $1;
3484 + $tag{'author'} = hide_mailaddrs_if_private($1);
3485 $tag{'author_epoch'} = $2;
3486 $tag{'author_tz'} = $3;
3487 if ($tag{'author'} =~ m/^([^<]+) <([^>]*)>/) {
@@ -3513,7 +3529,7 @@ sub parse_commit_text {
3529 } elsif ((!defined $withparents) && ($line =~ m/^parent ($oid_regex)$/)) {
3530 push @parents, $1;
3531 } elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) {
3516 - $co{'author'} = to_utf8($1);
3532 + $co{'author'} = hide_mailaddrs_if_private(to_utf8($1));
3533 $co{'author_epoch'} = $2;
3534 $co{'author_tz'} = $3;
3535 if ($co{'author'} =~ m/^([^<]+) <([^>]*)>/) {
@@ -3523,7 +3539,7 @@ sub parse_commit_text {
3539 $co{'author_name'} = $co{'author'};
3540 }
3541 } elsif ($line =~ m/^committer (.*) ([0-9]+) (.*)$/) {
3526 - $co{'committer'} = to_utf8($1);
3542 + $co{'committer'} = hide_mailaddrs_if_private(to_utf8($1));
3543 $co{'committer_epoch'} = $2;
3544 $co{'committer_tz'} = $3;
3545 if ($co{'committer'} =~ m/^([^<]+) <([^>]*)>/) {
@@ -3568,9 +3584,10 @@ sub parse_commit_text {
3584 if (! defined $co{'title'} || $co{'title'} eq "") {
3585 $co{'title'} = $co{'title_short'} = '(no commit message)';
3586 }
3571 - # remove added spaces
3587 + # remove added spaces, redact e-mail addresses if applicable.
3588 foreach my $line (@commit_lines) {
3589 $line =~ s/^ //;
3590 + $line = hide_mailaddrs_if_private($line);
3591 }
3592 $co{'comment'} = \@commit_lines;
3593
@@ -7489,7 +7506,8 @@ sub git_log_generic {
7506 -accesskey => "n", -title => "Alt-n"}, "next");
7507 }
7508 my $patch_max = gitweb_get_feature('patches');
7492 - if ($patch_max && !defined $file_name) {
7509 + if ($patch_max && !defined $file_name &&
7510 + !gitweb_check_feature('email-privacy')) {
7511 if ($patch_max < 0 || @commitlist <= $patch_max) {
7512 $paging_nav .= " &sdot; " .
7513 $cgi->a({-href => href(action=>"patches", -replay=>1)},
@@ -7550,7 +7568,8 @@ sub git_commit {
7568 } @$parents ) .
7569 ')';
7570 }
7553 - if (gitweb_check_feature('patches') && @$parents <= 1) {
7571 + if (gitweb_check_feature('patches') && @$parents <= 1 &&
7572 + !gitweb_check_feature('email-privacy')) {
7573 $formats_nav .= " | " .
7574 $cgi->a({-href => href(action=>"patch", -replay=>1)},
7575 "patch");
@@ -7863,7 +7882,8 @@ sub git_commitdiff {
7882 $formats_nav =
7883 $cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)},
7884 "raw");
7866 - if ($patch_max && @{$co{'parents'}} <= 1) {
7885 + if ($patch_max && @{$co{'parents'}} <= 1 &&
7886 + !gitweb_check_feature('email-privacy')) {
7887 $formats_nav .= " | " .
7888 $cgi->a({-href => href(action=>"patch", -replay=>1)},
7889 "patch");