Skip to content

Markdown support#179

Open
bhavberi wants to merge 6 commits intomasterfrom
md-support
Open

Markdown support#179
bhavberi wants to merge 6 commits intomasterfrom
md-support

Conversation

@bhavberi
Copy link
Member

Club & Event Descriptions
Supports GFM (github flavoured markdown), emoji codes & emoticons.

Strips away any unwanted JS calls for security

@bhavberi bhavberi added the enhancement New feature or request label Feb 28, 2026
@socket-security
Copy link

socket-security bot commented Feb 28, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedremark-emoji@​5.0.29910010081100
Addedremark-gfm@​4.0.19910010083100
Added@​mdxeditor/​editor@​3.52.4961008492100

View full report

Comment on lines +8 to +22
return markdown
.replace(/^#+\s+/gm, "") // headings
.replace(/\*\*(.*?)\*\*/g, "$1") // bold
.replace(/\*(.*?)\*/g, "$1") // italic
.replace(/__(.*?)__/g, "$1") // bold
.replace(/_(.*?)_/g, "$1") // italic
.replace(/~~(.*?)~~/g, "$1") // strikethrough
.replace(/\[(.*?)\]\(.*?\)/g, "$1") // links
.replace(/!\[(.*?)\]\(.*?\)/g, "$1") // images
.replace(/`{1,3}.*?`{1,3}/gs, "") // code blocks
.replace(/^\s*[-*+]\s+/gm, "") // list items
.replace(/^\s*\d+\.\s+/gm, "") // numbered list items
.replace(/>\s+/gm, "") // blockquotes
// remove html tags but keep content
.replace(/<[^>]*>/g, "")

Check failure

Code scanning / CodeQL

Incomplete multi-character sanitization High

This string may still contain
<script
, which may cause an HTML element injection vulnerability.

Copilot Autofix

AI 13 days ago

In general, to fix incomplete multi-character sanitization you either (1) delegate to a robust sanitization/escape library, or (2) ensure your transformation cannot reintroduce unsafe sequences by repeatedly applying it or simplifying the pattern to operate on single characters instead of whole constructs. Here, stripMarkdown is intended to return plain text, not HTML, so the simplest and safest fix is to make sure that no raw < or > characters remain that could be interpreted as HTML, regardless of whether they look like complete tags.

The single best fix, without changing existing visible functionality in benign cases, is to strengthen the HTML-removal step. Instead of only removing <...> patterns, we can remove all < and > characters after we’ve done the markdown stripping. This aligns with the examples in the background where rewriting the regex to match single characters /<|>/g avoids multi-character pitfalls. Concretely, in src/utils/markdown.jsx we will replace the .replace(/<[^>]*>/g, "") step and its comment with a more robust approach that strips both angle brackets. Since we are only allowed to modify shown code and not change imports, we won’t introduce an external sanitizer; we’ll just adjust the regex.

Specifically, within stripMarkdown we will: (1) update the HTML-related comment to reflect that we now remove angle brackets rather than attempt to preserve tag content; and (2) change the regex on line 22 from /<[^>]*>/g to /<|>/g. No new methods or imports are required.

Suggested changeset 1
src/utils/markdown.jsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/utils/markdown.jsx b/src/utils/markdown.jsx
--- a/src/utils/markdown.jsx
+++ b/src/utils/markdown.jsx
@@ -18,8 +18,8 @@
     .replace(/^\s*[-*+]\s+/gm, "") // list items
     .replace(/^\s*\d+\.\s+/gm, "") // numbered list items
     .replace(/>\s+/gm, "") // blockquotes
-    // remove html tags but keep content
-    .replace(/<[^>]*>/g, "")
+    // remove angle brackets to avoid residual HTML-like fragments
+    .replace(/<|>/g, "")
     // remove more than 2 consecutive newlines
     .replace(/\n{3,}/g, "\n\n")
     .trim();
EOF
@@ -18,8 +18,8 @@
.replace(/^\s*[-*+]\s+/gm, "") // list items
.replace(/^\s*\d+\.\s+/gm, "") // numbered list items
.replace(/>\s+/gm, "") // blockquotes
// remove html tags but keep content
.replace(/<[^>]*>/g, "")
// remove angle brackets to avoid residual HTML-like fragments
.replace(/<|>/g, "")
// remove more than 2 consecutive newlines
.replace(/\n{3,}/g, "\n\n")
.trim();
Copilot is powered by AI and may make mistakes. Always verify output.
@socket-security
Copy link

socket-security bot commented Feb 28, 2026

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Protestware or unwanted behavior: npm es5-ext

Note: The script attempts to run a local post-install script, which could potentially contain malicious code. The error handling suggests that it is designed to fail silently, which is a common tactic in malicious scripts.

From: package-lock.jsonnpm/@mdxeditor/editor@3.52.4npm/es5-ext@0.10.64

ℹ Read more on: This package | This alert | What is protestware?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Consider that consuming this package may come along with functionality unrelated to its primary purpose.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/es5-ext@0.10.64. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@bhavberi bhavberi changed the title Md support Markdown support Feb 28, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Markdown/MDX-based rendering and editing for club/event descriptions (and changelog content), plus a utility to derive plain-text descriptions for metadata and exports.

Changes:

  • Introduces a server-side MarkdownRenderer using next-mdx-remote-client/rsc with GFM + emoji/emoticon support.
  • Adds a client-side Markdown editor modal powered by @mdxeditor/editor, integrated into Club/Event forms.
  • Uses stripMarkdown for metadata and some export surfaces (calendar, PDF/DOCX) to avoid raw markdown in plain-text contexts.

Reviewed changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/utils/markdown.jsx Adds stripMarkdown helper for producing plain text from markdown.
src/components/markdown/MarkdownRenderer.jsx New shared Markdown renderer (MDX evaluate + remark plugins).
src/components/markdown/MarkdownEditorModal.jsx New modal wrapper for the editor (client-only).
src/components/markdown/MDXEditorComponent.jsx MDXEditor configuration/plugins + styles.
src/components/events/report/EventpdfDownloads.jsx Uses stripMarkdown for event description in PDF HTML output.
src/components/events/report/EventDocxDownloads.jsx Uses stripMarkdown for budget item descriptions in DOCX.
src/components/events/EventForm.jsx Replaces inline description editing with modal markdown editor.
src/components/events/EventDetails.jsx Renders event description via MarkdownRenderer.
src/components/events/AddToCalendarBtn.jsx Uses stripMarkdown for calendar description.
src/components/clubs/ClubInfo.jsx Allows passing rendered JSX description (e.g., MarkdownRenderer output).
src/components/clubs/ClubForm.jsx Replaces inline description editing with modal markdown editor.
src/app/student-bodies/[id]/page.jsx Uses stripMarkdown for metadata description.
src/app/manage/events/[id]/page.jsx Uses stripMarkdown for metadata description.
src/app/manage/clubs/[id]/page.jsx Renders club description via MarkdownRenderer.
src/app/events/[id]/page.jsx Uses stripMarkdown for metadata description.
src/app/clubs/[id]/page.jsx Uses stripMarkdown for metadata; renders description via MarkdownRenderer.
src/app/changelog/page.jsx Switches changelog rendering to shared MarkdownRenderer.
package.json Adds editor + remark plugins dependencies.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 635 to 639
<td>${
(item?.description
? item?.description.replace(/\n/g, "<br />")
: null) || "Unknown"
item?.description
? item.description.replace(/\n/g, "<br />")
: "Unknown"
}</td>
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as earlier budget table: item.description is interpolated into the HTML without markdown stripping/escaping. This can break the generated PDF HTML (and potentially allow unwanted tags/URLs). Consider running stripMarkdown (and/or HTML-escaping) on these item descriptions before inserting them.

Copilot uses AI. Check for mistakes.
const [contentState, setContentState] = useState(content);

const handleSave = () => {
onSave(content);
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleSave is saving the original content prop instead of the edited contentState, so changes made in the editor won't be persisted. Use the current state value when calling onSave (and consider trimming there if needed).

Suggested change
onSave(content);
const trimmedContent =
typeof contentState === "string" ? contentState.trim() : contentState;
onSave(trimmedContent);

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +28
mdxOptions: {
disableImports: true,
disableExports: true,
remarkPlugins: [
remarkMdxRemoveExpressions,
remarkGfm,
[remarkEmoji, { emoticon: true }],
],
},
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current MDX/Markdown pipeline doesn’t sanitize links or raw HTML. With remarkGfm, inputs like [x](javascript:alert(1)) will render an <a href="javascript:...">, and MDX can also embed raw tags; remark-mdx-remove-expressions won’t prevent these. Add a sanitization step (e.g., rehype-sanitize with a strict schema / allowed protocols, and/or a custom link component that blocks javascript:/data: URLs) before rendering user-provided content.

Copilot uses AI. Check for mistakes.
item?.description?.replace(/\n/g, "<br />") ||
"Unknown"
item?.description
? item.description.replace(/\n/g, "<br />")
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Budget item descriptions are interpolated into an HTML string without stripping markdown/HTML (only newlines are converted). Since stripMarkdown is already used for the main event description in this template, apply the same sanitization/escaping to item.description here to avoid HTML injection/broken PDF output and keep formatting consistent.

Suggested change
? item.description.replace(/\n/g, "<br />")
? stripMarkdown(item.description).replace(
/\n/g,
"<br />",
)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants