Add Pushover notifications support (#3693)#3976
Conversation
Adds Pushover as a new alert channel with per-project API token override (project GUI token wins over global config). Includes db migration v2.19.3, config fields, TaskRunner wiring, and template. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds Pushover API token input to ProjectForm and `pushoverApiTokenOptional` translation key across all 17 language files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…es across versions
There was a problem hiding this comment.
Stale comment
Security review: PR #3976 (Pushover notifications)
Reviewed the added Pushover alert path end-to-end (project settings → task runner →
pushover.tmpl→ Pushover API).Outcome: 2 medium-severity issues found in new code.
Severity Issue Location Medium Pushover API token readable by any project member (not just project admins) db/Project.goMedium JSON injection via unescaped task/template fields can override Pushover API parameters services/tasks/templates/pushover.tmplNo authn/authz bypass, SSRF, SQL injection, or new dependency supply-chain concerns identified in this diff.
Sent by Cursor Automation: Find vulnerabilities
| "user": "{{ .Pushover.User }}", | ||
| "title": "{{ .Name }} #{{ .Task.ID }}: {{ .Task.Result }}", | ||
| "html": 1, | ||
| "message": "<b>{{ .Task.Result }}</b> {{ .Task.Version }} - {{ .Task.Desc }}\nby {{ .Author }}", |
There was a problem hiding this comment.
Severity: Medium — JSON injection / alert manipulation
This template builds JSON with text/template and no escaping. {{ .Task.Desc }} is populated from Task.Message, which task starters can set via POST /api/project/{id}/tasks (CanRunProjectTasks). Template name and author are also attacker-influenced.
Attack path: start a task with a crafted message such as x"}, "user": "<attacker_pushover_key>", "priority": 2, "x": ". When the alert fires, injected keys can override Pushover API fields (recipient user, priority, etc.) because duplicate JSON keys are commonly last-wins.
Impact: redirect operational alerts to an attacker-controlled Pushover account (information disclosure) or manipulate notification metadata; phishing via injected url is also possible depending on parser behavior.
Drop the GUI/DB-stored alert_pushover_token in favor of the global
SEMAPHORE_PUSHOVER_API_TOKEN config. Storing a per-project token and
serializing it via the project JSON exposed the credential to any
project member through GET /api/project/{id}.
- Remove AlertPushoverToken from db.Project and its INSERT/UPDATE columns
- Drop the alert_pushover_token migration column changes
- Remove the in-memory carrier from TaskRunner and the test alert sender
- sendPushoverAlert now uses only the global config token/user
- Remove the Pushover token field and i18n keys from ProjectForm
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Security review: PR #3976 (Pushover notifications)
Re-reviewed after latest changes (synchronize).
Outcome: 1 medium-severity issue in new code. Prior per-project credential disclosure finding is resolved — Pushover credentials now come only from global config (util.Config), not project API fields.
| Severity | Issue | Location |
|---|---|---|
| Medium | JSON injection via unescaped task fields can override Pushover API parameters (recipient user, url, etc.) |
services/tasks/templates/pushover.tmpl |
Resolved since last review: Per-project alert_pushover_token exposure (db/Project.go) no longer present in this PR.
No authn/authz bypass, SSRF, SQL injection, or meaningful supply-chain change identified (package-lock.json is a formatting-only churn).
Sent by Cursor Automation: Find vulnerabilities
| "user": "{{ .Pushover.User }}", | ||
| "title": "{{ .Name }} #{{ .Task.ID }}: {{ .Task.Result }}", | ||
| "html": 1, | ||
| "message": "<b>{{ .Task.Result }}</b> {{ .Task.Version }} - {{ .Task.Desc }}\nby {{ .Author }}", |
There was a problem hiding this comment.
Severity: Medium — JSON injection / alert hijacking
This template builds JSON with text/template and no escaping. {{ .Task.Desc }} is populated from Task.Message, which task starters supply via POST /api/project/{id}/tasks (requires CanRunProjectTasks). {{ .Author }} and template {{ .Name }} are also attacker-influenced.
Attack path: start a task with a crafted message such as x"}, "user": "<attacker_pushover_key>", "priority": 2, "x": ". When the alert fires, injected keys appear after the legitimate user field; duplicate-key parsers (including typical JSON decoders) are last-wins, so the notification is redirected to the attacker's Pushover account using the server's application token.
Impact: operational alert hijacking (information disclosure) and possible phishing via injected url/title fields.
Fix: build the request with encoding/json (or at minimum json template func escaping) instead of hand-assembled JSON strings.


Originally requested in #3693
Tested and verified
Tested in Docker
Performed tests agains the new feature, by defining setting in
Pushover API token UI field > config.json || environment variables