From 48e652c1b9a50296b19956c916df802e3ef0de79 Mon Sep 17 00:00:00 2001 From: Gilbert Sanchez Date: Sat, 23 May 2026 07:03:11 -0700 Subject: [PATCH] Fix YAML frontmatter injection in event submission workflow Replace hand-rolled f-string YAML serialization with yaml.safe_dump() to prevent frontmatter injection via unescaped quotes in issue body fields. Strip raw HTML from event descriptions to block stored XSS through goldmark's unsafe renderer. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/add-event.yml | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/.github/workflows/add-event.yml b/.github/workflows/add-event.yml index f8b56bff5..3c8b4c900 100644 --- a/.github/workflows/add-event.yml +++ b/.github/workflows/add-event.yml @@ -26,7 +26,7 @@ jobs: ISSUE_TITLE: ${{ github.event.issue.title }} run: | python3 - <<'PYEOF' - import os, re + import os, re, yaml body = os.environ['ISSUE_BODY'] issue_num = os.environ['ISSUE_NUMBER'] @@ -46,25 +46,26 @@ jobs: virtual_s = field('Is this a virtual event?') description = field('Event Description') - virtual = 'true' if virtual_s.startswith(('Yes', 'Hybrid')) else 'false' + virtual = virtual_s.startswith(('Yes', 'Hybrid')) # Generate a URL-safe slug: name + year slug = re.sub(r'[^a-z0-9]+', '-', name.lower()).strip('-') slug = f"{slug}-{start_date[:4]}" - end_line = f'\nendDate: "{end_date}"' if end_date else '' - content = f"""--- - title: "{name}" - startDate: "{start_date}"{end_line} - where: "{location}" - externalUrl: "{url}" - virtual: {virtual} - --- - {description} - """ - # Dedent (the heredoc indents every line) - import textwrap - content = textwrap.dedent(content) + # Strip raw HTML from description to prevent stored XSS via goldmark unsafe mode + description = re.sub(r'<[^>]+>', '', description) + + fm = { + 'title': name, + 'startDate': start_date, + 'where': location, + 'externalUrl': url, + 'virtual': virtual, + } + if end_date: + fm['endDate'] = end_date + + content = '---\n' + yaml.safe_dump(fm, default_flow_style=False, allow_unicode=True) + '---\n' + description + '\n' filepath = f'content/calendar/{slug}.md' os.makedirs('content/calendar', exist_ok=True)