Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion supabase/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,4 @@ redirect_uri = ""
url = ""

[functions.new_user]
verify_jwt = false
verify_jwt = true
21 changes: 19 additions & 2 deletions supabase/functions/new_user/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@ if (!SLACK_WEBHOOK_URL) {
}
const SLACK_CHANNEL = 'user-signups'

// Simple email validation to reject obviously malformed input
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/

function sanitizeForSlack(text: string): string {
// Escape Slack mrkdwn special characters to prevent injection
return text.replace(/[&<>*_~`|]/g, (ch) => `&#${ch.charCodeAt(0)};`)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use Slack-supported escaping for sanitized emails

sanitizeForSlack replaces formatting characters with numeric entities (for example _&#95;), but Slack mrkdwn only decodes &amp;, &lt;, and &gt;; other entities are rendered literally. In practice, common addresses like first_last@example.com will be posted as first&#95;last@example.com, which makes signup alerts inaccurate and harder to use even for legitimate users. Restrict escaping to Slack-supported control chars (&, <, >) or disable mrkdwn for this message.

Useful? React with 👍 / 👎.

}

function sendSlackMessage(email: string) {
const message = `:fire: *New User Signed Up *\n*User*\n${email}\n`
const safeEmail = sanitizeForSlack(email)
const message = `:fire: *New User Signed Up *\n*User*\n${safeEmail}\n`

return fetch(SLACK_WEBHOOK_URL, {
method: 'POST',
Expand Down Expand Up @@ -42,7 +51,15 @@ serve(async (req) => {
*/
const data = await req.json()
const userRecord = data.record
const userEmail = userRecord.email

if (!userRecord || typeof userRecord.email !== 'string' || !EMAIL_RE.test(userRecord.email)) {
return new Response(JSON.stringify({ error: 'Invalid or missing email' }), {
status: 400,
headers: { 'Content-Type': 'application/json' },
})
}

const userEmail: string = userRecord.email

console.log(`New user with email: ${userEmail} created`)

Expand Down