Repository: ansible/ansible

Issue:
Slack module escapes all message text, preventing use of Slack markup
##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
Slack module

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.2.0.0
```

##### CONFIGURATION
<!---
Mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->
N/A
##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say “N/A” for anything that is not platform-specific.
-->
N/A
##### SUMMARY
Slack notification messages are being HTML escaped before being sent to Slack, causing all Slack markup to appear as literal strings. This issue was introduced by [this PR](https://github.com/ansible/ansible-modules-extras/pull/3032).

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->
Execute the following command using a valid Slack webhook token:
<!--- Paste example playbooks or commands between quotes below -->
```yaml
- name: send a slack message
  slack:
    token: "{{ valid_token }}"
    username: "Test Message"
    msg: "<https://google.com|google>"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
A message from Test Message appearing as: [google](https://google.com)
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
A message from Test Message appearing as: `<https://google.com|google>`
<!--- Paste verbatim command output between quotes below -->


Discussion:
Prior to this bug, messages containing ampersands and quotes caused the task to fail due to the Slack API responding with a `400 Bad Request` error.
cc @ramondelafuente
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/notification/slack.py b/lib/ansible/modules/notification/slack.py
--- a/lib/ansible/modules/notification/slack.py
+++ b/lib/ansible/modules/notification/slack.py
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
 
+# (c) 2017, Steve Pletcher <steve@steve-pletcher.com>
 # (c) 2016, René Moser <mail@renemoser.net>
 # (c) 2015, Stefan Berggren <nsg@nsg.cc>
 # (c) 2014, Ramon de la Fuente <ramon@delafuente.nl>
@@ -58,7 +59,9 @@
     required: true
   msg:
     description:
-      - Message to send.
+      - Message to send. Note that the module does not handle escaping characters.
+        Plain-text angle brackets and ampersands should be converted to HTML entities (e.g. & to &amp;) before sending.
+        See Slack's documentation (U(https://api.slack.com/docs/message-formatting)) for more.
     required: false
     default: None
   channel:
@@ -164,32 +167,39 @@
           - title: System B
             value: 'load average: 5,16, 4,64, 2,43'
             short: True
+
+- name: Send a message with a link using Slack markup
+  slack:
+    token: thetoken/generatedby/slack
+    msg: We sent this message using <https://www.ansible.com|Ansible>!
+
+- name: Send a message with angle brackets and ampersands
+  slack:
+    token: thetoken/generatedby/slack
+    msg: This message has &lt;brackets&gt; &amp; ampersands in plain text.
 """
 
 OLD_SLACK_INCOMING_WEBHOOK = 'https://%s/services/hooks/incoming-webhook?token=%s'
 SLACK_INCOMING_WEBHOOK = 'https://hooks.slack.com/services/%s'
 
-# See https://api.slack.com/docs/message-formatting#how_to_escape_characters
-# Escaping quotes and apostrophe however is related to how Ansible handles them.
-html_escape_table = {
-    '&': "&amp;",
-    '>': "&gt;",
-    '<': "&lt;",
+# Escaping quotes and apostrophes to avoid ending string prematurely in ansible call.
+# We do not escape other characters used as Slack metacharacters (e.g. &, <, >).
+escape_table = {
     '"': "\"",
     "'": "\'",
 }
 
-def html_escape(text):
-    '''Produce entities within text.'''
-    return "".join(html_escape_table.get(c,c) for c in text)
+def escape_quotes(text):
+    '''Backslash any quotes within text.'''
+    return "".join(escape_table.get(c,c) for c in text)
 
 def build_payload_for_slack(module, text, channel, username, icon_url, icon_emoji, link_names, parse, color, attachments):
     payload = {}
     if color == "normal" and text is not None:
-        payload = dict(text=html_escape(text))
+        payload = dict(text=escape_quotes(text))
     elif text is not None:
         # With a custom color we have to set the message as attachment, and explicitly turn markdown parsing on for it.
-        payload = dict(attachments=[dict(text=html_escape(text), color=color, mrkdwn_in=["text"])])
+        payload = dict(attachments=[dict(text=escape_quotes(text), color=color, mrkdwn_in=["text"])])
     if channel is not None:
         if (channel[0] == '#') or (channel[0] == '@'):
             payload['channel'] = channel
@@ -221,7 +231,7 @@ def build_payload_for_slack(module, text, channel, username, icon_url, icon_emoj
         for attachment in attachments:
             for key in keys_to_escape:
                 if key in attachment:
-                    attachment[key] = html_escape(attachment[key])
+                    attachment[key] = escape_quotes(attachment[key])
 
             if 'fallback' not in attachment:
                 attachment['fallback'] = attachment['text']


Test patch: