Slack: inbound bold conversion doubles literal asterisks in prose and inside inline code
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2.4k
- Forks
- 314
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 63
Description
Bug Description
slackMrkdwnToMarkdown converts Slack bold to markdown bold with
markdown.replace(/(?<![_*\\])\*([^*\n]+)\*(?![_*])/g, "**$1**");
The pattern matches any two asterisks on the same line, whatever surrounds them. A message that uses * as a multiplication sign or a wildcard has each matched asterisk doubled. The rule also runs over inline code spans, so the contents of `…` are changed.
Slack's own renderer does not show these messages as bold: its mrkdwn bold needs a non-space character directly inside each marker.
Steps to Reproduce
- In Slack, send the bot
cost is 2 * 3 seats and 4 * 5 licenses, or a message with an asterisk inside inline code such asrun `rm -rf *` then 5 * 3. - Read
message.textandstringifyMarkdown(message.formatted)in the handler, or run the code sample below, which calls the same converter the adapter uses.
Expected Behavior
Both messages come through with their asterisks unchanged and no strong node. this is *bold* text still converts to **bold**.
Actual Behavior
Input (event.text) |
message.text |
stringifyMarkdown |
|---|---|---|
cost is 2 * 3 seats and 4 * 5 licenses |
cost is 2 ** 3 seats and 4 ** 5 licenses |
cost is 2 \*\* 3 seats and 4 \*\* 5 licenses |
run `rm -rf *` then 5 * 3 |
run rm -rf ** then 5 ** 3 |
run `rm -rf **` then 5 \*\* 3 |
In the second row the match starts at the asterisk inside the code span and ends at the one outside it, so the code span's content changes from rm -rf * to rm -rf **.
Code Sample
import { SlackFormatConverter } from "@chat-adapter/slack";
import { stringifyMarkdown, toPlainText } from "chat";
const converter = new SlackFormatConverter();
for (const input of ["cost is 2 * 3 seats and 4 * 5 licenses", "run `rm -rf *` then 5 * 3"]) {
const ast = converter.toAst(input);
console.log(toPlainText(ast));
console.log(stringifyMarkdown(ast));
}
Chat SDK Version
4.30.0
Node.js Version
22.21.1
Platform Adapter
Microsoft Teams, Slack
Operating System
Linux
Additional Context
Possible approaches.
- Require a non-space character after the opening asterisk and before the closing one:
/(?<![_*\\])\*(\S(?:[^*\n]*\S)?)\*(?![_*])/g. On the inputs above it leaves both messages unchanged and still converts*bold*,*a* and *b c*, and*x*. The strikethrough rule (~…~) has the same shape and would take the same change. - Skip inline code spans. 4.40.0 already splits the input on code fences (
convertMrkdwnWithCodeFences) so fenced code is not rewritten; inline`…`spans still are. - When the event carries
rich_textblocks, bold is unambiguous there (style.boldon the text element), so reading formatting fromblockswould avoid the regex for user-typed messages.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at SlackFormatConverter and the slackMrkdwnToMarkdown conversion path, then run the provided TypeScript sample with the multiplication, inline-code, and bold inputs. Compare toPlainText and stringifyMarkdown output with the expected behavior; the work is done when literal asterisks and inline-code contents remain unchanged while genuine Slack bold still converts to Markdown strong text.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 74/100