Repository navigation
Pre-V1 audit: security review and threat model #178
Copy link
Copy link
Open
5 / 65 of 6 issues completedDescription
One of the audits to finish before 1.0. This issue tracks the security review; each finding is a sub-issue.
Scope
The six areas named for the audit, checked against the threat model in SECURITY.md:
- the dashboard's admin API and sign-in (tokens, session cookie, Host check, roles, trusted identity headers, gateway keys)
- CSRF (the same-origin check on writes, side-effecting GETs, CORS)
- the CLI connectors and every other place that starts a process or opens a URL
- the key ring (Data Protection, the secret file,
${env:}/${secret:}references, masking) - the desktop app's web-view bridge
- update-feed integrity (Velopack feed, publisher check, installers' update step)
Method
- Read SECURITY.md, docs/dashboard.md, docs/host.md, docs/providers.md, docs/desktop.md and docs/enterprise.md.
- Read the code:
DashboardAccess,RouterDashboardEndpoints,SetupApi,EvaluationApi,TuningApi,GatewayGuard,RouterConfiguration(masking, reference rules, process settings),Secrets/*,KeyRing,OwnerOnly,Connectors/Cli/*,AnswerJudge,ServicePlatforms,Desktop/*(bridge, window, shell, updater, publisher check) anddeploy/*. - Wrote one scratch unit check of the reference rules.
- Ran the existing security tests (
DashboardRole|Secrets|DashboardAccess|CliScratch|CliExecutable|Gateway|UpdatePublisher|DesktopUpdater|KeyRing|Session): 147 passed, 0 failed.
What held up
- Tokens and gateway keys: compared in constant time (SHA-256, then
FixedTimeEquals). Dashboard tokens need at least 16 characters and must differ from each other. Access is refused when a token reference does not resolve. - Session cookie: HttpOnly, SameSite=Strict, Secure over HTTPS, and HMAC-signed. Sessions are revoked on the server and the revocations are kept in SQLite.
- Host check: guards against DNS rebinding.
TrustedIdentityHeader: believed only from a known proxy, and refused when repeated or given as a list.- Metrics role: works as an allow-list.
- Same-origin check: applies to every non-GET and to GETs marked as writes.
- Browser hardening: no CORS, a strict CSP with no inline script,
frame-ancestors 'none'. - Masked secrets: a masked
***only restores a secret to the same destination. - CLI process start:
ArgumentList, no shell, and absolute-path resolution that skips relativePATHentries.cmd,.batand.ps1refused, and the whole process tree is killed- an owner-only scratch parent, and the prompt sent on stdin
- Key ring: 0700/0600 on Unix and DPAPI on Windows. Unencrypted keys are replaced.
- Update feed: refuses plain HTTP.
- Publisher check: guards against path traversal and uses WinVerifyTrust or
codesign --deep --strict.
Threat model
| Area | Asset | Attacker position | Defence | Verdict |
|---|---|---|---|---|
| 1 Admin API | prompts, config, routing | web page in the browser | Host check, same-origin on writes, no CORS, SameSite=Strict | Holds |
| 1 Admin API | prompts, config, routing | another local user or process | loopback rules, process-setting and reference rules | See sub-issues and private reports |
| 1 Admin API | dashboard, gateway | another machine on the LAN | tokens, sign-in throttle, sessions, gateway keys, known-proxy identity | Dashboard holds; gateway keys and the groups header need hardening |
| 2 CSRF | writes | web page | Origin / Sec-Fetch-Site on non-GET and write-marked GETs |
Holds for writes; see private reports |
| 3 CLI connectors | subscriptions, child processes | remote client; content in a prompt | AllowRemoteClients, argv, scrubbed environment, private working directory, tools off |
Mostly holds; the scrubbed environment is a short deny-list |
| 4 Key ring | keys, secret file | local user, backups, compromised backend | Data Protection, owner-only files, masking, redaction | Holds in the default profile directories; custom directories on Windows keep inherited ACLs |
| 5 Web-view bridge | the desktop app | page script, a link in the web view | same-router navigation check, a 3-action bridge | Low risk; no scheme allow-list |
| 6 Updates | the installed app | compromised feed or mirror | HTTPS, Velopack hash from the same feed, optional publisher policy | Integrity rests on GitHub unless the policy names a publisher |
Reported privately
4 findings that could be used against a current install are not filed here. Following SECURITY.md, they go to private security advisories and will be published when they are fixed.
Not checked here
These need Windows or macOS machines and are covered by the hands-on pass:
- WebView2 and WKWebView navigation behaviour
- DPAPI
- Authenticode and codesign
Also not checked:
- Velopack's own hash-verification internals
- the vendor CLIs' argument parsers
- a full XSS pass over the dashboard scripts (spot-checked only: the
h()escaping and the CSP) - prompted tool-call forgery, which is tracked in Make prompted tool calls harder to forge #111
Done when
- Every sub-issue is closed or deliberately deferred past 1.0.
- The private advisories are fixed and published.
Reactions are currently unavailable
Activity
siebe41 commentedon Oct 22, 2026
The XSS pass this audit left undone ("spot-checked only") is in draft PR #334. Every template interpolation and HTML sink in the dashboard scripts was traced to its source. Text from gateway clients, model backends, note files and the URL was escaped everywhere. Connector, profile and virtual-model names, which only an admin writes, went unescaped into form attributes; the CSP kept injected handlers from running. Those are now escaped. A new dashboard-e2e spec (
xss.spec.js) puts an attribute-breaking marker into configuration names, a gateway request, note files, a discovered lineup and notices, and fails if any page turns it into markup or trips the CSP.test(dashboard-e2e): the XSS spec checks each page after its data arr…
fix(dashboard): the digest escapes an unknown signal kind once again,…
siebe41 commentedon Oct 22, 2026
New finding from the review of #262: #338. A packaged connector type that runs a program is trusted to enforce
AllowRemoteClients,MaxConcurrencyandMaxQueueitself. The router enforces none of them for packages outside this repository.Merge pull request #334 from siebe41/sprint/hardening/H07e-dashboard-…