Skip to content

Commit

Permalink
Set noopener/noreferrer in Markdown links
Browse files Browse the repository at this point in the history
URLs wrapped in angle brackets like `<http://example.com>` are not
processed correctly (see FIXME note in MantisMarkdown::inlineUrlTag()).

This is because MantisCoreFormattingPlugin::formatted() applies
html_specialchars() first, so the < > are converted to &lt;/&gt;
and ParseDown never calls the inlineUrlTag() method, resulting in broken
links on single-line strings (`<http://example.com&gt;`).

On multi-line strings, the angle brackets wrapping the link remain
visible (they ought to be removed).

Fixes #30791
  • Loading branch information
dregad committed Sep 3, 2022
1 parent 7760863 commit 881f49a
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 22 deletions.
47 changes: 47 additions & 0 deletions core/helper_api.php
Original file line number Diff line number Diff line change
Expand Up @@ -842,3 +842,50 @@ function helper_parse_id( $p_id, $p_field_name ) {
function helper_parse_issue_id( $p_issue_id, $p_field_name = 'issue_id' ) {
return helper_parse_id( $p_issue_id, $p_field_name );
}

/**
* Return a link's attributes based on Mantis Config.
*
* Depending on $p_return_array parameter, return value will either be
* - an associative array with attribute => value pairs
* e.g. ['rel'=>'noopener', target=>'_blank'], or
* - a string ready to be added to an html <a> tag,
* e.g. ' rel="noopener" target="_blank"'
*
* @param bool $p_return_array true to return an array (default), false for string
* @return array|string
*
* @see $g_html_make_links
*/
function helper_get_link_attributes( $p_return_array = true ) {
$t_html_make_links = config_get( 'html_make_links' );

$t_attributes = array();
if( $t_html_make_links ) {
# Link target
if( $t_html_make_links & LINKS_NEW_WINDOW ) {
$t_attributes['target'] = '_blank';
}

# Link relation type
if( $t_html_make_links & ( LINKS_NOOPENER | LINKS_NOREFERRER ) ) {
if( $t_html_make_links & LINKS_NOREFERRER ) {
$t_attributes['rel'] = 'noreferrer';
# noreferrer implies noopener, so no need to set the latter
}
elseif( $t_html_make_links & LINKS_NOOPENER ) {
$t_attributes['rel'] = 'noopener';
}
}
}

if( $p_return_array ) {
return $t_attributes;
}

$t_string = '';
foreach( $t_attributes as $t_attr => $t_value ) {
$t_string .= " $t_attr=\"$t_value\"";
}
return $t_string;
}
14 changes: 1 addition & 13 deletions core/string_api.php
Original file line number Diff line number Diff line change
Expand Up @@ -508,19 +508,7 @@ function string_insert_hrefs( $p_string ) {
}

# Set the link's target and type according to configuration
$t_link_attributes = '';
if( $t_html_make_links & ( LINKS_NOOPENER | LINKS_NOREFERRER ) ) {
if( $t_html_make_links & LINKS_NOREFERRER ) {
$t_link_attributes .= 'noreferrer';
# noreferrer implies noopener, so no need to set the latter
} elseif( $t_html_make_links & LINKS_NOOPENER ) {
$t_link_attributes .= 'noopener';
}
$t_link_attributes = ' rel="' . $t_link_attributes . '"';
}
if( $t_html_make_links & LINKS_NEW_WINDOW ) {
$t_link_attributes .= ' target="_blank"';
}
$t_link_attributes = helper_get_link_attributes( false );

# Find any URL in a string and replace it with a clickable link
$p_string = preg_replace_callback(
Expand Down
50 changes: 41 additions & 9 deletions plugins/MantisCoreFormatting/core/MantisMarkdown.php
Original file line number Diff line number Diff line change
Expand Up @@ -255,21 +255,29 @@ protected function blockCodeComplete( $block ) {
/**
* Customize the inlineLink method
*
* @param array $block A block-level element
* @param array $Excerpt A block-level element
* @access protected
* @return string html representation generated from markdown.
* @return array html representation generated from markdown.
*/
protected function inlineLink( $block ) {

$block = parent::inlineLink( $block );
protected function inlineLink( $Excerpt ) {
return $this->processUrl( parent::inlineLink( $Excerpt ) );
}

if( isset( $block['element']['attributes']['href'] )) {
$this->processAmpersand( $block['element']['attributes']['href'] );
}
protected function inlineUrl( $Excerpt ) {
return $this->processUrl( parent::inlineUrl( $Excerpt ) );
}

return $block;
protected function inlineUrlTag( $Excerpt ) {
# @FIXME
# This function is supposed to process links like `<http://example.com>`
# on single-line texts, but it does not actually work: the function is
# never called (see Parsedown::line() 1077), because
# MantisCoreFormattingPlugin::formatted() applies html_specialchars()
# first, so the < > are converted to &lt;/&gt;.
return $this->processUrl( parent::inlineUrlTag( $Excerpt ) );
}


/**
* Initialize the singleton static instance.
*/
Expand All @@ -295,4 +303,28 @@ private function processAmpersand( &$p_text ) {
$p_text = str_replace( '&amp;', '&', $p_text );
}

/**
* Set a link's target and rel attributes as appropriate.
*
* @param array|null $Excerpt
* @return array|null
*
* @see helper_get_link_attributes()
*/
private function processUrl( $Excerpt ) {
if( isset( $Excerpt['element']['attributes']['href'] ) ) {
$this->processAmpersand( $Excerpt['element']['attributes']['href'] );
}

if( isset( $Excerpt['element']['attributes'] ) ) {
# Set the link's attributes according to configuration
$Excerpt['element']['attributes'] = array_replace(
$Excerpt['element']['attributes'],
helper_get_link_attributes()
);
}

return $Excerpt;
}

}

0 comments on commit 881f49a

Please sign in to comment.