48904c7787
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.
59 lines
1.4 KiB
TOML
59 lines
1.4 KiB
TOML
[project]
|
||
name = "better-claude-config"
|
||
version = "1.3.0"
|
||
description = "Cross-platform GUI for editing the mcpServers block of Claude Desktop and Claude Code configs"
|
||
readme = "README.md"
|
||
license = { file = "LICENSE" }
|
||
requires-python = ">=3.10"
|
||
dependencies = [
|
||
"PySide6>=6.6",
|
||
"cryptography>=42.0",
|
||
]
|
||
|
||
[project.optional-dependencies]
|
||
dev = [
|
||
"pytest>=8.0",
|
||
"ruff>=0.6",
|
||
"pre-commit>=3.5",
|
||
]
|
||
build = [
|
||
"pyinstaller>=6.0",
|
||
"pillow>=10.0",
|
||
]
|
||
|
||
[project.urls]
|
||
Repository = "https://git.avezzano.io/the_og/better-claude-config"
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Tooling
|
||
# ---------------------------------------------------------------------------
|
||
[tool.pytest.ini_options]
|
||
testpaths = ["tests"]
|
||
addopts = "-q"
|
||
|
||
[tool.ruff]
|
||
line-length = 100
|
||
target-version = "py310"
|
||
|
||
[tool.ruff.lint]
|
||
select = [
|
||
"E", # pycodestyle errors
|
||
"W", # pycodestyle warnings
|
||
"F", # pyflakes
|
||
"I", # isort
|
||
"UP", # pyupgrade
|
||
"B", # bugbear
|
||
"SIM", # simplify
|
||
"RUF", # ruff-specific
|
||
]
|
||
ignore = [
|
||
"E501", # line length handled pragmatically; GUI strings run long
|
||
"SIM108", # ternary rewrites hurt readability in places
|
||
"RUF001", # UI strings intentionally use typographic glyphs (−, →, ↳)
|
||
"RUF002",
|
||
"RUF003",
|
||
]
|
||
|
||
[tool.ruff.lint.per-file-ignores]
|
||
"bcc.py" = ["F405", "F403"] # Qt star-import style if ever used
|