opsdash-app/opsdash/lib/Service/NotesService.php
blade34242 b723d9f3d0 security: store notes raw, escape at render time
htmlspecialchars was applied on write which caused literal entities to
appear in the editor and could mask double-escaping in future render
paths. Content is now stored as-is; the Vue component must use text
interpolation (not v-html) to display it safely.
2026-05-06 10:22:16 +07:00

77 lines
2.8 KiB
PHP

<?php
declare(strict_types=1);
namespace OCA\Opsdash\Service;
use OCP\IConfig;
use Psr\Log\LoggerInterface;
class NotesService {
private const APP_NAME = 'opsdash';
public function __construct(
private IConfig $config,
private CalendarAccessService $calendarAccess,
private LoggerInterface $logger,
) {
}
public function getNotes(string $uid, string $range, int $offset): array {
$userTz = $this->calendarAccess->resolveUserTimezone($uid);
$weekStart = $this->calendarAccess->resolveUserWeekStart($uid);
[$fromCur] = $this->calendarAccess->rangeBounds($range, $offset, $userTz, $weekStart);
[$fromPrev] = $this->calendarAccess->rangeBounds($range, $offset - 1, $userTz, $weekStart);
$keyCurrent = $this->calendarAccess->notesKey($range, $fromCur);
$keyPrevious = $this->calendarAccess->notesKey($range, $fromPrev);
$current = (string)$this->config->getUserValue($uid, self::APP_NAME, $keyCurrent, '');
$previous = (string)$this->config->getUserValue($uid, self::APP_NAME, $keyPrevious, '');
$history = [];
for ($i = 1; $i <= 4; $i++) {
[$fromHistory] = $this->calendarAccess->rangeBounds($range, $offset - $i, $userTz, $weekStart);
$keyHistory = $this->calendarAccess->notesKey($range, $fromHistory);
$content = (string)$this->config->getUserValue($uid, self::APP_NAME, $keyHistory, '');
if (trim($content) === '') {
continue;
}
$history[] = [
'offset' => $i,
'from' => $fromHistory->format('Y-m-d'),
'content' => $content,
];
}
return [
'period' => [
'type' => $range,
'current_from' => $fromCur->format('Y-m-d'),
'previous_from' => $fromPrev->format('Y-m-d'),
],
'notes' => [
'current' => $current,
'previous' => $previous,
'history' => $history,
],
];
}
public function saveNotes(string $uid, string $range, int $offset, string $content): bool {
if (strlen($content) > 32768) {
$content = substr($content, 0, 32768);
}
$userTz = $this->calendarAccess->resolveUserTimezone($uid);
$weekStart = $this->calendarAccess->resolveUserWeekStart($uid);
[$fromCur] = $this->calendarAccess->rangeBounds($range, $offset, $userTz, $weekStart);
$key = $this->calendarAccess->notesKey($range, $fromCur);
try {
$this->config->setUserValue($uid, self::APP_NAME, $key, $content);
return true;
} catch (\Throwable $e) {
$this->logger->error('notes save failed: ' . $e->getMessage(), ['app' => self::APP_NAME]);
return false;
}
}
}