4743c4a99510b0850489b860b98fca88a659ad2d
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
38f14deeff |
fix(core): validate config.env, enforce version pinning, fix CI trust anchor and resolve_catalog guards (#68)
CI / Lint (ruff) (pull_request) Successful in 7s
CI / Tests (py3.10 / ubuntu-latest) (pull_request) Successful in 11s
CI / Tests (py3.12 / ubuntu-latest) (pull_request) Successful in 11s
CI / Tests (py3.13 / ubuntu-latest) (pull_request) Successful in 10s
CI / Tests (py3.12 / windows-latest) (pull_request) Successful in 23s
CI / Catalog signature (pull_request) Successful in 7s
Fixes findings 2, 3, 4, 6, 7 from the issue #68 adversarial review. - Finding 2: config.env was type-checked only. Add CATALOG_DENIED_ENV_KEYS (case-insensitive) for interpreter/loader-override keys (NODE_OPTIONS, PYTHONPATH, LD_PRELOAD, ...), apply the ASCII check and the existing secret-value check to env keys/values, and require env values to be empty or a single <PLACEHOLDER> token. - Finding 3: version pinning was only checked by catalog_review.py (which never runs on the signing path per finding 1). Move enforcement into _validate_catalog_config: npm/uvx specs must carry @version or ==version (scoped names handled), docker images must have an explicit non-latest tag. Only the first plausible package-spec token is checked, so flags, <PLACEHOLDER>s, and docker subcommands/flags don't trip it. All 19 real catalog entries still validate clean. - Finding 4: the CI catalog-signature gate imported bcc_core from the PR branch and trusted whatever CATALOG_PUBKEYS said there, so a PR changing both catalog.json and CATALOG_PUBKEYS (with a matching signature) went green. ci.yml now hardcodes the expected base64 pubkey and asserts bcc_core.CATALOG_PUBKEYS matches it before verifying the signature. NOTE: the maintainer is planning to rotate this key -- update EXPECTED_CATALOG_PUBKEY_B64 in ci.yml as its own reviewed change when that happens, never bundled with a catalog content change. - Finding 6: resolve_catalog's anti-rollback/anti-freeze guards sat behind `if best_version >= 0`, so the first verified candidate was accepted unconditionally and the anti-freeze anchor drifted with each accepted candidate instead of staying fixed. The cap is now measured against the bundled catalog's version specifically (the trust anchor baked into the binary), regardless of evaluation order; bundled wins version ties; and a new pure `floor` parameter lets a future caller pass a persisted accepted-version floor. - Finding 7: catalog id is now constrained to ^[a-z0-9][a-z0-9._-]{0,63}$. Tests: fixed _minimal_catalog to use a pinned package (was enshrining finding 3), rewrote the env-passthrough test to prove the validation boundary instead of asserting env passes through unchecked, and reordered test_resolve_catalog_rejects_absurd_version_jump so it actually exercises the first-candidate path. Added positive/negative tests for every new rule. Manually verified each new check by commenting it out and confirming the guarding test goes red, then restoring it. |
||
|
|
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
|
||
|
|
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. |