Install verification: Ecosystem plugin: installs via dsh plugin add but declares no dsh.bundle and mounts no plugin capability — the L1-L5 install ladder does not rate it; the security scan still runs.Install verification details →
What it doesAI
DSH desktop tray plugin to launch/control Harness, show notifications, manage power, and integrate GitHub account.
- Double-click tray to launch DSH with no command line
- Popup notifications for task completion and permission/questions
- Power management: block sleep during tasks, auto-sleep when idle
Capability tags are AI-inferred or rule-extracted from the README (see badge) — vocabulary-limited, not install-verified. AI = inferred; README = literal keyword from the readme.
Installation
dsh plugin --profile web add github:KDronin/dsh-trayInstall method: GitHub · runtime test inconclusive (L5 unknown)
Verification & Compatibility○ Ecosystem plugin
Grouped by plugin version: each card lists the dsh versions actually tested against that artifact version and their install verdicts (L5 wins). Untested combinations are omitted — absence means untested, never assumed compatible. L1–L3 are a plugin-level static archive (independent of the dsh version — the same verdict on every dsh row): they sit in each row's level grid next to L4/L5, marked "static", while their evidence is stored once per plugin inside that row's "checks & evidence" instead of being duplicated per version.
External credentials (plugin-level): none detected
○ The author declares no dsh version requirement — install is not blocked by version.
| plugin \ dsh | dsh 0.2.1-rc.2 current | dsh 0.2.1-alpha.4 | dsh 0.2.1-alpha.2 | dsh 0.2.0-rc.2 | dsh 0.2.0-rc.1 |
|---|---|---|---|---|---|
| d903fc | · | · | · | · | · |
| c37017 | ○ | ○ | ○ | ○ | ○ |
d903fclocal2026-09-08
Ecosystem plugin: installs via dsh plugin add but declares no dsh.bundle and mounts no plugin capability — its level reads L5 · Ecosystem plugin.
c37017github○ 12 ecosystem plugin2026-10-19
https://github.com/KDronin/dsh-tray
○dsh 0.2.1-rc.2current2026-10-19Ecosystem plugin
- • Node.js: v24.14.0
- • DSH: 0.2.1-rc.2
- • Plugin artifact: github · https://github.com/KDronin/dsh-tray (version unstated)
ID: dsh-tray-9@c37017@dsh0.2.1-rc.2 · profile: l4-sandbox
Issued: 2026-10-19 · expires: 2026-10-26
View checks & evidence
not present in web-profile loader inventory
L1–L3 are plugin-level static conclusions (baseline, identical across dsh versions): evidence is stored once per plugin.
○dsh 0.2.1-alpha.42026-10-16Ecosystem plugin
- • Node.js: v24.14.0
- • DSH: 0.2.1-alpha.4
- • Plugin artifact: github · https://github.com/KDronin/dsh-tray (version unstated)
ID: dsh-tray-9@c37017@dsh0.2.1-alpha.4 · profile: l4-sandbox
Issued: 2026-10-16 · expires: 2026-10-23
View checks & evidence
not present in web-profile loader inventory
L1–L3 are plugin-level static conclusions (baseline, identical across dsh versions): evidence is stored once per plugin.
○dsh 0.2.1-alpha.22026-10-11Ecosystem plugin
- • Node.js: v24.14.0
- • DSH: 0.2.1-alpha.2
- • Plugin artifact: github · https://github.com/KDronin/dsh-tray (version unstated)
ID: dsh-tray-9@c37017@dsh0.2.1-alpha.2 · profile: l4-sandbox
Issued: 2026-10-11 · expires: 2026-10-18
View checks & evidence
not present in web-profile loader inventory
L1–L3 are plugin-level static conclusions (baseline, identical across dsh versions): evidence is stored once per plugin.
○dsh 0.2.0-rc.22026-10-09Ecosystem plugin
- • Node.js: v24.14.0
- • DSH: 0.2.0-rc.2
- • Plugin artifact: github · https://github.com/KDronin/dsh-tray (version unstated)
ID: dsh-tray-9@c37017@dsh0.2.0-rc.2 · profile: l4-sandbox
Issued: 2026-10-09 · expires: 2026-10-16
View checks & evidence
not present in web-profile loader inventory
L1–L3 are plugin-level static conclusions (baseline, identical across dsh versions): evidence is stored once per plugin.
○dsh 0.2.0-rc.12026-09-29Ecosystem plugin
- • Node.js: v24.14.0
- • DSH: 0.2.0-rc.1
- • Plugin artifact: github · https://github.com/KDronin/dsh-tray (version unstated)
ID: dsh-tray-9@c37017@dsh0.2.0-rc.1 · profile: l4-sandbox
Issued: 2026-09-29 · expires: 2026-10-06
View checks & evidence
not present in web-profile loader inventory
L1–L3 are plugin-level static conclusions (baseline, identical across dsh versions): evidence is stored once per plugin.
○dsh 0.1.7-rc.22026-09-25Ecosystem plugin
- • Node.js: v24.14.0
- • DSH: 0.1.7-rc.2
- • Plugin artifact: github · https://github.com/KDronin/dsh-tray (version unstated)
ID: dsh-tray-9@c37017@dsh0.1.7-rc.2 · profile: l4-sandbox
Issued: 2026-09-25 · expires: 2026-10-02
View checks & evidence
not present in web-profile loader inventory
L1–L3 are plugin-level static conclusions (baseline, identical across dsh versions): evidence is stored once per plugin.
○dsh 0.1.7-rc.12026-09-23Ecosystem plugin
- • Node.js: v24.14.0
- • DSH: 0.1.7-rc.1
- • Plugin artifact: github · https://github.com/KDronin/dsh-tray (version unstated)
ID: dsh-tray-9@c37017@dsh0.1.7-rc.1 · profile: l4-sandbox
Issued: 2026-09-23 · expires: 2026-09-30
View checks & evidence
This level was not executed in this scan.
L1–L3 are plugin-level static conclusions (baseline, identical across dsh versions): evidence is stored once per plugin.
○dsh 0.1.6-alpha.22026-09-20Ecosystem plugin
- • Node.js: v24.14.0
- • DSH: 0.1.6-alpha.2
- • Plugin artifact: github · https://github.com/KDronin/dsh-tray (version unstated)
ID: dsh-tray-9@c37017@dsh0.1.6-alpha.2 · profile: l4-sandbox
Issued: 2026-09-20 · expires: 2026-09-27
View checks & evidence
not present in web-profile loader inventory
L1–L3 are plugin-level static conclusions (baseline, identical across dsh versions): evidence is stored once per plugin.
○dsh 0.1.6-alpha.12026-09-17Ecosystem plugin
- • Node.js: v24.14.0
- • DSH: 0.1.6-alpha.1
- • Plugin artifact: github · https://github.com/KDronin/dsh-tray (version unstated)
ID: dsh-tray-9@c37017@dsh0.1.6-alpha.1 · profile: l4-sandbox
Issued: 2026-09-17 · expires: 2026-09-24
View checks & evidence
not present in web-profile loader inventory
L1–L3 are plugin-level static conclusions (baseline, identical across dsh versions): evidence is stored once per plugin.
○dsh 0.1.5-rc.22026-09-11Ecosystem plugin
- • Node.js: v24.14.0
- • DSH: 0.1.5-rc.2
- • Plugin artifact: github · https://github.com/KDronin/dsh-tray (version unstated)
ID: dsh-tray-9@c37017@dsh0.1.5-rc.2 · profile: l4-sandbox
Issued: 2026-09-11 · expires: 2026-09-18
View checks & evidence
not present in web-profile loader inventory
L1–L3 are plugin-level static conclusions (baseline, identical across dsh versions): evidence is stored once per plugin.
○dsh 0.1.3-alpha.22026-09-10Ecosystem plugin
- • Node.js: v24.14.0
- • DSH: 0.1.3-alpha.2
- • Plugin artifact: github · https://github.com/KDronin/dsh-tray (version unstated)
ID: dsh-tray-9@c37017@dsh0.1.3-alpha.2 · profile: l3-fullscan
Issued: 2026-09-10 · expires: 2026-09-17
View checks & evidence
not present in web-profile loader inventory
L1–L3 are plugin-level static conclusions (baseline, identical across dsh versions): evidence is stored once per plugin.
○prehistory2026-10-04Ecosystem plugin
- • Node.js: v24.14.0
- • DSH: unknown
- • Plugin artifact: github · https://github.com/KDronin/dsh-tray (version unstated)
ID: dsh-tray-9@c37017@dshunknown · profile: l4-sandbox
Issued: 2026-10-04 · expires: 2026-10-11
View checks & evidence
not present in web-profile loader inventory
L1–L3 are plugin-level static conclusions (baseline, identical across dsh versions): evidence is stored once per plugin.
Author badges
Embed the official badges in your README to showcase security & install-test status:
`[](https://www.dsh.so/artifact/dsh-tray-9/)`Security Report
Two automated layers, neither a manual review: a static scan (per-plugin rule checks) and a model-driven dynamic security audit that covers only the subset static-flagged at warn or above and never executes plugin code.
Static high; dynamic medium (authoritative when present) → overall medium.
v1.5.01.5.0git2026-10-241.5.0same versionsuspicious · gitVet is an AST static scan (npm artifact or git source).
show static findings (12)
- warningchildProcess · code-exec
tray-main.js:162child = spawn('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', ps], { - warningchildProcess · code-exec
tray-main.js:215spawn('rundll32.exe', ['powrprof.dll,SetSuspendState', '0,1,0'], { windowsHide: true, stdio: 'ignore' }) - warningchildProcess · code-exec
tray-main.js:295child = spawn('cmd.exe', ['/d', '/c', bat], { - warningshellExec · shell
tray-main.js:252execFile('cmd.exe', ['/d', '/c', cmd], { - warningshellExec · shell
tray-main.js:847execFile(cmd, args, { windowsHide: true, timeout: 30000 }, (err, stdout) => { - warningshellExec · shell
tray-main.js:1389execFileSync('taskkill', ['/pid', String(state.dshPid), '/T', '/F'], { windowsHide: true, stdio: 'ignore', timeout: 5000 }) - warningnetIp · network
tray-main.js:30const DSH_URL = 'http://127.0.0.1:3080' - warningnetIp · network
tray-main.js:925const WINDOW_CTRL = 'http://127.0.0.1:3490' - warningfsWrite · files
tray-main.js:90try { fs.appendFileSync(logPath, line) } catch { /* ignore */ } - warningfsWrite · files
tray-main.js:106fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2)) - warningfsWrite · files
tray-main.js:272fs.writeFileSync(bat, - warningnetIp · network
window-app/main.js:19let dshUrl = process.env.DSH_WEB_URL || 'http://127.0.0.1:3080'
show 8 audit findings
- mediumUnauthenticated loopback /notify endpoint can force sleep and spoof tray popups
tray-main.js:686The tray's HTTP server binds to 127.0.0.1:3489 with no token/origin check and parses JSON from any body regardless of Content-Type. handleNotify() then accepts caller-supplied payloads: type 'task-start' keeps the machine awake, type 'task-complete' can sleep the machine (maybeScheduleSleep -> rundll32 powrprof.dll,SetSuspendState), and any payload renders an attacker-chosen notification or approval/question window. Any local process, and a web page issuing a 'simple' cross-origin POST, can drive power state and UI. /status also answers with Access-Control-Allow-Origin: *.
- mediumLoopback window control port 3490 lets any local caller quit the window and load arbitrary URLs
window-main.js:257POST /url on 127.0.0.1:3490 feeds setDshUrl(), which navigates the DSH WebContentsView to an arbitrary caller-chosen URL and then treats that URL's origin as the trusted DSH origin (dshOrigin()), so links from that page no longer get pushed to the external browser. POST /quit terminates the window app. No origin/secret check, so any local process or cross-origin 'simple' POST can repoint the trusted Harness window at attacker content.
- mediumGitHub token written in plaintext to ~/.dsh/github-auth.json and ~/.git-credentials with a global credential helper
tray-main.js:876saveGithub() persists the raw PAT twice in cleartext, then runs 'git config --global credential.helper store' and pushes 'https://<login>:<token>@github.com' into ~/.git-credentials. Any local process able to read the user profile (or any home-directory sync/backup) obtains a repo/workflow-scoped GitHub credential, and the globally enabled plaintext store applies to every subsequent git operation.
- mediumshell.openExternal called on any non-DSH URL without scheme allowlist
window-main.js:153Any navigation or window.open target that is not the current DSH origin is handed to the OS opener. A DSH page (agent output, rendered markdown link) or a page placed there via the /url endpoint can pass file://, ms-msdt:, search-ms: or other registered handlers, which on Windows can launch local programs/handlers outside the app's sandbox.
- lowDSH launch-token URL is written to the app logs in plaintext
tray-main.js:408The authenticated dsh web URL (which carries the launch token that grants access to the tool-capable DSH web UI) is appended to dshtray.log and, via the child stdout/stderr consumer, to dsh-child.log. It is later read back by lastDshUrlFromLog(). Any process or backup that can read %APPDATA%\DSHTray obtains a reusable token.
- lowcordis.patch.yml loads harness plugin code from a hardcoded absolute path outside the package
plugin/cordis.patch.yml:5The shipped host-plane patch instructs the DSH harness to import a module from another user's home directory instead of the plugin file shipped in this repo (plugin/dsh-tray-notifier.mjs). Whatever sits at that path executes inside the harness with full plugin privileges and cannot be verified against the package.
- lowRegistry-supplied version string interpolated unquoted into a batch file run via cmd.exe
tray-main.js:277latestDshVersion() takes the version from `pnpm view`/`npm view` output and drops it verbatim into update-dsh.bat, which is then executed as cmd.exe /d /c <bat>. A hostile or compromised registry response (or a MITM'd registry read) containing batch metacharacters would execute additional commands in the user's context. The precondition is a compromised package registry, hence low.
- lowHarness plugin exposes an unauthenticated /answer endpoint on port 3491
plugin/dsh-tray-notifier.mjs:94Any local process (or cross-origin simple POST) can send {type:'approval', id, decision:'allowed-once'} to 127.0.0.1:3491 and resolve a pending permission request without user interaction; ids are derived from session/tool/callId plus Date.now() and Math.random(), which is what keeps the practical exploitability low, not any access control.
No backdoor or RCE: the verified risks are unauthenticated loopback control ports (3489/3490/3491), plaintext GitHub/DSH token storage, and unchecked shell.openExternal.
Rule (2026-10-24): when a dynamic conclusion exists it is authoritative (it may raise or lower the level); without one, the static/vet fused tier applies. The audit never writes the exemption list (exemptions are manual only); a wrong conclusion is withdrawn by human review and the tier falls back to static/vet.
Disclaimer: the static scan and the dynamic security audit are both automated analysis, not a security guarantee. Always review what you install.
Activity
Last commit 2026-09-14 · activity: Stable
• Repo created: 2026-08-16
• Stars: ★ 0 · Forks: 0
• Health: Stable — committed within 90 days
Source
GitHub: github.com/KDronin/dsh-tray
Author README
View on GitHub ↗The author's original README from the repository — fetched live, unedited (the English page loads the default README).
