fix: add credentials to all fetch calls for auth cookie support (#72)
* fix: add credentials to all fetch calls for auth cookie support - Add credentials: include to useMedications.ts fetch calls - Add credentials: include to MedicationsPage.tsx save function - Add credentials: include to useSettings.ts settings update - Add credentials: include to useShare.ts share generation - Add credentials: include to DashboardPage.tsx reminder email - Add credentials: include to PlannerPage.tsx usage calculation - Make create-release workflow skip if release already exists * fix: default to ntfy-style notifications for HTTP URLs - Change notification logic to use plain text format by default - Only use JSON format for known webhook services (Discord, Slack, Telegram, Gotify) - This fixes ntfy URLs not being recognized when hostname doesn't contain 'ntfy' * feat: highlight medication being edited - Add blue border and background to the medication row being edited - Show medication avatar and name in the edit form header - Makes it easy to identify which medication is being edited when there are many * fix: use proper URL parsing for webhook detection (CodeQL security fix) Replace vulnerable .includes() URL checks with proper URL hostname parsing to prevent bypass attacks (e.g., evil.com?hooks.slack.com). Fixes CodeQL alerts #33 and #34 (js/incomplete-url-substring-sanitization)
This commit is contained in:
@@ -482,10 +482,33 @@ export async function sendShoutrrrNotification(
|
||||
)
|
||||
.trim();
|
||||
|
||||
// Determine notification type based on validation result and URL pattern
|
||||
const isNtfyUrl = isNtfy || sanitizedUrl.includes("ntfy.sh") || sanitizedUrl.includes("/ntfy/");
|
||||
// Determine notification type based on URL hostname
|
||||
// Use JSON format only for known webhook services that require it
|
||||
// Use proper URL parsing to prevent bypass attacks (e.g., evil.com?hooks.slack.com)
|
||||
let isJsonWebhook = false;
|
||||
try {
|
||||
const parsedUrl = new URL(sanitizedUrl);
|
||||
const hostname = parsedUrl.hostname.toLowerCase();
|
||||
const pathname = parsedUrl.pathname.toLowerCase();
|
||||
|
||||
if (isNtfyUrl) {
|
||||
isJsonWebhook =
|
||||
// Discord webhooks
|
||||
((hostname === "discord.com" || hostname === "discordapp.com") && pathname.startsWith("/api/webhooks")) ||
|
||||
// Slack webhooks
|
||||
hostname === "hooks.slack.com" ||
|
||||
hostname.endsWith(".hooks.slack.com") ||
|
||||
// Telegram API
|
||||
hostname === "api.telegram.org" ||
|
||||
// Gotify (can be self-hosted, so check if "gotify" is in hostname)
|
||||
hostname.includes("gotify");
|
||||
} catch {
|
||||
// If URL parsing fails, default to ntfy-style
|
||||
isJsonWebhook = false;
|
||||
}
|
||||
|
||||
// Default to ntfy-style (plain text with Title header) for all other HTTP URLs
|
||||
// This works for ntfy, Apprise, and most simple push services
|
||||
if (!isJsonWebhook) {
|
||||
targetUrl = sanitizedUrl;
|
||||
headers = { Title: cleanTitle, Tags: "pill" };
|
||||
body = message;
|
||||
|
||||
Reference in New Issue
Block a user