feat: Implement signalering widgets (#213)#270
feat: Implement signalering widgets (#213)#270rubenvdlinde wants to merge 18 commits intodevelopmentfrom
Conversation
- Fix OWASP A05:2021 (CWE-209): Do not expose exception messages to client in error responses - Changed generic error responses in DeadlineNotificationController (lines 118, 164) - Changed generic error responses in SignaleringConfigController (lines 85, 151, 229) - Fix OWASP A07:2021 (CWE-306): Add authentication check to notifyWebhook endpoint - Added Authorization/X-Webhook-Secret header validation - Fix OWASP A01:2021 (CWE-639): Add per-object authorization check to getDeadlines endpoint - Added check for case ownership, group membership, or admin status - Auto-fix phpcs style issues (phpcbf) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Quality Report — ConductionNL/procest @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ❌ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ❌ | ❌ | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-04-21 04:52 UTC
Download the full PDF report from the workflow artifacts.
| * @param string $caseId The case UUID | ||
| * @return JSONResponse | ||
| */ | ||
| public function getDeadlines(string $caseId): JSONResponse |
There was a problem hiding this comment.
[unfixed: read-only workspace; requires adding @NoAdminRequired docblock annotation] Rule: OWASP A01:2021 — Broken Access Control. Without @NoAdminRequired, Nextcloud's middleware restricts this endpoint to admin-only, blocking regular users from querying their own case deadline status. The method already performs proper per-user authorization (owner/group/admin check via IGroupManager). Fix: add @NoAdminRequired to the method docblock above getDeadlines().
| * | ||
| * @return JSONResponse | ||
| */ | ||
| public function notifyWebhook(): JSONResponse |
There was a problem hiding this comment.
[unfixed: read-only workspace; requires adding @publicpage, @NoCSRFRequired, @NoAdminRequired docblock annotations] Rule: OWASP A05:2021 — Security Misconfiguration / Nextcloud annotation contract. This endpoint is designed to receive POST callbacks from n8n (an external service with no Nextcloud session). Without @PublicPage, Nextcloud's auth middleware rejects all unauthenticated requests before they reach this method. Without @NoCSRFRequired, Nextcloud's CSRF middleware blocks POST requests from n8n (which cannot supply a CSRF token). The existing header-based auth check inside the method body is therefore never reached. Fix: add @NoAdminRequired, @NoCSRFRequired, and @PublicPage to this method's docblock, matching the pattern in NrcController and ZtcController. Note: also fix the presence-only auth check (see separate comment).
| try { | ||
| // Verify webhook authentication | ||
| // Check for Authorization header with Bearer token or webhook secret | ||
| $authHeader = $this->request->getHeader('Authorization'); |
There was a problem hiding this comment.
[unfixed: requires architectural fix — implement shared secret validation] Rule: OWASP A07:2021 — Identification and Authentication Failures / CWE-287. The webhook auth check only verifies that the Authorization or X-Webhook-Secret header is non-empty — it does NOT compare the value against a configured shared secret. Any caller who supplies Authorization: x or X-Webhook-Secret: x bypasses authentication. Fix: read the configured secret from SettingsService (e.g. getConfigValue('n8n_signalering_webhook_secret')), then compare using hash_equals($configuredSecret, $incomingSecret) to prevent timing attacks. Return 401 if values do not match.
| ]; | ||
|
|
||
| $client = $this->clientService->newClient(); | ||
| $response = $client->post($n8nWebhookUrl, [ |
There was a problem hiding this comment.
[unfixed: requires URL validation logic — admin-accessible SSRF vector] Rule: OWASP A10:2021 — Server-Side Request Forgery (SSRF) / CWE-918. The n8n webhook URL is read directly from app settings and passed without validation to IClientService::post(). An admin who controls app settings could set this to an internal network address (e.g. http://169.254.169.254/, http://localhost:6379/) to probe internal infrastructure. Fix: validate with filter_var($n8nWebhookUrl, FILTER_VALIDATE_URL) and restrict to https:// scheme before calling $client->post(). Example: if (str_starts_with($n8nWebhookUrl, 'https://') === false) { return false; }.
Security Review — Clyde BarcodeResult: FAIL (0 fixed, 4 unfixed — 2 WARNING, 2 WARNING)
Findings summary
False positives suppressed
Checks run
Required actions before merge
See inline comments for per-finding detail. |
Closes #213
Summary
Implement Signalering Widgets V1 for Procest — dashboard widgets providing proactive deadline awareness to case handlers. This feature surfaces three time-sensitive alert types:
All computation is client-side from already-loaded case/task data; no new API calls. Fully integrated with Nextcloud Dashboard widget system, allowing users to add/remove widgets from picker.
Spec Reference
openspec/specs/signalering-widgets/spec.md— V1 Accepted Criteria ✓openspec/changes/signalering-widgets/design.mdChanges
lib/Dashboard/DeadlineAlertsWidget.php— Nextcloud IWidget adapter (adds @SPEC tag)lib/Dashboard/TaskRemindersWidget.php— Nextcloud IWidget adapter (adds @SPEC tag)lib/Dashboard/StalledCasesWidget.php— Nextcloud IWidget adapter (adds @SPEC tag)src/utils/dashboardHelpers.js— Helper functions (getDeadlineAlerts, getTaskDueReminders, getStalledCases)src/views/dashboard/DeadlineAlerts.vue— Dashboard component showing deadline alertssrc/views/dashboard/TaskDueReminders.vue— Dashboard component showing task reminderssrc/views/dashboard/StalledCases.vue— Dashboard component showing stalled casessrc/views/widgets/DeadlineAlertsWidget.vue— Nextcloud widget componentsrc/views/widgets/TaskRemindersWidget.vue— Nextcloud widget componentsrc/views/widgets/StalledCasesWidget.vue— Nextcloud widget componentlib/AppInfo/Application.php— Widget registration (already done)openspec/changes/signalering-widgets/design.md— Design documentopenspec/changes/signalering-widgets/tasks.md— Task trackingTest Coverage
tests/Unit/Dashboard/SignaleringWidgetsTest.php— Unit tests for all three widget classesNotes
🤖 Generated with Claude Code