diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 9a84fb4467..9145b73f1b 100644 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -1605,14 +1605,34 @@ sub _check_bug_file_loc { my ($invocant, $url) = @_; return '' if !defined $url; $url = trim($url); + return '' if $url eq ''; # On bug entry, if bug_file_loc is "http://", the default, use an - # empty value instead. However, on bug editing people can set that - # back if they *really* want to. + # empty value instead. On bug editing, "http://" is not generally + # accepted by the safe URL validation below; it is only allowed if it + # is already stored on the bug and remains unchanged. if (!ref $invocant && $url eq 'http://') { - $url = ''; + return ''; } - return $url; + + # Allowlist scheme check — mirrors the is_safe_url template helper so + # what passes here is exactly what renders as a clickable link. + my $safe_url_regexp = SAFE_URL_REGEXP(); + return $url if $url =~ /^$safe_url_regexp$/; + + # Colon-free relative path / local reference (matches is_safe_url + # second branch in Bugzilla/Template.pm). + return $url if $url =~ /^[^\s<>\":]+[\w\/]$/i; + + # Allow an already-stored unsafe value to pass through unchanged so that + # edits to other fields on the bug are not blocked. Unsafe values are + # rendered as plain text by templates; a DB cleanup script will scrub them. + if (ref $invocant) { + my $current = $invocant->{bug_file_loc} // ''; + return $url if $url eq $current; + } + + ThrowUserError('bug_file_loc_invalid', {url => $url}); } sub _check_bug_status { diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 6155d985ff..c4c86a09ac 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -171,6 +171,7 @@ use Memoize; MAX_STS_AGE SAFE_PROTOCOLS + SAFE_URL_REGEXP LEGAL_CONTENT_TYPES MIN_SMALLINT @@ -490,6 +491,11 @@ use constant SAFE_PROTOCOLS => ( 'telnet', 'wais' ); +sub SAFE_URL_REGEXP { + my $safe_protocols = join('|', SAFE_PROTOCOLS); + return qr/($safe_protocols):[^:\s<>\"][^\s<>\"]+[\w\/]/i; +} + # Valid MIME types for attachments. use constant LEGAL_CONTENT_TYPES => ( 'application', 'audio', 'font', 'image', 'message', diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm index 4e7afb4d5e..f51496bce9 100644 --- a/Bugzilla/Template.pm +++ b/Bugzilla/Template.pm @@ -52,11 +52,6 @@ use constant FORMAT_2_SIZE => [19, 55]; our %SHARED_PROVIDERS; our $COLOR_QUOTES = 1; -# Pseudo-constant. -sub SAFE_URL_REGEXP { - my $safe_protocols = join('|', SAFE_PROTOCOLS); - return qr/($safe_protocols):[^:\s<>\"][^\s<>\"]+[\w\/]/i; -} sub is_safe_url { my $url = shift; diff --git a/Bugzilla/WebService/Constants.pm b/Bugzilla/WebService/Constants.pm index 10c42df9d1..349b2d83dc 100644 --- a/Bugzilla/WebService/Constants.pm +++ b/Bugzilla/WebService/Constants.pm @@ -126,9 +126,10 @@ use constant WS_ERROR_CODE => { comment_tag_too_long => 127, comment_tag_too_short => 128, - # See Also errors - bug_url_invalid => 112, - bug_url_too_long => 112, + # Bug Url errors + bug_url_invalid => 112, + bug_url_too_long => 112, + bug_file_loc_invalid => 112, # Insidergroup Errors user_not_insider => 113, diff --git a/extensions/BugModal/template/en/default/bug_modal/activity_stream.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/activity_stream.html.tmpl index aae369b866..c7413b062f 100644 --- a/extensions/BugModal/template/en/default/bug_modal/activity_stream.html.tmpl +++ b/extensions/BugModal/template/en/default/bug_modal/activity_stream.html.tmpl @@ -440,9 +440,12 @@ CASE 'bug_file_loc'; %] + [% IF is_safe_url(value) %] [% value FILTER truncate(40) FILTER html %] + class="bug-url">[% value FILTER truncate(40) FILTER html %] + [% ELSE %] + [% value FILTER truncate(40) FILTER html %] + [% END %] [% CASE 'see_also'; diff --git a/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl index f61783f92f..02b25ac646 100755 --- a/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl +++ b/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl @@ -1530,10 +1530,17 @@ END; %]
[% url FILTER html %] is not a valid URL for the URL field.
+ Only safe protocols (http, https, ftp, and similar) are allowed.
+ Protocols such as "javascript:", "data:", and "vbscript:" are not permitted.
+
[% ELSIF error == "bug_url_invalid" %]
[% title = "Invalid $terms.Bug URL" %]
[% url FILTER html %] is not a valid URL to [% terms.abug %].