From 82ed172608af38f6f7b886d661703ad4037e471f Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 00:40:21 -0600 Subject: [PATCH 01/18] Potential fix for code scanning alert no. 13: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- packages/lib/vultr/index.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/lib/vultr/index.ts b/packages/lib/vultr/index.ts index 519f298..daa2448 100644 --- a/packages/lib/vultr/index.ts +++ b/packages/lib/vultr/index.ts @@ -16,6 +16,17 @@ import { getIntegrations } from '@/packages/lib/config' const VULTR_API_BASE = 'https://api.vultr.com/v2' +function buildVultrApiUrl(path: string): string { + if (!path.startsWith('/')) { + throw new Error(`Invalid Vultr API path: "${path}"`) + } + if (path.startsWith('//') || path.includes('..') || path.includes('://')) { + throw new Error(`Unsafe Vultr API path: "${path}"`) + } + + return new URL(path, VULTR_API_BASE).toString() +} + async function getApiKey(): Promise { const integrations = await getIntegrations() const key = integrations?.vultr?.apiKey || process.env.VULTR_API_KEY @@ -28,7 +39,7 @@ async function vultrRequest( path: string, body?: unknown, ): Promise { - const response = await fetch(`${VULTR_API_BASE}${path}`, { + const response = await fetch(buildVultrApiUrl(path), { method, headers: { Authorization: `Bearer ${await getApiKey()}`, @@ -187,6 +198,10 @@ export async function listClusters(): Promise { /** List tiers available for a specific cluster. */ export async function listClusterTiers(clusterId: number): Promise { + if (!Number.isInteger(clusterId)) { + throw new Error(`Invalid clusterId: ${clusterId}`) + } + const data = await vultrRequest<{ tiers: VultrTier[] }>('GET', `/object-storage/clusters/${clusterId}/tiers`) return data.tiers ?? [] } From 4c6f3195e17715240982e7400a9493e9dcb2f749 Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 00:43:50 -0600 Subject: [PATCH 02/18] Potential fix for code scanning alert no. 12: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- app/api/admin/integrations/test/route.ts | 54 +++++++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/app/api/admin/integrations/test/route.ts b/app/api/admin/integrations/test/route.ts index 6d2aaea..0ce87df 100644 --- a/app/api/admin/integrations/test/route.ts +++ b/app/api/admin/integrations/test/route.ts @@ -13,6 +13,52 @@ interface TestIntegrationBody { credentials: Record } +function isPrivateOrLocalHost(hostname: string): boolean { + const host = hostname.toLowerCase() + + if (host === 'localhost' || host === '::1' || host === '[::1]') return true + if (host.endsWith('.localhost') || host.endsWith('.local')) return true + + // IPv4 checks + const ipv4Match = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/) + if (ipv4Match) { + const octets = ipv4Match.slice(1).map((n) => Number(n)) + if (octets.some((o) => Number.isNaN(o) || o < 0 || o > 255)) return true + + const [a, b] = octets + if (a === 10) return true + if (a === 127) return true + if (a === 169 && b === 254) return true + if (a === 172 && b >= 16 && b <= 31) return true + if (a === 192 && b === 168) return true + if (a === 0) return true + } + + // Common IPv6 local/private forms + const normalized = host.replace(/^\[|\]$/g, '') + if (normalized.startsWith('fc') || normalized.startsWith('fd')) return true // ULA + if (normalized.startsWith('fe80:')) return true // link-local + + return false +} + +function getSafeKenerOrigin(baseUrl?: string): string | null { + const candidate = (baseUrl && baseUrl.trim()) || 'https://emberlystat.us' + + let parsed: URL + try { + parsed = new URL(candidate) + } catch { + return null + } + + if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return null + if (parsed.username || parsed.password) return null + if (isPrivateOrLocalHost(parsed.hostname)) return null + + return parsed.origin +} + interface TestResult { ok: boolean message: string @@ -122,9 +168,13 @@ async function testGitHub(pat: string, org?: string): Promise { } async function testKener(apiKey: string, baseUrl?: string): Promise { - const url = (baseUrl || 'https://emberlystat.us').replace(/\/$/, '') + const origin = getSafeKenerOrigin(baseUrl) + if (!origin) { + return { ok: false, message: 'Invalid Kener base URL' } + } + try { - const res = await fetch(`${url}/api/v4/monitors`, { + const res = await fetch(`${origin}/api/v4/monitors`, { headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : {}, signal: AbortSignal.timeout(8000), }) From a2da666a379fcfe7f3221999076ee212d92254fc Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 11:58:52 -0600 Subject: [PATCH 03/18] Potential fix for code scanning alert no. 18: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- app/api/admin/integrations/test/route.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/api/admin/integrations/test/route.ts b/app/api/admin/integrations/test/route.ts index 0ce87df..018c42c 100644 --- a/app/api/admin/integrations/test/route.ts +++ b/app/api/admin/integrations/test/route.ts @@ -56,6 +56,9 @@ function getSafeKenerOrigin(baseUrl?: string): string | null { if (parsed.username || parsed.password) return null if (isPrivateOrLocalHost(parsed.hostname)) return null + const allowedOrigins = new Set(['https://emberlystat.us']) + if (!allowedOrigins.has(parsed.origin)) return null + return parsed.origin } From 12502ad3d992104adff11c3c34a566344ff77b15 Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 12:09:02 -0600 Subject: [PATCH 04/18] Potential fix for code scanning alert no. 11: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- app/api/admin/integrations/test/route.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/app/api/admin/integrations/test/route.ts b/app/api/admin/integrations/test/route.ts index 018c42c..f8a27f3 100644 --- a/app/api/admin/integrations/test/route.ts +++ b/app/api/admin/integrations/test/route.ts @@ -147,11 +147,23 @@ async function testDiscord(webhookUrl: string, botToken?: string, serverId?: str return { ok: false, message: 'No credentials configured' } } +function sanitizeGitHubOrg(org?: string): string | null { + if (!org) return null + const trimmed = org.trim() + // GitHub org/user name rules: alphanumeric or single hyphens, no leading/trailing hyphen, max 39 chars. + if (!/^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,37}[a-zA-Z0-9])?$/.test(trimmed)) return null + return trimmed +} + async function testGitHub(pat: string, org?: string): Promise { if (!pat) return { ok: false, message: 'Personal access token is not configured' } + const safeOrg = sanitizeGitHubOrg(org) + if (org && !safeOrg) { + return { ok: false, message: 'Invalid GitHub organization name format' } + } try { - const endpoint = org - ? `https://api.github.com/orgs/${org}` + const endpoint = safeOrg + ? `https://api.github.com/orgs/${safeOrg}` : 'https://api.github.com/user' const res = await fetch(endpoint, { headers: { @@ -161,10 +173,10 @@ async function testGitHub(pat: string, org?: string): Promise { }, }) if (res.status === 401) return { ok: false, message: 'Invalid personal access token' } - if (res.status === 404) return { ok: false, message: `Organization "${org}" not found` } + if (res.status === 404) return { ok: false, message: `Organization "${safeOrg ?? org}" not found` } if (!res.ok) return { ok: false, message: `GitHub API error (${res.status})` } const json = await res.json().catch(() => null) - return { ok: true, message: `Connected to GitHub${org ? ` — org: ${json?.name ?? org}` : ` — user: ${json?.login}`}` } + return { ok: true, message: `Connected to GitHub${safeOrg ? ` — org: ${json?.name ?? safeOrg}` : ` — user: ${json?.login}`}` } } catch (err) { return { ok: false, message: 'Failed to reach GitHub API', detail: String(err) } } From cb2dbf599c92d86135a825ac420b3055f5675e4e Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 12:06:23 -0600 Subject: [PATCH 05/18] Potential fix for code scanning alert no. 17: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- packages/lib/vultr/index.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/lib/vultr/index.ts b/packages/lib/vultr/index.ts index daa2448..df02e99 100644 --- a/packages/lib/vultr/index.ts +++ b/packages/lib/vultr/index.ts @@ -15,6 +15,7 @@ import { getIntegrations } from '@/packages/lib/config' const VULTR_API_BASE = 'https://api.vultr.com/v2' +const VULTR_API_BASE_URL = new URL(VULTR_API_BASE) function buildVultrApiUrl(path: string): string { if (!path.startsWith('/')) { @@ -24,7 +25,26 @@ function buildVultrApiUrl(path: string): string { throw new Error(`Unsafe Vultr API path: "${path}"`) } - return new URL(path, VULTR_API_BASE).toString() + const url = new URL(path, VULTR_API_BASE_URL) + + if (url.origin !== VULTR_API_BASE_URL.origin) { + throw new Error(`Unsafe Vultr API URL origin: "${url.origin}"`) + } + + if (!url.pathname.startsWith('/v2/')) { + throw new Error(`Unsafe Vultr API URL path: "${url.pathname}"`) + } + + const lowerPath = url.pathname.toLowerCase() + if ( + lowerPath.includes('%2e') || + lowerPath.includes('%2f') || + lowerPath.includes('%5c') + ) { + throw new Error(`Unsafe encoded Vultr API path: "${url.pathname}"`) + } + + return url.toString() } async function getApiKey(): Promise { From fbb803adb816574b4db9140f4c0a3df3ebee276b Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 12:16:45 -0600 Subject: [PATCH 06/18] Potential fix for code scanning alert no. 17: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- packages/lib/vultr/index.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/lib/vultr/index.ts b/packages/lib/vultr/index.ts index df02e99..755a9bf 100644 --- a/packages/lib/vultr/index.ts +++ b/packages/lib/vultr/index.ts @@ -218,11 +218,19 @@ export async function listClusters(): Promise { /** List tiers available for a specific cluster. */ export async function listClusterTiers(clusterId: number): Promise { - if (!Number.isInteger(clusterId)) { + if (!Number.isFinite(clusterId) || !Number.isSafeInteger(clusterId)) { throw new Error(`Invalid clusterId: ${clusterId}`) } - const data = await vultrRequest<{ tiers: VultrTier[] }>('GET', `/object-storage/clusters/${clusterId}/tiers`) + const safeClusterId = Math.trunc(clusterId) + if (safeClusterId !== clusterId || safeClusterId < 0) { + throw new Error(`Invalid clusterId: ${clusterId}`) + } + + const data = await vultrRequest<{ tiers: VultrTier[] }>( + 'GET', + `/object-storage/clusters/${safeClusterId}/tiers` + ) return data.tiers ?? [] } From 53362efca525643cb6f0d15767ba8eb2a3dcf7b8 Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 12:30:24 -0600 Subject: [PATCH 07/18] Potential fix for code scanning alert no. 15: Use of externally-controlled format string Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- packages/lib/security/backfill-password-history.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/security/backfill-password-history.ts b/packages/lib/security/backfill-password-history.ts index 1522989..c5b1171 100644 --- a/packages/lib/security/backfill-password-history.ts +++ b/packages/lib/security/backfill-password-history.ts @@ -130,7 +130,7 @@ export async function backfillUserPasswordHistory(userId: string): Promise Date: Fri, 12 Jun 2026 12:51:24 -0600 Subject: [PATCH 08/18] Potential fix for code scanning alert no. 16: Shell command built from environment values Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- scripts/generate-media-kit.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/scripts/generate-media-kit.ts b/scripts/generate-media-kit.ts index 30b9a98..9a96dc8 100644 --- a/scripts/generate-media-kit.ts +++ b/scripts/generate-media-kit.ts @@ -16,10 +16,10 @@ import { mkdir, writeFile, copyFile, readFile, readdir } from 'fs/promises' import { existsSync } from 'fs' import { join } from 'path' -import { exec } from 'child_process' +import { execFile } from 'child_process' import { promisify } from 'util' -const execAsync = promisify(exec) +const execFileAsync = promisify(execFile) const ROOT_DIR = process.cwd() const PUBLIC_DIR = join(ROOT_DIR, 'public') @@ -440,10 +440,19 @@ async function createZip() { try { if (process.platform === 'win32') { // PowerShell Compress-Archive - await execAsync(`powershell -Command "Compress-Archive -Path '${OUTPUT_DIR}\\*' -DestinationPath '${OUTPUT_ZIP}' -Force"`) + await execFileAsync('powershell', [ + '-NoProfile', + '-Command', + 'Compress-Archive', + '-Path', + `${OUTPUT_DIR}\\*`, + '-DestinationPath', + OUTPUT_ZIP, + '-Force', + ]) } else { // Unix zip - await execAsync(`cd "${OUTPUT_DIR}" && zip -r "${OUTPUT_ZIP}" .`) + await execFileAsync('zip', ['-r', OUTPUT_ZIP, '.'], { cwd: OUTPUT_DIR }) } console.log(`✓ Created zip: ${OUTPUT_ZIP}`) } catch (error) { From 6e77cfed5290069f312053d8665f6d043a1dde73 Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 13:11:45 -0600 Subject: [PATCH 09/18] Potential fix for code scanning alert no. 5: Replacement of a substring with itself Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- packages/lib/auth/login-detection.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/lib/auth/login-detection.ts b/packages/lib/auth/login-detection.ts index b0ec3bf..cdd2538 100644 --- a/packages/lib/auth/login-detection.ts +++ b/packages/lib/auth/login-detection.ts @@ -44,7 +44,6 @@ export function createDeviceFingerprint(userAgent: string | null | undefined): s .replace(/mac os x [\d_]+/g, 'macos') .replace(/android [\d.]+/g, 'android') .replace(/iphone os [\d_]+/g, 'ios') - .replace(/linux/g, 'linux') // Extract browser .replace(/chrome\/[\d.]+/g, 'chrome') .replace(/firefox\/[\d.]+/g, 'firefox') From 01c282099b457d68ae11606d0dac7f98e2fd17f1 Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 13:15:05 -0600 Subject: [PATCH 10/18] Potential fix for code scanning alert no. 6: Incomplete URL substring sanitization Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- app/api/discovery/signals/[id]/route.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/api/discovery/signals/[id]/route.ts b/app/api/discovery/signals/[id]/route.ts index 658a333..7372853 100644 --- a/app/api/discovery/signals/[id]/route.ts +++ b/app/api/discovery/signals/[id]/route.ts @@ -8,7 +8,9 @@ import { prisma } from '@/packages/lib/database/prisma' function parseGitHubUrl(url: string): { owner: string; repo: string } | null { try { const parsed = new URL(url) - if (!parsed.hostname.endsWith('github.com')) return null + const hostname = parsed.hostname.toLowerCase().replace(/\.$/, '') + const allowedHosts = new Set(['github.com', 'www.github.com']) + if (!allowedHosts.has(hostname)) return null const parts = parsed.pathname.replace(/^\//, '').split('/') if (parts.length < 2) return null return { owner: parts[0], repo: parts[1].replace(/\.git$/, '') } From 95b2c628c518ff1ce05356b1a3722d35c4d64413 Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 13:17:01 -0600 Subject: [PATCH 11/18] Potential fix for code scanning alert no. 7: Incomplete URL substring sanitization Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- app/api/discovery/signals/route.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/api/discovery/signals/route.ts b/app/api/discovery/signals/route.ts index 457f4ae..696d04e 100644 --- a/app/api/discovery/signals/route.ts +++ b/app/api/discovery/signals/route.ts @@ -12,7 +12,8 @@ import { function parseGitHubUrl(url: string): { owner: string; repo: string } | null { try { const parsed = new URL(url) - if (!parsed.hostname.endsWith('github.com')) return null + const host = parsed.hostname.toLowerCase() + if (host !== 'github.com' && !host.endsWith('.github.com')) return null const parts = parsed.pathname.replace(/^\//, '').split('/') if (parts.length < 2) return null return { owner: parts[0], repo: parts[1].replace(/\.git$/, '') } From 253749d25d1b63ce1b6b0d4354293d521b13d07c Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 13:22:00 -0600 Subject: [PATCH 12/18] Potential fix for code scanning alert no. 10: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- app/api/admin/integrations/test/route.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/api/admin/integrations/test/route.ts b/app/api/admin/integrations/test/route.ts index f8a27f3..d47f227 100644 --- a/app/api/admin/integrations/test/route.ts +++ b/app/api/admin/integrations/test/route.ts @@ -135,7 +135,20 @@ async function testDiscord(webhookUrl: string, botToken?: string, serverId?: str // Fall back to webhook validation if (webhookUrl) { try { - const res = await fetch(webhookUrl, { method: 'GET' }) + let parsed: URL + try { + parsed = new URL(webhookUrl) + } catch { + return { ok: false, message: 'Invalid webhook URL format' } + } + + const hostname = parsed.hostname.toLowerCase() + const isDiscordHost = hostname === 'discord.com' || hostname === 'discordapp.com' + if (parsed.protocol !== 'https:' || !isDiscordHost || isPrivateOrLocalHost(hostname)) { + return { ok: false, message: 'Webhook URL must be a valid Discord HTTPS URL' } + } + + const res = await fetch(parsed.toString(), { method: 'GET' }) if (res.status === 401) return { ok: false, message: 'Invalid webhook URL' } if (!res.ok) return { ok: false, message: `Discord webhook error (${res.status})` } return { ok: true, message: 'Discord webhook is valid' } From fd72c9f68cddaa45039a1e8bb62ac393f1f6fc34 Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 13:26:49 -0600 Subject: [PATCH 13/18] Update README.md --- .github/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/README.md b/.github/README.md index ae6bc44..b0b009c 100644 --- a/.github/README.md +++ b/.github/README.md @@ -2,6 +2,8 @@ Emberly is an open source platform for modern file storage, sharing, and identity verification. Build your digital presence with powerful tools for teams and individuals. +[![Build Checks](https://github.com/EmberlyOSS/Emberly/actions/workflows/build.yml/badge.svg)](https://github.com/EmberlyOSS/Emberly/actions/workflows/build.yml) + ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/EmberlyOSS/Emberly?utm_source=oss&utm_medium=github&utm_campaign=EmberlyOSS%2FEmberly&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews) ## Features From 01e0c451f845bd3eba7bf694eb01e57b11e217ce Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 13:27:44 -0600 Subject: [PATCH 14/18] Update README.md --- .github/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/README.md b/.github/README.md index b0b009c..c0e630d 100644 --- a/.github/README.md +++ b/.github/README.md @@ -2,9 +2,7 @@ Emberly is an open source platform for modern file storage, sharing, and identity verification. Build your digital presence with powerful tools for teams and individuals. -[![Build Checks](https://github.com/EmberlyOSS/Emberly/actions/workflows/build.yml/badge.svg)](https://github.com/EmberlyOSS/Emberly/actions/workflows/build.yml) - -![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/EmberlyOSS/Emberly?utm_source=oss&utm_medium=github&utm_campaign=EmberlyOSS%2FEmberly&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews) +[![Build Checks](https://github.com/EmberlyOSS/Emberly/actions/workflows/build.yml/badge.svg)](https://github.com/EmberlyOSS/Emberly/actions/workflows/build.yml) [![CodeQL Advanced](https://github.com/EmberlyOSS/Emberly/actions/workflows/codeql.yml/badge.svg)](https://github.com/EmberlyOSS/Emberly/actions/workflows/codeql.yml) ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/EmberlyOSS/Emberly?utm_source=oss&utm_medium=github&utm_campaign=EmberlyOSS%2FEmberly&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews) ## Features From 1df329e3e7dffcdec01e97956611b3af6233ae8a Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 13:30:03 -0600 Subject: [PATCH 15/18] Potential fix for code scanning alert no. 19: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- app/api/admin/integrations/test/route.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/api/admin/integrations/test/route.ts b/app/api/admin/integrations/test/route.ts index d47f227..7b1bb0d 100644 --- a/app/api/admin/integrations/test/route.ts +++ b/app/api/admin/integrations/test/route.ts @@ -148,7 +148,15 @@ async function testDiscord(webhookUrl: string, botToken?: string, serverId?: str return { ok: false, message: 'Webhook URL must be a valid Discord HTTPS URL' } } - const res = await fetch(parsed.toString(), { method: 'GET' }) + const match = parsed.pathname.match(/^\/api\/webhooks\/(\d+)\/([A-Za-z0-9._-]+)$/) + if (!match) { + return { ok: false, message: 'Webhook URL must match Discord webhook format' } + } + + const [, webhookId, webhookToken] = match + const safeWebhookUrl = `https://${hostname}/api/webhooks/${webhookId}/${webhookToken}` + + const res = await fetch(safeWebhookUrl, { method: 'GET' }) if (res.status === 401) return { ok: false, message: 'Invalid webhook URL' } if (!res.ok) return { ok: false, message: `Discord webhook error (${res.status})` } return { ok: true, message: 'Discord webhook is valid' } From e49e429e54b2416456aec4ae3118f92c73848fb4 Mon Sep 17 00:00:00 2001 From: fossabot Date: Fri, 12 Jun 2026 14:45:57 -0500 Subject: [PATCH 16/18] Add license scan report and status Signed off by: fossabot --- .github/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index c0e630d..7912692 100644 --- a/.github/README.md +++ b/.github/README.md @@ -3,6 +3,7 @@ Emberly is an open source platform for modern file storage, sharing, and identity verification. Build your digital presence with powerful tools for teams and individuals. [![Build Checks](https://github.com/EmberlyOSS/Emberly/actions/workflows/build.yml/badge.svg)](https://github.com/EmberlyOSS/Emberly/actions/workflows/build.yml) [![CodeQL Advanced](https://github.com/EmberlyOSS/Emberly/actions/workflows/codeql.yml/badge.svg)](https://github.com/EmberlyOSS/Emberly/actions/workflows/codeql.yml) ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/EmberlyOSS/Emberly?utm_source=oss&utm_medium=github&utm_campaign=EmberlyOSS%2FEmberly&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews) +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FEmberlyOSS%2FEmberly.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FEmberlyOSS%2FEmberly?ref=badge_shield) ## Features @@ -176,10 +177,13 @@ Get help and connect with the community: This project is licensed under the GNU Affero General Public License v3 (AGPL-3.0). See the [LICENSE](LICENSE) file for details. + +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FEmberlyOSS%2FEmberly.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FEmberlyOSS%2FEmberly?ref=badge_large) + ## Code of Conduct This project adheres to the Contributor Covenant Code of Conduct. By participating, you agree to uphold this code. See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for the full text. ## Acknowledgments -Thank you to all [contributors](https://github.com/EmberlyOSS/Emberly/graphs/contributors) who have helped make Emberly possible. We also appreciate the open source projects and communities that make this platform possible. +Thank you to all [contributors](https://github.com/EmberlyOSS/Emberly/graphs/contributors) who have helped make Emberly possible. We also appreciate the open source projects and communities that make this platform possible. \ No newline at end of file From 1bddffe64e796f8e3920a3d0e1f7bb0afbb07054 Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 13:50:53 -0600 Subject: [PATCH 17/18] Update README.md --- .github/README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/README.md b/.github/README.md index 7912692..d4f176e 100644 --- a/.github/README.md +++ b/.github/README.md @@ -3,7 +3,6 @@ Emberly is an open source platform for modern file storage, sharing, and identity verification. Build your digital presence with powerful tools for teams and individuals. [![Build Checks](https://github.com/EmberlyOSS/Emberly/actions/workflows/build.yml/badge.svg)](https://github.com/EmberlyOSS/Emberly/actions/workflows/build.yml) [![CodeQL Advanced](https://github.com/EmberlyOSS/Emberly/actions/workflows/codeql.yml/badge.svg)](https://github.com/EmberlyOSS/Emberly/actions/workflows/codeql.yml) ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/EmberlyOSS/Emberly?utm_source=oss&utm_medium=github&utm_campaign=EmberlyOSS%2FEmberly&labelColor=171717&color=FF570A&link=https%3A%2F%2Fcoderabbit.ai&label=CodeRabbit+Reviews) -[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FEmberlyOSS%2FEmberly.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FEmberlyOSS%2FEmberly?ref=badge_shield) ## Features @@ -178,12 +177,10 @@ Get help and connect with the community: This project is licensed under the GNU Affero General Public License v3 (AGPL-3.0). See the [LICENSE](LICENSE) file for details. -[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FEmberlyOSS%2FEmberly.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FEmberlyOSS%2FEmberly?ref=badge_large) - ## Code of Conduct This project adheres to the Contributor Covenant Code of Conduct. By participating, you agree to uphold this code. See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for the full text. ## Acknowledgments -Thank you to all [contributors](https://github.com/EmberlyOSS/Emberly/graphs/contributors) who have helped make Emberly possible. We also appreciate the open source projects and communities that make this platform possible. \ No newline at end of file +Thank you to all [contributors](https://github.com/EmberlyOSS/Emberly/graphs/contributors) who have helped make Emberly possible. We also appreciate the open source projects and communities that make this platform possible. From cdf9d717236e3e1a0d9197f42cd4c67352f47420 Mon Sep 17 00:00:00 2001 From: Pixelated Date: Fri, 12 Jun 2026 13:53:10 -0600 Subject: [PATCH 18/18] Potential fix for pull request finding 'CodeQL / Server-side request forgery' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- app/api/admin/integrations/test/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/admin/integrations/test/route.ts b/app/api/admin/integrations/test/route.ts index 7b1bb0d..e45a560 100644 --- a/app/api/admin/integrations/test/route.ts +++ b/app/api/admin/integrations/test/route.ts @@ -154,7 +154,7 @@ async function testDiscord(webhookUrl: string, botToken?: string, serverId?: str } const [, webhookId, webhookToken] = match - const safeWebhookUrl = `https://${hostname}/api/webhooks/${webhookId}/${webhookToken}` + const safeWebhookUrl = `https://discord.com/api/webhooks/${encodeURIComponent(webhookId)}/${encodeURIComponent(webhookToken)}` const res = await fetch(safeWebhookUrl, { method: 'GET' }) if (res.status === 401) return { ok: false, message: 'Invalid webhook URL' }