mirror of
https://github.com/ellite/Wallos.git
synced 2026-07-31 18:25:58 -04:00
Raw CRLF in subscription name/notes/url passes through validate() untouched (it only escapes HTML metacharacters) and was interpolated directly into .ics VEVENT properties, letting an unescaped newline terminate the current property and inject arbitrary calendar events. Adds includes/ical_helper.php::icalEscape() (RFC 5545 value escaping) and applies it to every user-controlled value embedded in both iCal export endpoints, rather than stripping newlines in the shared validate() sanitizer (which would break legitimate multi-line notes used elsewhere in the app).
84 lines
3.2 KiB
PHP
84 lines
3.2 KiB
PHP
<?php
|
|
require_once '../../includes/connect_endpoint.php';
|
|
require_once '../../includes/validate_endpoint.php';
|
|
require_once '../../includes/getdbkeys.php';
|
|
require_once '../../includes/ical_helper.php';
|
|
|
|
$postData = file_get_contents("php://input");
|
|
$data = json_decode($postData, true);
|
|
|
|
$id = $data['id'];
|
|
|
|
$stmt = $db->prepare('SELECT * FROM subscriptions WHERE id = :id AND user_id = :userId');
|
|
$stmt->bindParam(':id', $id, SQLITE3_INTEGER);
|
|
$stmt->bindParam(':userId', $_SESSION['userId'], SQLITE3_INTEGER);
|
|
$result = $stmt->execute();
|
|
|
|
if ($result === false) {
|
|
die(json_encode([
|
|
'success' => false,
|
|
'message' => "Subscription not found"
|
|
]));
|
|
}
|
|
|
|
$subscription = $result->fetchArray(SQLITE3_ASSOC); // Fetch the subscription details as an associative array
|
|
|
|
if ($subscription) {
|
|
$subscription['payer_user'] = $members[$subscription['payer_user_id']]['name'];
|
|
$subscription['category'] = $categories[$subscription['category_id']]['name'];
|
|
$subscription['payment_method'] = $payment_methods[$subscription['payment_method_id']]['name'];
|
|
$subscription['currency'] = $currencies[$subscription['currency_id']]['symbol'];
|
|
$subscription['trigger'] = $subscription['notify_days_before'] ? $subscription['notify_days_before'] : 1;
|
|
$subscription['price'] = number_format($subscription['price'], 2);
|
|
|
|
// Create ICS from subscription information
|
|
$uid = 'wallos-subscription-' . $subscription['id'] . '@wallos';
|
|
$summary = icalEscape(html_entity_decode($subscription['name'], ENT_QUOTES, 'UTF-8'));
|
|
$notes = icalEscape(html_entity_decode($subscription['notes'], ENT_QUOTES, 'UTF-8'));
|
|
$category = icalEscape($subscription['category']);
|
|
$paymentMethod = icalEscape($subscription['payment_method']);
|
|
$payer = icalEscape($subscription['payer_user']);
|
|
$description = "Price: {$subscription['currency']}{$subscription['price']}\\nCategory: {$category}\\nPayment Method: {$paymentMethod}\\nPayer: {$payer}\\n\\nNotes: {$notes}";
|
|
|
|
$dtstamp = gmdate('Ymd\THis\Z');
|
|
$dtstart = (new DateTime($subscription['next_payment']))->format('Ymd');
|
|
$dtend = (new DateTime($subscription['next_payment']))->format('Ymd');
|
|
$location = icalEscape(isset($subscription['url']) ? $subscription['url'] : '');
|
|
$alarm_trigger = '-P' . $subscription['trigger'] . 'D';
|
|
|
|
$icsContent = <<<ICS
|
|
BEGIN:VCALENDAR
|
|
VERSION:2.0
|
|
PRODID:-//Your Organization//Your Application//EN
|
|
CALSCALE:GREGORIAN
|
|
METHOD:PUBLISH
|
|
BEGIN:VEVENT
|
|
UID:$uid
|
|
DTSTAMP:$dtstamp
|
|
SUMMARY:$summary
|
|
DESCRIPTION:$description
|
|
DTSTART;VALUE=DATE:$dtstart
|
|
DTEND;VALUE=DATE:$dtend
|
|
LOCATION:$location
|
|
STATUS:CONFIRMED
|
|
TRANSP:OPAQUE
|
|
BEGIN:VALARM
|
|
ACTION:DISPLAY
|
|
DESCRIPTION:Reminder
|
|
TRIGGER:$alarm_trigger
|
|
END:VALARM
|
|
END:VEVENT
|
|
END:VCALENDAR
|
|
ICS;
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'ics' => $icsContent,
|
|
'name' => $subscription['name']
|
|
]);
|
|
} else {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => "Subscription not found"
|
|
]);
|
|
} |