Send user-written link markup as plain text in Slack notifications

What does this MR do and why?

What does this MR do and why?

GitLab posts chat notifications and slash command responses to Slack. Slack renders a <url|label> token as a link that shows only the label, with no hover preview of the destination. So these messages are conservative about which text becomes a link: links GitLab builds itself (issue, project, pipeline) stay clickable, and user-written text is sent as plain text.

  • Send user-written text in issue and incident descriptions, note bodies, wiki page messages, and group mention text as written. Angle brackets are escaped and link markup (HTML anchors, Markdown links, raw <url|label> tokens) is not converted. The webhook notifier no longer uses the slack-messenger gem's link formatting middleware, because links are converted while the message is built. The GitLab for Slack app posts the same attachments directly.
  • Escape <url|label> tokens that contain more than one pipe in the sanitizer used for integrations that render Markdown themselves.
  • Strip link markup from slash command responses: issue titles, assignee names, milestone titles, label names, the attachment author name, the plain-text fallback summary, and the project name in pretexts. In a personal namespace the project name includes the owner's display name. This matches chat notifications.

References

Issue: https://gitlab.com/gitlab-org/gitlab/-/work_items/593573 (confidential). Background and discussion are in that issue.

Screenshots or screen recordings

Not applicable.

How to set up and validate locally

Option A: without a Slack workspace

  1. In bin/rails console, build a notification message and inspect the attachment text:

    description = <<~TEXT
      <a href="https://example.org/one">https://example.com/two</a>
      [https://example.com/two](https://example.org/one)
      <https://example.org/one|https://example.com/two|extra>
    TEXT
    message = Integrations::ChatMessage::IssueMessage.new(
      object_kind: 'issue',
      user: { name: 'Dev', username: 'dev' },
      project_name: 'group/project',
      project_url: 'https://gitlab.example.com/group/project',
      object_attributes: {
        title: 'Title', iid: 1, action: 'open', state: 'opened',
        url: 'https://gitlab.example.com/group/project/-/issues/1',
        description: description
      }
    )
    puts message.attachments.first[:text]
  2. Expected:

    &lt;a href="https://example.org/one"&gt;https://example.com/two&lt;/a&gt;
    [https://example.com/two](https://example.org/one)
    &lt;https://example.org/one|https://example.com/two|extra&gt;

    None of the three is a <url|label> token, so Slack shows them as text and only auto-links the bare URLs to themselves.

Option B: with a Slack or Mattermost integration configured on a project

  1. Create an issue whose description contains the three lines above and confirm the posted message shows them as plain text, with only the bare URLs auto-linked to themselves.
  2. With Slack slash commands or the GitLab for Slack app configured, create an issue whose title contains <https://example.org/one|https://example.com/two>, run /gitlab issue search two, and confirm the response shows the title text without a clickable link.

Specs to run (the first command must run separately from the others because that spec uses fast_spec_helper):

bin/rspec spec/lib/slack_markdown_sanitizer_spec.rb
bin/rspec spec/lib/gitlab/slash_commands/presenters spec/models/integrations/chat_message
bin/rspec spec/models/integrations/slack_spec.rb spec/models/integrations/mattermost_spec.rb

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Sreeram Narayanan

Merge request reports

Loading
Loading