The npm and PyPI installers only ever fetched the standard (headless)
build, so the UI build (embedded graph visualization) was reachable only
through install.sh --ui or a manual archive download. Add a CBM_VARIANT=ui
opt-in to both installers that fetches the codebase-memory-mcp-ui-*
archive instead; the default is unchanged (standard headless build).
Document the per-channel opt-in in the README.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Bump the npm/PyPI/server.json version fields and the PyPI shim's
version fallback, and drop the stale 0.6.x Winget manifest folders
whose installer URLs no longer resolve.
The standard linux release binary dynamically links glibc 2.38+ and
GLIBCXX_3.4.32, so it fails to start on Debian 11, Ubuntu 20.04/22.04,
RHEL/Rocky 8/9, Amazon Linux 2, etc. — yet install.sh, the npm and PyPI
wrappers, and the binary's own self-update all fetched it by default,
contradicting the "single static binary" promise.
Point every linux install + self-update path at the fully-static
"-portable" asset (gcc -static), which has no glibc floor. macOS/Windows
are unaffected and unchanged.
- install.sh, pkg/npm/install.js, pkg/pypi _cli.py: select -portable on linux
- src/cli/cli.c: self-update download URL AND checksum archive name both use
-portable on linux (they must match or the update fails checksum verify)
- scripts/smoke-test.sh: assert linux self-update targets the -portable asset
- scripts/ci/check-glibc-compat.sh: new guard — runs the binary inside
debian:bullseye (glibc 2.31) and asserts it starts
- _smoke.yml: run the guard on the portable binary in smoke-linux-portable
Reproduced: standard binary -> "GLIBC_2.38 not found" on glibc 2.31;
portable binary runs cleanly.
Closes#246.
Three security improvements to pkg/pypi/src/codebase_memory_mcp/_cli.py:
1. tar-slip mitigation (S202) — the previous `tf.extractall(tmp)` /
`zf.extractall(tmp)` calls trusted the archive not to contain absolute
paths or `..` components. Replaced with _safe_extract_tar and
_safe_extract_zip:
- On Python 3.12+, tarfile uses the new `filter='data'` (PEP 706).
- On Python 3.8–3.11 and for zipfile (no built-in filter), each member
path is validated against the destination via os.path.abspath checks;
symlinks and hardlinks in tarballs are rejected outright.
2. URL scheme allowlist (S310) — _validate_url_scheme rejects anything
that isn't https before urlretrieve runs. The URL is constructed from
hardcoded constants today, but this is a cheap defense-in-depth check
against future config sources, redirects, or typos.
3. Justified noqa suppressions on the exec path — os.execv and
subprocess.run receive args as a list (not a shell string) and neither
uses shell=True, so there is no injection vector. Comments document
the invariant so future changes don't reintroduce a shell form.
Verification:
- `ruff check _cli.py --select S` — passes (was 5 findings, now 0)
- Syntax check passes under Python 3.14
- Behavior preserved: download/extract/exec flow is unchanged on happy path
- New failure modes: clear `sys.exit` with actionable message when a
malicious member path or non-https URL is detected
No new dependencies; all changes stdlib-only and compatible with the
project's declared `requires-python = ">=3.8"`.
Introduces pkg/ as a self-contained home for all package registry wrappers.
Each subdirectory is a ready-to-publish package that downloads the appropriate
pre-built binary from GitHub Releases — no source compilation at install time.
- pkg/npm/ — postinstall downloads binary; bin.js shim for CLI
- pkg/pypi/ — lazy download on first run via _cli.py; pipx-friendly
- pkg/homebrew/ — multi-arch formula (darwin/linux × arm64/amd64)
- pkg/scoop/ — Windows bucket manifest with autoupdate + checksum
- pkg/winget/ — v1.6.0 manifests (version + installer + locale)
- pkg/chocolatey/ — nuspec + install/uninstall scripts
- pkg/aur/ — PKGBUILD + .SRCINFO for x86_64 and aarch64
All hashes are real SHA-256 checksums from the v0.6.0 release.