MCP, MCP server
Meridian Browser
Meridian Browser, an MCP server listed in the PlugVet directory.
Compatibility: Not stated.
Grade D
Two or more Medium findings, or a High. Only try it in a sandbox.
Not a guarantee. A hygiene check of the source at the commit shown, not a security audit.
INSTALL_REMOTE_SCRIPT SECRETS_PATH_CREDENTIAL
Why this grade
- INSTALL_REMOTE_SCRIPT High
Fetches a script from the network and runs it (a pipe to a shell, or an install hook with a URL).
src/updates.js, line 85
const child = spawn('/bin/bash', ['-c', `curl -fsSL ${INSTALL_SH} | bash`], { - SECRETS_PATH_CREDENTIAL Medium
Mentions a credential store path (~/.ssh, id_rsa, .npmrc, .aws/credentials, credentials.json, .netrc).
src/dev/guard.js, line 9
// id_rsa, credentials…). Only then it asks Meridian over a private local socket: a unix socket in
Any High, or two or more Medium findings, give D. One Medium gives C. Low findings or a security Note give B. Each code names a finding behind the letter.
If you think this grade is wrong, email [email protected] with the entry link and your evidence. We aim to acknowledge within two working days and decide within seven. If we confirm an error, we aim to correct it within 24 hours of confirming it. These are aims, not guarantees.
Grades are a hygiene check of the source at the commit shown, on the date checked, not a security audit or a guarantee. The free directory grades are automated. Before release, a PlugVet engineer reviews every paid report and any grade we hold back, including all F grades, against the source code and the evidence we quote.
What was checked
Plain notes from the hygiene checks.
Permissions
Concern
can delete files.
PERMISSION_DELETE_FILES
scripts/perf-bench.js, line 18
fs.rmSync(dir, { recursive: true, force: true });
src/agent/hub.js, line 666
try { fs.unlinkSync(this.endpointPath); } catch {}
src/agent/hub.js, line 691
try { fs.unlinkSync(this.endpointPath); } catch {}
Show more
src/agent/hub.js, line 692
if (process.platform !== 'win32' && this.socketPath) { try { fs.unlinkSync(this.socketPath); } catch {} }
src/agent/hub.js, line 693
if (this.tmpDir) { try { fs.rmSync(this.tmpDir, { recursive: true, force: true }); } catch {} this.tmpDir = null; }
src/agents/channel.js, line 184
try { fs.rmSync(st.dir, { recursive: true, force: true }); } catch {}
src/agents/channel.js, line 191
if (this.dir) { try { fs.rmSync(this.dir, { recursive: true, force: true }); } catch {} this.dir = null; }
src/ai/assistant.js, line 55
if (!key) { try { fs.unlinkSync(this.keyFile); } catch {} return; }
Install and update
Serious
install fetches a script from the network. README documents an installer that pipes a download into a shell. The docs document an installer that pipes a download into a shell.
INSTALL_REMOTE_SCRIPT INSTALL_DOCUMENTED_INSTALLER
README.md, line 55
curl -fsSL https://undermove.github.io/meridian-browser/install.sh | bash
docs/install.sh, line 4
# curl -fsSL https://undermove.github.io/meridian-browser/install.sh | bash
src/updates.js, line 85
const child = spawn('/bin/bash', ['-c', `curl -fsSL ${INSTALL_SH} | bash`], {
Show more
src/ai/local-agents.js, line 83
install: { mac: 'curl https://cursor.com/install -fsS | bash', linux: 'curl https://cursor.com/install -fsS | bash', win: null },
src/ai/local-agents.js, line 116
all opencode', needs: 'brew' }, { cmd: 'curl -fsSL https://opencode.ai/install | bash' }], linux: 'curl -fsSL https://opencod
src/ai/local-agents.js, line 167
install: { mac: 'curl -fsSL https://ampcode.com/install.sh | bash', linux: 'curl -fsSL https://ampcode.co
src/ai/local-agents.js, line 205
install: { mac: 'curl -fsSL https://app.factory.ai/cli | sh', linux: 'curl -fsSL https://app.factory.ai/cli | sh', win: [{ cmd: 'npm install -g droid', needs: 'npm' }] },
src/ai/local-agents.js, line 228
ll llama3.2', needs: 'brew' }], linux: 'curl -fsSL https://ollama.com/install.sh | sh && ollama pull llama3.2', win: null },
src/dev/agents.js, line 27
opencode: { label: 'OpenCode', bin: 'opencode', install: 'curl -fsSL https://opencode.ai/install | bash', mcp: true, extra: true },
src/dev/agents.js, line 28
cursor: { label: 'Cursor', bin: 'cursor-agent', install: 'curl https://cursor.com/install -fsS | bash', mcp: false, extra: true },
src/dev/agents.js, line 32
droid: { label: 'Droid', bin: 'droid', install: 'curl -fsSL https://app.factory.ai/cli | sh', mcp: false, extra: true },
package.json, line 15
postinstall
docs/index.html, line 194
<div class="cmd"><code id="cmd-unix">curl -fsSL https://undermove.github.io/meridian-browser/install.sh | bash</code><button class="copy" data-copy="cmd-unix"><svg viewBox="0 0 24
docs/index.html, line 238
<div class="cmd"><code id="cmd-linux">curl -fsSL https://undermove.github.io/meridian-browser/install.sh | bash</code><button class="copy" data-copy="cmd-linux"><svg viewBox="0 0 2
Network
Concern
can reach a host that is not written as a fixed address. can reach user. The description does not name that host. can reach api.github.com. The description does not name that host.
NETWORK_NO_FIXED_HOST NETWORK_UNNAMED_HOST
docs/install.sh, line 19
command -v curl >/dev/null || fail "curl is needed."
docs/install.sh, line 38
RELEASE="$(curl -fsSL -H 'Accept: application/vnd.github+json' "$API")" || fail "Couldn't reach GitHub. Check your internet connection and try again."
docs/install.sh, line 49
curl -fL --progress-bar -o "$TMP/$FILE" "$URL" || fail "Download failed."
Show more
docs/install.sh, line 52
curl -fsSL -o "$TMP/SHA256SUMS.txt" "$SUMS_URL" || fail "Couldn't download the checksums."
docs/install.sh, line 72
# Files fetched with curl aren't quarantined, but clear the flag in case something added it.
docs/install.sh, line 87
curl -fsSL -o "$HOME/.local/share/icons/hicolor/512x512/apps/meridian.png" \
mcp/meridian-mcp.js, line 52
const req = http.request({
src/main.js, line 816
const res = await session.fromPartition(partition).fetch(url, { signal: AbortSignal.timeout(8000) });
src/ai/local-agents.js, line 186
ck-goose-cli', needs: 'brew' }, { cmd: 'curl -fsSL https://github.com/aaif-goose/goos
Secrets
Concern
reads a credential or environment file.
SECRETS_ENV_READ SECRETS_PATH_CREDENTIAL SECRETS_PATH_DOTENV
mcp/meridian-mcp.js, line 31
const SESSION_HASH = process.env.MERIDIAN_MISSION_TOKEN ? require('crypto').createHash('sha256').update(process.env.MERIDIAN_MISSION_TOKEN).digest('hex').slice(0, 32) : null;
src/main.js, line 1757
const forcedLocked = DEV && process.env.MERIDIAN_E2E_KEYCHAIN === 'locked';
src/dev/guard.js, line 9
// id_rsa, credentials…). Only then it asks Meridian over a private local socket: a unix socket in
Show more
src/agent/hub.js, line 554
.startsWith(d + path.sep)) || /(^|[\\/])\.env(\.|$)|\.(pem|key|p12|pfx|keychain-db)$/
src/agents/hooks/mission-hook.js, line 111
const token = process.env.MERIDIAN_MISSION_TOKEN;
src/ai/assistant.js, line 51
return fs.existsSync(this.keyFile) || !!process.env.ANTHROPIC_API_KEY;
src/ai/assistant.js, line 89
if (!apiKey && !process.env.ANTHROPIC_API_KEY) throw new Error('Add your Anthropic API key in Settings → AI.');
src/ai/local-agents.js, line 399
const child = spawnSafe(bin, args, { env: { ...env, ...(spec.env || {}) }, cwd });
src/dev/agents.js, line 12
// and for Claude Code a line in its system prompt), so it uses them instead of hunting for the blank .env values.
src/dev/agents.js, line 39
+ 'Don\'t ask the user to paste them, don\'t write their values into files or logs, and when a script loads a .env file, don\'t let it override variables that are already set.';
src/dev/agents.js, line 123
)}). If ${agent.label} looks for them in .env, tell it to read the environment variab
src/dev/env.js, line 119
return { ...env, ...extra };
src/dev/guard.js, line 8
// - The hook decides locally whether a tool call touches a secret-looking file (.env, *.pem,
src/dev/index.js, line 157
this.vault = new Vault(path.join(app.getPath('userData'), 'vault.json'), { testKeychain: !app.isPackaged && process.env.MERIDIAN_TEST_KEYCHAIN === '1' });
src/dev/hooks/secret-guard-hook.js, line 82
const token = process.env.MERIDIAN_GUARD_TOKEN;
Agent instructions
Pass
No instruction to ignore earlier instructions or to hide a step.
Obfuscation
Note
a file was over the read cap and was not fully read.
OBFUSCATION_FILE_OVER_READ_CAP
THIRD_PARTY_NOTICES.md, line 1
# Third-party notices
Licence
Pass
A licence file is present (LICENSE).
Maintainer activity
Pass
The latest commit is within the last 365 days.
Note
Hygiene check of commit f514da76c5db. can delete files. install fetches a script from the network. README documents an installer that pipes a download into a shell. The docs document an installer that pipes a download into a shell. can reach a host that is not written as a fixed address. can reach user. The description does not name that host. can reach api.github.com. The description does not name that host. reads a credential or environment file. a file was over the read cap and was not fully read.
Date and source
Date checked:
github.com
Commit graded: f514da76c5db178f37ed7c0f18e2ae5e19c86fb6
This link goes to the original host. PlugVet does not host or check what you download from there, and installing it is your own decision and risk.