Skip to content

Commit 1670c48

Browse files
Copilotseven7ty
andauthored
fix: sanitize untrusted fenced markdown content across outputs
Agent-Logs-Url: https://github.com/statch/gitbot/sessions/46b730a3-a822-49cd-9473-c994c6dd7db6 Co-authored-by: seven7ty <63970738+seven7ty@users.noreply.github.com>
1 parent a768383 commit 1670c48

12 files changed

Lines changed: 40 additions & 19 deletions

File tree

bot.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,16 @@ async def do_cog_op(ctx: GitBotContext, cog: str, op: str) -> None:
2727
getattr(bot, f'{op}_extension')(str(ext))
2828
done += 1
2929
except commands.ExtensionError as e:
30-
await ctx.error(f'**Exception during batch-{op}ing:**\n```{e}```')
30+
error: str = bot.mgr.sanitize_codeblock_content(str(e))
31+
await ctx.error(f'**Exception during batch-{op}ing:**\n```{error}```')
3132
else:
3233
await ctx.success(f'All extensions **successfully {op}ed.** ({done})')
3334
else:
3435
try:
3536
getattr(bot, f'{op}_extension')(cog)
3637
except commands.ExtensionError as e:
37-
await ctx.error(f'**Exception while {op}ing** `{cog}`**:**\n```{e}```')
38+
error: str = bot.mgr.sanitize_codeblock_content(str(e))
39+
await ctx.error(f'**Exception while {op}ing** `{cog}`**:**\n```{error}```')
3840
else:
3941
await ctx.success(f'**Successfully {op}ed** `{cog}`.')
4042

cogs/backend/handle/errors/_error_tools.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,18 @@ async def log_error_in_discord(ctx: GitBotContext, error: Exception, _actual=Non
4040
color=0xda4353,
4141
title=f'Error in `{ctx.command}` command'
4242
)
43-
embed.add_field(name='Message', value=f'```{error}```')
44-
embed.add_field(name='Traceback', value=f'```{format_tb(error.__traceback__)}```')
43+
message: str = ctx.bot.mgr.sanitize_codeblock_content(str(error))
44+
tb: str = ctx.bot.mgr.sanitize_codeblock_content(format_tb(error.__traceback__))
45+
embed.add_field(name='Message', value=f'```{message}```')
46+
embed.add_field(name='Traceback', value=f'```{tb}```')
4547
embed.add_field(name='Arguments',
46-
value=f'```properties\nargs={format_args(ctx.args)}\nkwargs={format_kwargs(ctx.kwargs)}```')
48+
value=f'```properties\nargs={ctx.bot.mgr.sanitize_codeblock_content(format_args(ctx.args))}\nkwargs={ctx.bot.mgr.sanitize_codeblock_content(format_kwargs(ctx.kwargs))}```')
4749
elif isinstance(error, commands.CommandNotFound):
50+
error_text: str = ctx.bot.mgr.sanitize_codeblock_content(str(error))
4851
embed: GitBotEmbed = GitBotEmbed(
4952
color=0x0384fc,
5053
title='Nonexistent command!',
51-
description=f'```{(error := str(error))}```',
54+
description=f'```{error_text}```',
5255
footer='Closest existing command: ' + closest_existing_command_from_error(ctx.bot, error)
5356
)
5457
elif isinstance(error, (BadRequest, QueryError)):
@@ -57,10 +60,13 @@ async def log_error_in_discord(ctx: GitBotContext, error: Exception, _actual=Non
5760
title='GitHub API Error!',
5861
footer='The logs may contain more information.'
5962
)
60-
embed.add_field(name='API Response', value=f'```diff\n- {error}```')
61-
embed.add_field(name='Code Location', value=f'```{ctx.gh_query_debug.code_location}```')
63+
api_response: str = ctx.bot.mgr.sanitize_codeblock_content(str(error))
64+
code_location: str = ctx.bot.mgr.sanitize_codeblock_content(ctx.gh_query_debug.code_location)
65+
embed.add_field(name='API Response', value=f'```diff\n- {api_response}```')
66+
embed.add_field(name='Code Location', value=f'```{code_location}```')
6267
if ctx.gh_query_debug.additional_info:
63-
embed.add_field(name='Additional Info', value=f'```{ctx.gh_query_debug.additional_info}```')
68+
additional_info: str = ctx.bot.mgr.sanitize_codeblock_content(ctx.gh_query_debug.additional_info)
69+
embed.add_field(name='Additional Info', value=f'```{additional_info}```')
6470
if ctx.gh_query_debug.status_code is not None:
6571
embed.add_field(name='Status Code', value=f'```c\n{error.status_code}```')
6672
ping_owner: bool = True

cogs/backend/workers/release_feed.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ async def handle_feed_repo(self,
7979

8080
if body := new_release['release']['descriptionHTML']:
8181
body: str = ' '.join(BeautifulSoup(body, features='html.parser').getText().split())
82-
body: str = f"```{self.bot.mgr.truncate(body, 400, full_word=True)}```".strip()
82+
body = self.bot.mgr.sanitize_codeblock_content(self.bot.mgr.truncate(body, 400, full_word=True))
83+
body: str = f"```{body}```".strip()
8384

8485
author: dict = new_release["release"]["author"]
8586
author: str = f'Created by [{author["login"]}]({author["url"]}) on ' \

cogs/github/base/org.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ async def org_info_command(self, ctx: GitBotContext, organization: Optional[GitH
5454
members: str = ctx.fmt('one_member', f"{org['html_url']}/people") + '\n'
5555
email: str = f"Email: {org['email']}\n" if 'email' in org and org["email"] is not None else '\n'
5656
if org['description'] is not None and len(org['description']) > 0:
57-
embed.add_field(name=f":notepad_spiral: {ctx.l.org.info.glossary[0]}:", value=f"```{org['description']}```")
57+
description: str = self.bot.mgr.sanitize_codeblock_content(org['description'])
58+
embed.add_field(name=f":notepad_spiral: {ctx.l.org.info.glossary[0]}:", value=f"```{description}```")
5859
repos: str = f"{ctx.l.org.info.repos.no_repos}\n" if org['public_repos'] == 0 else ctx.fmt('repos plural',
5960
org['public_repos'],
6061
f"{org['url']}?tab=repositories") + '\n'

cogs/github/base/repo/repo.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ async def repo_info_command(self, ctx: GitBotContext, repo: Optional[GitHubRepos
6060
open_issues: int = r['issues']['totalCount']
6161

6262
if r['description'] is not None and len(r['description']) != 0:
63+
description: str = self.bot.mgr.sanitize_codeblock_content(re.sub(MARKDOWN_EMOJI_RE, '', r['description']).strip())
6364
embed.add_field(name=f":notepad_spiral: {ctx.l.repo.info.glossary[0]}:",
64-
value=f"```{re.sub(MARKDOWN_EMOJI_RE, '', r['description']).strip()}```")
65+
value=f"```{description}```")
6566

6667
watchers: str = ctx.fmt('watchers plural', watch, f"{r['url']}/watchers") if watch != 1 else ctx.fmt(
6768
'watchers singular', f"{r['url']}/watchers")
@@ -162,12 +163,13 @@ async def repo_files_command(self, ctx: GitBotContext, repo_or_path: GitHubRepos
162163
embeds: list = []
163164

164165
def make_embed(items: list, ftr: str | None = None) -> GitBotEmbed:
166+
sanitized_path: str | None = self.bot.mgr.sanitize_codeblock_content(path) if path else None
165167
return GitBotEmbed(
166168
color=self.bot.mgr.c.rounded,
167169
title=f'{self.bot.mgr.e.branch} `{repo}`' + (f' ({ref})' if ref else ''),
168170
description='\n'.join(
169171
f'{self.bot.mgr.e.file} [`{f["name"]}`]({f["html_url"]})' if f['type'] == 'file' else
170-
f'{self.bot.mgr.e.folder} [`{f["name"]}`]({f["html_url"]})' for f in items) + ('\n' + f'```{path}```' if path else ''),
172+
f'{self.bot.mgr.e.folder} [`{f["name"]}`]({f["html_url"]})' for f in items) + ('\n' + f'```{sanitized_path}```' if sanitized_path else ''),
171173
url=link,
172174
footer=ftr
173175
)

cogs/github/base/user.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ async def user_info_command(self, ctx: GitBotContext, user: Optional[GitHubUser]
5050
contrib_count: Optional[tuple] = u['contributions']
5151
orgs_c: int = u['organizations_count']
5252
if "bio" in u and u['bio'] is not None and len(u['bio']) > 0:
53-
embed.add_field(name=f":notepad_spiral: {ctx.l.user.info.glossary[0]}:", value=f"```{u['bio']}```")
53+
bio: str = self.bot.mgr.sanitize_codeblock_content(u['bio'])
54+
embed.add_field(name=f":notepad_spiral: {ctx.l.user.info.glossary[0]}:", value=f"```{bio}```")
5455
occupation: str = (ctx.l.user.info.company + '\n').format(u['company']) if 'company' in u and u[
5556
'company'] is not None else ctx.l.user.info.no_company + '\n'
5657
orgs: str = (ctx.l.user.info.orgs.plural.format(orgs_c) if orgs_c != 0 else ctx.l.user.info.orgs.no_orgs) + '\n'

cogs/github/numbered/gist.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ async def build_gist_embed(self,
9090

9191
stargazers_and_comments = f'{stargazers} and {comments}'
9292
info: str = f'{created_at}{updated_at}{stargazers_and_comments}'
93-
content: str = self.bot.mgr.truncate(first_file['text'], 749, ' [...]').replace('`', '\u200b`')
93+
content: str = self.bot.mgr.sanitize_codeblock_content(self.bot.mgr.truncate(first_file['text'], 749, ' [...]'))
9494
embed.add_field(name=f':notepad_spiral: {ctx.l.gist.glossary[0]}:', value=f"```{self.extension(first_file['extension'])}\n{content}```")
9595
embed.add_field(name=f":mag_right: {ctx.l.gist.glossary[1]}:", value=info)
9696

cogs/github/numbered/issue.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ async def issue_command(self, ctx: GitBotContext, repo: GitHubRepository, issue_
6464
else:
6565
body = None
6666
if body:
67+
body = self.bot.mgr.sanitize_codeblock_content(body)
6768
embed.add_field(name=f':notepad_spiral: {ctx.l.issue.glossary[0]}:', value=f"```{body}```", inline=False)
6869
user: str = ctx.fmt('created_at',
6970
self.bot.mgr.to_github_hyperlink(issue['author']['login']),

cogs/github/numbered/pr.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@ async def pull_request_command(self,
6666
)
6767
embed.set_thumbnail(url=pr['author']['avatarUrl'])
6868
if all(['bodyText' in pr and pr['bodyText'], len(pr['bodyText'])]):
69+
body: str = self.bot.mgr.sanitize_codeblock_content(self.bot.mgr.truncate(pr['bodyText'], 387, full_word=True))
6970
embed.add_field(name=':notepad_spiral: Body:',
70-
value=f"```{self.bot.mgr.truncate(pr['bodyText'], 387, full_word=True)}```",
71+
value=f"```{body}```",
7172
inline=False)
7273
user: str = ctx.fmt('created_at',
7374
self.bot.mgr.to_github_hyperlink(pr['author']['login']),

cogs/github/other/snippets/_snippet_tools.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ async def get_text_from_url_and_data(ctx: 'GitBotContext',
4848
text: str = ''.join(lines)
4949
ctx.lines_total = len(lines_)
5050
if text:
51-
return f"```{extension}\n{text.rstrip()}\n```" if wrap_in_codeblock else text.rstrip(), None
51+
text = text.rstrip()
52+
if wrap_in_codeblock:
53+
text = ctx.bot.mgr.sanitize_codeblock_content(text)
54+
return f"```{extension}\n{text}\n```", None
55+
return text, None
5256
return '', None
5357

5458

0 commit comments

Comments
 (0)