d6fc6845c4106640784c6ad23e6efd8f29b20edd
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
88e93edc6c |
catalog: pin exact package versions, drop firecrawl, add last_release
CI / Tests (py3.12 / windows-latest) (pull_request) Failing after 16s
CI / Lint (ruff) (pull_request) Successful in 52s
CI / Tests (py3.10 / ubuntu-latest) (pull_request) Successful in 12s
CI / Tests (py3.12 / ubuntu-latest) (pull_request) Successful in 14s
CI / Tests (py3.13 / ubuntu-latest) (pull_request) Successful in 14s
- Pin every basic-tier entry to an exact published version (npm @x.y.z, uvx @x.y.z, docker :tag). Unpinned npx -y <pkg> means a package compromised AFTER we ship auto-upgrades into every user; a pin bounds supply-chain compromise to versions we actually reviewed. - Drop firecrawl from the seed (19 entries). npm publish rights are held solely by hello_sideguide/sideguide.dev, which has no visible relationship to firecrawl.dev, while the package is presented as official. Publisher identity we cannot tie to the vendor is exactly what this catalog must not execute on a user's machine. Retained in the research pool pending confirmation. - postgres: ship --access-mode=restricted, not unrestricted. A curated catalog must not default to handing an LLM write access to your DB. - Add last_release (ISO date, from the live registry) so the UI can show freshness; postgres-mcp and mcp-obsidian are both ~14mo stale. |
||
|
|
48904c7787 |
feat: MCP server catalog core -- signed, validated, resolvable (#10, #61)
CI / Lint (ruff) (pull_request) Successful in 12s
CI / Tests (py3.12 / windows-latest) (pull_request) Failing after 17s
CI / Tests (py3.10 / ubuntu-latest) (pull_request) Successful in 10s
CI / Tests (py3.12 / ubuntu-latest) (pull_request) Successful in 14s
CI / Tests (py3.13 / ubuntu-latest) (pull_request) Successful in 39s
Phase 1 of the MCP server catalog: pure, GUI-free core functions plus the seed data/catalog.json (20 servers). No GUI wiring in this PR -- bcc.py is untouched; a follow-up PR adds the picker dialog. - load_catalog(): strict json.loads ONLY. The lenient repair pipeline (repair_json_text / parse_pasted_json*) is never used on catalog bytes, by design and by comment, so a signature always authenticates exactly what gets parsed. - validate_catalog(): rejects the whole file (not per-entry) on: bad schema/version types, missing tier-appropriate fields (basic needs config.command+args, link-only needs docs_url and no config), a command allowlist (npx/uvx/docker/node/python/python3 only), -e/--eval/-c denial for node/python, --privileged and root/$HOME volume-mount denial for docker, non-empty env_required values (hard rejection -- secrets never ship in the catalog), secret-looking args (reuses _TOKEN_PREFIXES/_is_secret_value rather than reimplementing), non-https URL fields, and non-ASCII code points in id/command/args (homoglyph defence). - verify_catalog_signature(): Ed25519 via the cryptography package, domain-separated message (the literal prefix "bcc-catalog-v1|" + raw bytes), accepts a match against any key in CATALOG_PUBKEYS (rotation-ready), never raises. - resolve_catalog(): picks the highest version among bundled/cached/remote candidates that EACH independently pass verify + validate -- the bundled catalog gets no implicit trust, closing the hole where an unsigned payload merged to main would win on being local. Anti-rollback (never regress below the best verified candidate already in hand) and anti-freeze (reject a jump of more than 1000 versions) built in. - catalog_entry_to_paste_json() / config_has_unfilled_placeholders(): small pure helpers the future GUI dialog will use to feed a catalog pick into the existing paste-import path and to gate Save on unfilled placeholder tokens. data/catalog.json: the provided 20-server seed, with a signed_at field added at the top level (lives inside the signed payload once real signing lands in #62). Wired into bcc.spec's PyInstaller datas so it bundles into the frozen app. Security requirements from the issue, and where they landed: - Catalog bytes never touch the lenient JSON repair path -- enforced by load_catalog()'s strict json.loads and a comment warning against wiring it in later. - env_required values are a hard rejection when non-empty, not a warning. - Secret-looking args are rejected at validation time, reusing the existing secret-detection helpers instead of duplicating them. - Non-ASCII id/command/args rejected (typosquat/homoglyph defence). - URL fields restricted to https://. - Ed25519 signature verification is domain-separated and never raises. - The bundled catalog is verified at runtime exactly like remote/cached -- no implicit trust for being local. - Anti-rollback and anti-freeze bounds on resolve_catalog's version comparison. Tests: 42 new tests added to tests/test_core.py (full suite: 239 passed, 1 pre-existing unrelated skip). ruff check and ruff format --check both clean. |