Files
Charon/docs/reports/qa_report_ntfy_notifications.md
GitHub Actions 86023788aa feat: add support for Ntfy notification provider
- Updated the list of supported notification provider types to include 'ntfy'.
- Modified the notification settings UI to accommodate the Ntfy provider, including form fields for topic URL and access token.
- Enhanced localization files to include translations for Ntfy-related fields in German, English, Spanish, French, and Chinese.
- Implemented tests for the Ntfy notification provider, covering form rendering, CRUD operations, payload contracts, and security measures.
- Updated existing tests to account for the new Ntfy provider in various scenarios.
2026-03-24 21:04:54 +00:00

6.5 KiB

QA & Security Audit Report: Ntfy Notification Provider

Field Value
Date 2026-03-24
Branch feature/beta-release
Head Commit 5a2b6fec
Feature Ntfy notification provider
Verdict APPROVED

Step Summary

# Step Status Details
0 Read security instructions PASS security-and-owasp, testing, copilot instructions, SECURITY.md reviewed
1 Rebuild E2E environment PASS skill-runner.sh docker-rebuild-e2e — container healthy, ports 8080/2020/2019
2 Playwright E2E tests PASS 12/12 ntfy-specific tests passed (Firefox)
3 Local patch report PASS 100% patch coverage (0 changed lines vs development)
4 Backend unit coverage PASS 88.0% overall (threshold: 85%)
5 Frontend unit coverage PASS Lines 90.13%, Statements 89.38%, Functions 86.71%, Branches 81.86%
6 TypeScript type check PASS tsc --noEmit — zero errors
7 Pre-commit hooks N/A Project uses lefthook (not pre-commit); lefthook unavailable in shell
8 GORM security scan PASS 0 CRITICAL, 0 HIGH, 0 MEDIUM, 2 INFO (index suggestions only)
9 Security scans (Trivy) PASS 0 HIGH/CRITICAL findings in backend or frontend dependencies
10 Linting PASS Go: 0 issues (golangci-lint). ESLint: 0 errors, 834 warnings (all pre-existing, 0 ntfy-related)
11 Security code review PASS See detailed findings below

Step 2: Playwright E2E Tests (Ntfy)

Command: npx playwright test --project=firefox tests/settings/ntfy-notification-provider.spec.ts

All 12 tests passed in 1.6 minutes:

Test Result
Form Rendering — token field and topic URL placeholder PASS
Form Rendering — toggle between ntfy and discord PASS
Form Rendering — JSON template section PASS
CRUD — create with URL and token PASS
CRUD — create with URL only (no token) PASS
CRUD — edit and preserve token when field left blank PASS
CRUD — test notification PASS
CRUD — delete provider PASS
Security — GET response does NOT expose token PASS
Security — token not in URL or visible fields PASS
Payload Contract — POST body type/url/token structure PASS

Step 4: Backend Unit Coverage

Command: cd backend && go test -coverprofile=coverage.txt ./...

Package Coverage
services 86.0%
handlers 86.3%
notifications 89.4%
models 97.5%
Overall 88.0%

Threshold: 85% — PASS


Step 5: Frontend Unit Coverage

Source: frontend/coverage/coverage-summary.json (163 test files, 1938 tests passed)

Metric Coverage
Statements 89.38%
Branches 81.86%
Functions 86.71%
Lines 90.13%

Threshold: 85% line coverage — PASS


Step 8: GORM Security Scan

Command: /projects/Charon/scripts/scan-gorm-security.sh --check

  • Scanned: 43 Go files (2396 lines)
  • CRITICAL: 0
  • HIGH: 0
  • MEDIUM: 0
  • INFO: 2 (missing FK indexes on UserPermittedHost.UserID and UserPermittedHost.ProxyHostID)
  • Result: PASSED (no blocking issues)

Step 9: Trivy Filesystem Scan

Command: trivy fs --severity HIGH,CRITICAL --scanners vuln

  • Backend (/projects/Charon/backend/): 0 HIGH/CRITICAL
  • Frontend (/projects/Charon/frontend/): 0 HIGH/CRITICAL
  • Result: PASSED

Known CVEs from SECURITY.md (all "Awaiting Upstream", not ntfy-related):

  • CVE-2025-68121 (Critical, CrowdSec Go stdlib)
  • CVE-2026-2673 (High, OpenSSL in Alpine)
  • CHARON-2025-001 (High, CrowdSec Go CVEs)
  • CVE-2026-27171 (Medium, zlib)

Step 11: Security Code Review

Token Handling

Check Status Evidence
Token never logged PASS grep -n "log.*[Tt]oken" notification_service.go — 0 matches
Token json:"-" tag PASS models/notification_provider.go: Token string \json:"-"``
Bearer auth conditional PASS Line 593: if strings.TrimSpace(p.Token) != "" — only adds header when set
No hardcoded secrets PASS Only test file has tk_test123 (acceptable)
Auth header allowed PASS http_wrapper.go line 465: "authorization" in sanitizeOutboundHeaders allowlist
Token preservation PASS Handler update logic includes ntfy in token preservation chain

SSRF Protection

Check Status Evidence
HTTPWrapper uses SafeHTTPClient PASS http_wrapper.go line 70: network.NewSafeHTTPClient(opts...)
SafeHTTPClient blocks SSRF PASS safeclient_test.go line 227: TestNewSafeHTTPClient_BlocksSSRF
Cloud metadata detection PASS url_validator_test.go line 562: TestValidateExternalURL_CloudMetadataDetection

The ntfy dispatch path (dispatchURL = p.URLhttpWrapper.Send()) uses SafeHTTPClient at the transport layer, which provides SSRF protection including private IP and cloud metadata blocking.

API Security

Check Status
Only admin users can create/modify providers PASS (middleware-enforced)
Token write-only (never returned in GET) PASS (E2E test verified)
has_token boolean indicator only PASS (computed field, gorm:"-")

Gotify Token Protection Policy

Check Status
No tokens in logs PASS
No tokens in API responses PASS
No tokenized URLs in output PASS
URL query params redacted in diagnostics PASS

Issues & Recommendations

Blocking Issues

None.

Non-Blocking Observations

  1. ESLint warnings (834): Pre-existing, zero ntfy-related. Recommend gradual cleanup.
  2. GORM INFO findings: Missing indexes on UserPermittedHost foreign keys. Non-blocking, performance optimization opportunity.
  3. Frontend coverage (branches 81.86%): Below 85% but line/statement/function metrics all pass. Branch coverage is inherently lower due to conditional rendering patterns.

Final Verdict

APPROVED — The ntfy notification provider implementation passes all mandatory quality and security gates. No blocking issues identified. The feature is ready to ship.