Grades are a hygiene check, not a security guarantee.

MCP, MCP server

Lore

Lore, 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.

PERMISSION_SHELL_UNDECLARED NETWORK_HOST_FROM_INPUT

Why this grade

  • PERMISSION_SHELL_UNDECLARED Medium

    Runs shell commands that the manifest or description does not mention.

    benchmarks/run_benchmarks.py, line 210 shutil.rmtree(tmpdir, ignore_errors=True)

  • NETWORK_HOST_FROM_INPUT Medium

    A file takes a URL or endpoint as input and has no fixed host list.

    hooks/lore-precompact.ts, line 51 const response = await fetch(`${LORE_API_URL}/v1/snapshots`, {

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. requests shell access. No manifest field names shell or command execution.

    PERMISSION_SHELL_UNDECLARED PERMISSION_DELETE_FILES

    benchmarks/run_benchmarks.py, line 210

    shutil.rmtree(tmpdir, ignore_errors=True)

    infra/lib/lore-stack.ts, line 152

    subprocess.check_call([sys.executable, "-m", "pip", "install", "psycopg2-binary", "-t", "/tmp/deps", "-q"])

    src/lore/bootstrap.py, line 132

    result = subprocess.run(

    Show more

    src/lore/bootstrap.py, line 172

    result = subprocess.run(

    src/lore/bootstrap.py, line 217

    result = subprocess.run(

    src/lore/setup.py, line 127

    subprocess.run(

    src/lore/setup.py, line 684

    subprocess.Popen(

    src/lore/setup.py, line 1253

    result = subprocess.run(

  • Install and update

    Note

    The docs document an installer that pipes a download into a shell.

    INSTALL_DOCUMENTED_INSTALLER

    docs/setup-claude-code.md, line 179

    | "command not found: uvx" | Install uv: `curl -LsSf https://astral.sh/uv/install.sh \| sh` or use `python -m lore.mcp.server` instead. |

    docs/setup-claude-desktop.md, line 88

    | "command not found: uvx" | Install uv: `curl -LsSf https://astral.sh/uv/install.sh \| sh` or use `python -m lore.mcp.server` instead of `uvx --from lore-sdk[mcp] lore-memory`. |

    docs/setup-cline.md, line 86

    | "command not found: uvx" | Install uv: `curl -LsSf https://astral.sh/uv/install.sh \| sh` or use `python -m lore.mcp.server` instead. |

    Show more

    docs/setup-codex.md, line 135

    | "command not found: uvx" | Install uv: `curl -LsSf https://astral.sh/uv/install.sh \| sh` or use `python -m lore.mcp.server` instead. |

    docs/setup-cursor.md, line 171

    | "command not found: uvx" | Install uv: `curl -LsSf https://astral.sh/uv/install.sh \| sh` or use `python -m lore.mcp.server` instead. |

    docs/setup-openclaw.md, line 138

    | "command not found: uvx" | Install uv: `curl -LsSf https://astral.sh/uv/install.sh \| sh` or use `python -m lore.mcp.server` instead. |

    docs/setup-vscode.md, line 86

    | "command not found: uvx" | Install uv: `curl -LsSf https://astral.sh/uv/install.sh \| sh` or use `python -m lore.mcp.server` instead. |

    docs/setup-warp.md, line 68

    | "command not found: uvx" | Install uv: `curl -LsSf https://astral.sh/uv/install.sh \| sh` or use `python -m lore.mcp.server` instead. |

    docs/setup-windsurf.md, line 86

    | "command not found: uvx" | Install uv: `curl -LsSf https://astral.sh/uv/install.sh \| sh` or use `python -m lore.mcp.server` instead. |

  • Network

    Serious

    accepts a host from input, with no fixed list in this file.

    NETWORK_HOST_FROM_INPUT

    hooks/lore-precompact.ts, line 51

    const response = await fetch(`${LORE_API_URL}/v1/snapshots`, {

    infra/lib/lore-stack.ts, line 143

    http.request("PUT", event["ResponseURL"], body=body.encode(),

    scripts/backfill_graph.py, line 50

    rows = await conn.fetch("""

    Show more

    scripts/backfill_graph.py, line 63

    existing = await conn.fetch("SELECT id, name FROM entities")

    scripts/install-mac.sh, line 37

    echo ' /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"'

    scripts/install-mac.sh, line 215

    curl -sSL "https://raw.githubusercontent.com/agentkitai/lore/$LORE_GIT_REF/migrations/$mig" -o "$LORE_MIGRATIONS_DIR/$mig"

    scripts/install-mac.sh, line 336

    if curl -sf "http://localhost:$MAX_PROXY_PORT/v1/models" > /dev/null 2>&1; then

    scripts/install-mac.sh, line 460

    if curl -sf "http://localhost:$LORE_PORT/health" > /dev/null 2>&1; then

    scripts/install-mac.sh, line 490

    ORG_INIT=$(curl -sf -X POST "http://localhost:$LORE_PORT/v1/org/init" \

    src/lore/_workers.py, line 291

    with urllib.request.urlopen(req, timeout=10): # noqa: S310

    src/lore/bootstrap.py, line 388

    with urllib.request.urlopen(url, timeout=3) as resp:

    src/lore/setup.py, line 34

    if curl -sf -o /dev/null --max-time 0.5 "$url/health" 2>/dev/null; then

    src/lore/server/slo_checker.py, line 155

    urllib.request.urlopen(req, timeout=10)

    Dockerfile, line 37

    CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8765/health')" || exit 1

    Dockerfile.server, line 61

    CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8765/health')" || exit 1

  • Secrets

    Concern

    reads a credential or environment file.

    SECRETS_ENV_READ SECRETS_PATH_DOTENV

    hooks/lore-precompact.ts, line 11

    const LORE_API_KEY = process.env.LORE_API_KEY || "";

    src/lore/bootstrap.py, line 74

    """Check .env file exists; create with sensible defaults if --fix."""

    src/lore/bootstrap.py, line 76

    env_path = os.path.join(os.getcwd(), ".env")

    Show more

    src/lore/bootstrap.py, line 81

    message=f".env exists at {env_path}",

    src/lore/bootstrap.py, line 88

    message=f"Created .env with defaults at {env_path}",

    src/lore/bootstrap.py, line 93

    message=".env not found in current directory",

    src/lore/bootstrap.py, line 94

    fix_hint="Run with --fix to create a default .env, or create one manually",

    src/lore/bootstrap.py, line 422

    """Create a .env file with sensible defaults."""

    src/lore/integrate.py, line 372

    api_key = api_key or os.environ.get("LORE_API_KEY")

    src/lore/lore.py, line 242

    llm_key = llm_api_key or os.environ.get("LORE_LLM_API_KEY")

    src/lore/lore.py, line 276

    llm_key = llm_api_key or os.environ.get("LORE_LLM_API_KEY")

    src/lore/lore.py, line 330

    _llm_key = llm_api_key or os.environ.get("LORE_LLM_API_KEY")

    src/lore/setup.py, line 145

    val = os.environ.get("LORE_API_KEY") or DEFAULT_API_KEY

    src/lore/setup.py, line 1439

    api_key_val = api_key or os.environ.get("LORE_API_KEY") or _read_solo_key()

    src/lore/setup.py, line 1554

    api_key_val = api_key or os.environ.get("LORE_API_KEY") or _read_solo_key() or ""

    src/lore/cli/commands/doctor.py, line 30

    DEFAULT_ENV_PATH = Path.cwd() / ".env"

  • 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. the file cap was reached before every file was read.

    OBFUSCATION_FILE_OVER_READ_CAP OBFUSCATION_FILE_CAP_REACHED

    src/lore/persistence/sqlite.py, line 1

    """SQLite Store implementation (Phase 3 of the solo-mode design).

    stories/story-6.md, line 1

    # Story 6: Prompt Helper

  • 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 f2fd553a6163. can delete files. requests shell access. No manifest field names shell or command execution. The docs document an installer that pipes a download into a shell. accepts a host from input, with no fixed list in this file. reads a credential or environment file. a file was over the read cap and was not fully read. the file cap was reached before every file was read.

Date and source

Date checked:

View source

github.com

Commit graded: f2fd553a6163fb408c7c842d7445d3b1f5d3da09

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