Install verification: sandbox install ok, web smoke failed (L5) · tested on dsh 0.2.1-rc.2 · last checked Install verification details →
What it doesAI
Single-instance multi-tenant plugin for DSH, with Caddy+Authelia auth, ACLs, and event filtering to securely isolate sessions/workspaces.
- Caddy + Authelia front-end for user identity extraction
- Session/workspace isolation via owner ACLs, shadow routing, event filters
- Supports workspace sharing, invite codes, and public site publication
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:Choi-Peng/dsh-tenancyInstall method: GitHub · sandbox install ok, web smoke failed (L5)
Verification & Compatibility
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
Based on 1 requirement(s) across 3 @deepseek-ai/dsh-* packages. raw ranges
^0.1.1-rc.2 ×3
✗ Running dsh 0.2.1-rc.2 is outside the declared range — the official install gate rejects it (incompatible-version).
To force-install, grant an exact-version exemption: dsh plugin allow-version dsh-tenancy@0.1.0-alpha.2 0.2.1-rc.2 (the exemption lives in the local profile; this site does not assume one)
Whether it actually runs is a separate question → see the measured matrix below (L1–L5).
| 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 |
|---|---|---|---|---|---|
| 2df41c | ✗ | ✗ | ✗ | ✗ | ✗ |
2df41cgithub11 failed2026-10-19
https://github.com/Choi-Peng/dsh-tenancy
✗dsh 0.2.1-rc.2current2026-10-19L5 · failed
- • Node.js: v24.14.0
- • DSH: 0.2.1-rc.2
- • Plugin artifact: github · https://github.com/Choi-Peng/dsh-tenancy (version unstated)
ID: dsh-tenancy@2df41c@dsh0.2.1-rc.2 · profile: l4-sandbox
Issued: 2026-10-19 · expires: 2026-10-26
View checks & evidence
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-16L5 · failed
- • Node.js: v24.14.0
- • DSH: 0.2.1-alpha.4
- • Plugin artifact: github · https://github.com/Choi-Peng/dsh-tenancy (version unstated)
ID: dsh-tenancy@2df41c@dsh0.2.1-alpha.4 · profile: l4-sandbox
Issued: 2026-10-16 · expires: 2026-10-23
View checks & evidence
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-11L5 · failed
- • Node.js: v24.14.0
- • DSH: 0.2.1-alpha.2
- • Plugin artifact: github · https://github.com/Choi-Peng/dsh-tenancy (version unstated)
ID: dsh-tenancy@2df41c@dsh0.2.1-alpha.2 · profile: l4-sandbox
Issued: 2026-10-11 · expires: 2026-10-18
View checks & evidence
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-09L5 · failed
- • Node.js: v24.14.0
- • DSH: 0.2.0-rc.2
- • Plugin artifact: github · https://github.com/Choi-Peng/dsh-tenancy (version unstated)
ID: dsh-tenancy@2df41c@dsh0.2.0-rc.2 · profile: l4-sandbox
Issued: 2026-10-09 · expires: 2026-10-16
View checks & evidence
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-29L5 · failed
- • Node.js: v24.14.0
- • DSH: 0.2.0-rc.1
- • Plugin artifact: github · https://github.com/Choi-Peng/dsh-tenancy (version unstated)
ID: dsh-tenancy@2df41c@dsh0.2.0-rc.1 · profile: l4-sandbox
Issued: 2026-09-29 · expires: 2026-10-06
View checks & evidence
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-25L5 · failed
- • Node.js: v24.14.0
- • DSH: 0.1.7-rc.2
- • Plugin artifact: github · https://github.com/Choi-Peng/dsh-tenancy (version unstated)
ID: dsh-tenancy@2df41c@dsh0.1.7-rc.2 · profile: l4-sandbox
Issued: 2026-09-25 · expires: 2026-10-02
View checks & evidence
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-23not tested
- • Node.js: v24.14.0
- • DSH: 0.1.7-rc.1
- • Plugin artifact: github · https://github.com/Choi-Peng/dsh-tenancy (version unstated)
ID: dsh-tenancy@2df41c@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-20L5 · failed
- • Node.js: v24.14.0
- • DSH: 0.1.6-alpha.2
- • Plugin artifact: github · https://github.com/Choi-Peng/dsh-tenancy (version unstated)
ID: dsh-tenancy@2df41c@dsh0.1.6-alpha.2 · profile: l3-fullscan
Issued: 2026-09-20 · expires: 2026-09-27
View checks & evidence
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-17L5 · failed
- • Node.js: v24.14.0
- • DSH: 0.1.6-alpha.1
- • Plugin artifact: github · https://github.com/Choi-Peng/dsh-tenancy (version unstated)
ID: dsh-tenancy@2df41c@dsh0.1.6-alpha.1 · profile: l4-sandbox
Issued: 2026-09-17 · expires: 2026-09-24
View checks & evidence
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-11L5 · failed
- • Node.js: v24.14.0
- • DSH: 0.1.5-rc.2
- • Plugin artifact: github · https://github.com/Choi-Peng/dsh-tenancy (version unstated)
ID: dsh-tenancy@2df41c@dsh0.1.5-rc.2 · profile: l4-sandbox
Issued: 2026-09-11 · expires: 2026-09-18
View checks & evidence
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-09L5 · failed
- • Node.js: v24.14.0
- • DSH: 0.1.3-alpha.2
- • Plugin artifact: github · https://github.com/Choi-Peng/dsh-tenancy (version unstated)
ID: dsh-tenancy@2df41c@dsh0.1.3-alpha.2 · profile: l4-sandbox
Issued: 2026-09-09 · expires: 2026-09-16
View checks & evidence
L1–L3 are plugin-level static conclusions (baseline, identical across dsh versions): evidence is stored once per plugin.
✗prehistory2026-10-04L5 · failed
- • Node.js: v24.14.0
- • DSH: unknown
- • Plugin artifact: github · https://github.com/Choi-Peng/dsh-tenancy (version unstated)
ID: dsh-tenancy@2df41c@dshunknown · profile: l4-sandbox
Issued: 2026-10-04 · expires: 2026-10-11
View checks & evidence
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-tenancy/)`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 low; dynamic medium (authoritative when present) → overall medium.
v0.1.0-alpha.2dsh manifest0.1.0-alpha.2git2026-10-240.1.0-alpha.2same versionclean · gitVet is an AST static scan (npm artifact or git source).
show static findings (12)
- criticalshellExec · shell
lib/sysuser.js:85out = await exec('getent', ['passwd', String(username)]); - criticalshellExec · shell
lib/sysuser.js:124await exec('useradd', [...base, '--user-group', username], opts); - warningnetIp · network
install.sh:371if curl -sf http://127.0.0.1:9091/auth/api/health >/dev/null 2>&1; then - warningnetIp · network
install.sh:441http://127.0.0.1:9443/api/host.describe 2>/dev/null || echo "000")" - warningnetIp · network
install.sh:570if curl -sf http://127.0.0.1:9091/auth/api/health >/dev/null 2>&1; then - warningcurlDownload · network
install.sh:150curl -sSL --retry 3 -o "$authelia_cache_tgz" "$authelia_url" - warningcurlDownload · network
install.sh:208curl -sSL --retry 3 -o "$caddy_cache_tgz" "$caddy_url" - warningcurlDownload · network
install.sh:439http_code="$(curl -s -o /dev/null -w "%{http_code}" \ - warningshellExec · shell
lib/index.js:1506execFile('/bin/sh', ['-c', 'pm2 restart dsh-web'], { timeout: 30_000 }, (error, stdout, stderr) => { - warningshellExec · shell
lib/index.js:1556const child = execFile('zstd', ['-c', filePath], { timeout: 30_000 }, (error, stdout) => { - warningshellExec · shell
lib/register.js:185execFile('/bin/sh', ['-c', 'exec "$AUTHELIA_BIN" crypto hash generate argon2 --password "$REG_PW"'], { - warningshellExec · shell
lib/register.js:221execFile('/bin/sh', ['-c', 'exec "$AUTHELIA_BIN" crypto hash validate --password "$REG_PW" -- "$REG_HASH"'], {
show 5 audit findings
- mediumRoot installer downloads Authelia/Caddy binaries with no checksum or signature verification and accepts an operator-supplied URL prefix
install.sh:149The script is documented as `sudo bash install.sh` and installs both tarballs as root, then runs them as systemd services. The only integrity checks are `gzip -t` and non-empty file size, so a compromised GitHub release/mirror (or a GITHUB_PROXY value pointing at an attacker-controlled host, which is prepended to the URL) yields persistent root-level code execution on the host.
- mediumDefault sharedSecret='' plus localIsAdmin=true makes any header-less request from a trusted Host a full-admin 'local' principal
lib/index.js:112With the shipped defaults, any request that clears the origin check but carries neither X-Dsh-Tenancy-Key nor the identity header is treated as the `local` principal with every admin group: full cross-tenant read/write, /tenancy/*, claim, sysuser, password and restart authority. The origin check only rejects non-loopback Host unless the operator adds its public domain to trustedHosts — which deployments must do for /register and /tenancy/* — so the secret is the effective identity boundary, and install.sh currently never writes it (the auto-config step is commented out with `# deploy_dsh_plugi
- lowRegistration displayName is taken unvalidated and flows into the Authelia users.yml block
lib/register.js:362Unlike username/password/email there is no server-side length or character check on displayName (only a client-side maxlength='64'), and it is interpolated into the YAML block that Authelia parses. If buildUserBlock (lib/util.js, not harvested) does not quote/escape the value, a crafted display name such as a quote followed by `\ngroups:` lines could corrupt the block or inject YAML keys (e.g. adding the new account to dsh-admins). Exploitability depends on code that could not be inspected here.
- lowList/search response filtering fails open for rows without a sessionId field
lib/index.jsAny row whose tenant key is not exposed as `sessionId` (different field name, nested object, future schema change) is passed through unfiltered to a non-admin member, defeating the per-session visibility filter for that row shape. The same fail-open pattern appears for non-object entries in workspace.list.
- lowAdmin-only restart endpoint executes `pm2 restart dsh-web` through /bin/sh -c (hardcoded, not injectable)
lib/index.jsThis is the construct the heuristic scanner flags as shell execution. The command string is a constant with no interpolated request data, and the route sits behind the trusted-origin check, the shared-secret check and the admin gate, so no user-controlled input reaches the shell. Residual impact is limited to an authenticated administrator being able to restart the dsh-web process (denial-of-service against their own deployment).
Root installer fetches Authelia/Caddy without checksum checks and an empty sharedSecret default falls back to a full-admin 'local' principal; shell execs are hardcoded and admin-gated.
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-18 · activity: Stable
• Repo created: 2026-08-27
• Stars: ★ 0 · Forks: 0
• Health: Stable — committed within 90 days
Source
GitHub: github.com/Choi-Peng/dsh-tenancy
Author README
View on GitHub ↗The author's original README from the repository — fetched live, unedited (the English page loads the default README).
