Commit Graph

6 Commits

Author SHA1 Message Date
BCC Fix Agent 82483e693d fix(catalog-console): close the vacuous review gate; split catalog/release signing keys
CI / Lint (ruff) (pull_request) Successful in 9s
CI / Tests (py3.10 / ubuntu-latest) (pull_request) Successful in 11s
CI / Tests (py3.12 / windows-latest) (pull_request) Successful in 34s
CI / Tests (py3.12 / ubuntu-latest) (pull_request) Successful in 9s
CI / Tests (py3.13 / ubuntu-latest) (pull_request) Successful in 10s
CI / Catalog signature (pull_request) Successful in 6s
Fixes #68 findings 1 and 5.

Finding 1 -- the review gate signed without reviewing anything:
- ReviewWindow._on_sign hardcoded "main" as the TOCTOU comparison ref, so
  any PR review (where _on_load pins the PR head's blob SHA) could never
  sign; the only working path was main-vs-itself, whose empty diff made
  can_sign() vacuously True (set() <= set()). Commit b08cf21 signed 19
  entries through exactly that path with zero of them reviewed.
- can_sign() now refuses an empty changeset outright, and itself checks
  has_blocking_risk() across every changed entry rather than trusting the
  GUI to have disabled a checkbox.
- ReviewSession now carries loaded_ref (the exact ref reviewed); a new pure
  sign_precondition(session, resolve_blob_sha) resolves the TOCTOU SHA from
  that ref, never a hardcoded "main". _on_load's retry path re-diffs
  instead of re-pinning the same stale SHA, so a blob-mismatch refusal
  can't loop forever.
- source="main" now diffs against the last catalog a maintainer actually
  SIGNED (walking catalog.json's git history until a version verifies
  against the current .sig), not against itself.
- Replaced the theatre-only test_no_acknowledge_all_function_exists (only
  asserted no function was *named* acknowledge_all) with a test that also
  exercises the real gate. Added can_sign/sign_precondition coverage for
  the empty-diff, blocking-risk, and ref-resolution seams -- each verified
  to fail when its guard is removed.

Finding 5 -- the catalog key and release key were the same CI-resident key:
- scripts/sign_checksums.py gets its own RELEASE_PUBKEYS (separate from
  bcc_core.CATALOG_PUBKEYS) and a verify_checksums_against_any() helper.
- release.yml's signing-smoke-test now verifies RELEASE_SIGNING_KEY against
  RELEASE_PUBKEYS only -- it no longer imports bcc_core/CATALOG_PUBKEYS at
  all, so this workflow can never compare a CI secret against the
  catalog's root of trust.
- catalog_console.py: keygen/show-seed-b64 gain --release, with separate
  keychain/file storage per key kind. show-seed-b64 refuses to run without
  --release, so the catalog seed can't be exported to a CI secret by habit.
- README documents both keys' trust properties and the asymmetry: a CI
  compromise burns the release key, never the catalog key.

The maintainer must rotate the catalog key (it was CI-resident, so treat it
as burned for catalog use) and generate a fresh release key -- see the PR
description for the exact steps. No key is generated or committed here.
2026-07-12 21:25:34 -04:00
BCC Agent cd38fd0c78 Sign release checksums with Ed25519 (#63)
CI / Lint (ruff) (pull_request) Successful in 6s
CI / Tests (py3.10 / ubuntu-latest) (pull_request) Successful in 12s
CI / Tests (py3.12 / windows-latest) (pull_request) Successful in 23s
CI / Tests (py3.12 / ubuntu-latest) (pull_request) Successful in 11s
CI / Tests (py3.13 / ubuntu-latest) (pull_request) Successful in 13s
Publish SHA256SUMS for every release archive and sign it with a
detached Ed25519 signature (SHA256SUMS.sig), since paid code signing
(macOS Developer ID, Windows Authenticode) and Sigstore keyless (needs
a Fulcio-trusted OIDC issuer; self-hosted Gitea isn't one) are both
out of budget/scope.

- scripts/sign_checksums.py: dependency-light (cryptography only)
  helper to hash a directory of files into a sha256sum(1)-compatible
  SHA256SUMS manifest, sign it (domain-separated: b"bcc-release-v1|"
  + raw manifest bytes), and verify a signature. CLI has generate/
  sign/verify subcommands; verify doubles as the check path.
- tests/test_checksums.py: 15 unit + CLI-subprocess tests covering
  hashing, manifest formatting, sign/verify roundtrip, tamper
  detection, wrong-key rejection, domain-separation, and the
  no-key-provided failure path (must error, never write an empty/
  bogus .sig).
- .github/workflows/release.yml: Publish Release job now checks out
  the repo, flattens build artifacts, generates SHA256SUMS, and signs
  it from the RELEASE_SIGNING_KEY secret (base64 raw Ed25519 seed) if
  present. If the secret is absent, the release still publishes with
  a loud ::warning:: and no .sig — it never fails the release or
  publishes a bogus signature.
- README.md: new 'Verifying your download' section with the (still
  placeholder) public key, sha256sum -c / Get-FileHash commands, and
  an explicit statement that this does not remove Gatekeeper/
  SmartScreen warnings.
- requirements-dev.txt / ci.yml: add cryptography as a dev/test
  dependency for the new script and its tests.

Touches no files from bcc_core.py / tests/test_core.py /
pyproject.toml / bcc.spec to avoid colliding with concurrent work on
those files.
2026-07-12 17:30:09 -04:00
AJ b16c0fd526 fix: point Claude Code profile at ~/.claude.json, not settings.json
CI / Lint (ruff) (push) Successful in 7s
CI / Tests (py3.10) (push) Successful in 7s
CI / Tests (py3.12) (push) Successful in 7s
Claude Code stores user-scope MCP servers in ~/.claude.json (what
'claude mcp add' writes); ~/.claude/settings.json is for permissions
and hooks and rejects an mcpServers key with a schema error, so BCC
was reading (and writing) servers where Claude Code never looks.

If servers are found parked in settings.json, that file is still
listed as 'Claude Code (legacy settings.json)' so they can be copied
into the real config via Copy to. Docs updated; verified against
docs.claude.com/en/docs/claude-code/settings.
2026-07-02 00:27:01 -04:00
AJ 6205c85b61 feat: lenient JSON repair for pasted MCP snippets
CI / Lint (ruff) (push) Successful in 10s
CI / Tests (py3.10) (push) Successful in 23s
CI / Tests (py3.12) (push) Successful in 10s
Pasted config snippets no longer have to be valid JSON. repair_json_text()
auto-fixes markdown fences, surrounding prose, // /* */ # comments,
trailing and missing commas, smart quotes, single quotes, unquoted keys,
Python/JS literals, and unclosed braces. parse_pasted_json_verbose()
reports every repair applied; the paste dialog now parses as you type
and previews exactly what will be added and what was fixed.

Also includes ruff lint fixes and formatting across bcc.py/bcc_core.py.
2026-07-01 23:43:39 -04:00
AJ Avezzano dd7557ea62 Add cross-platform packaging, icons, and Claude Code config discovery
Build & Release / Build (Linux) (push) Failing after 2m56s
Build & Release / Build (macOS) (push) Successful in 4m24s
Build & Release / Build (Windows) (push) Has been cancelled
Build & Release / Publish Release (push) Has been cancelled
- Auto-discovers Claude Code (~/.claude/settings.json) alongside Claude Desktop
- Generated icons/app.icns (macOS) and icons/app.ico (Windows) from rounded PNGs
- bcc.spec: PyInstaller spec for all platforms (.app on macOS, onefile on Windows/Linux)
- .github/workflows/release.yml: builds and publishes GitHub Release on version tags
- scripts/build_icons.py: regenerates icon files from source PNGs
- requirements-dev.txt: adds pyinstaller and pillow for building
- CLAUDE.md: initial repo guidance for Claude Code

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 15:01:30 -04:00
AJ Avezzano 780d4c3a9c Initial commit: Better Claude Config 2026-06-29 14:12:50 -04:00