Commit Graph

36 Commits

Author SHA1 Message Date
Martin Vogel 8eabe191d2 fix(install,daemon): unbreak npx clients, group-writable homes, and legacy updaters
Five field reports in the 24 hours after v0.10.0 all pointed at the same thing:
gates that were right in principle refused real, ordinary setups, and then
failed to say why. Per the consolidated strictness decision, each gate keeps the
protection that matters and drops the part that was refusing legitimate users —
and every refusal now names what it refused and how to proceed.

**Daemon image gate: npx and every ephemeral install path (#1539, #1383).**
The admission check treated "the peer's image hashes differently" and "the
peer's image cannot be examined at all" as one failure. The second is what
`npx codebase-memory-mcp` always produces (ephemeral cache path,
unfingerprintable), so every npx-invoked client was rejected — and, because the
client never reported it, agents saw a transport that closed mid-handshake with
zero bytes on stdout. Reported by @wassolles with the admission path already
read and the fix space mapped.

An unverifiable image is now admitted: the rendezvous HELLO immediately above it
has already proven semantic version, build fingerprint, and protocol/store/
feature ABI, and the image check was trading that real proof for an unavailable
one. It logs daemon.client_image_unverifiable_admitted so the weaker check is
never invisible. A fingerprint MISMATCH — the tamper case the gate exists for —
still rejects hard. Separate test seams keep the two modes testable apart.

**Client bootstrap failures are no longer silent (#1539).**
An MCP client that cannot reach the daemon now emits a JSON-RPC error on stdout
naming the reason, plus the same text on stderr. Previously the reason sat in
bootstrap_result.message and the process exited having written nothing at all.

**POSIX activation: group-writable ancestors (#1535, discussion #1526).**
activation_directory_secure required no group or other write bit on the install
directory AND every ancestor. WSL2 ships ~ and ~/.local at 0775, as do several
distro skeletons and any site using a shared primary group, so install.sh failed
for a large fraction of Linux users — reporting a policy refusal as "activation
transaction I/O failed", which sent reporters after disk errors and filesystem
types. Root-caused by @AmirF194 in a clean ubuntu container; @shochdoerfer and
@iandol confirmed independently.

World-writable ancestors are still refused (any local user could swap a path
component mid-transaction). Group-writable ancestors are now warned about and
admitted. The LEAF directory stays strictly owner-private — that is where the
binary is published, and group write there would let another account replace the
executable between validation and exec. Refusals now name the directory, its
mode, and which rule refused.

**The obsolete ui/standard chooser (#1538, from discussion #1526).**
v0.10.0 consolidated to one archive per platform with the UI always embedded,
but `update` still offered a variant choice: "ui" could only 404, and "standard"
quietly WAS the UI build. Reported by @iandol upgrading 0.9.0 -> 0.10.0. The
chooser, its --standard/--ui flags, and the ui- URL plumbing are removed, along
with the CBM_VARIANT=ui remnant in the npm installer.

Already-released 0.9.x binaries cannot be fixed retroactively, so the release
workflow now publishes byte-identical ui-*-named alias assets — their updaters
work again with no user action. The aliases are uploaded AFTER the VirusTotal
gate: they are the same bytes as archives it already cleared, and uploading them
earlier would duplicate every object in the scan set and the provenance manifest.

**macOS install noise and attribution (#1537).**
install.sh silenced the "No such xattr: com.apple.quarantine" line, which is
what happens when a curl-downloaded archive carries no quarantine attribute —
harmless, and it became the title of a bug report about an unrelated failure.
The session-stop refusal now points at `daemon status` to list the client
processes actually holding the daemon, instead of asserting sessions exist and
leaving the reader to guess. Reported by @listepo.

**Riders.** hatchling is pinned in pkg/pypi (an unpinned backend resolved fresh
inside `python -m build` is what emitted Metadata-Version 2.5 and broke the
v0.10.1 publish); SECURITY.md's supported-versions table moves to 0.10.x.

Tests: separate seams for unverifiable vs mismatched peer images with a test per
outcome; activation refusal must name directory + mode + rule; a group-writable
ancestor must stage successfully. The update tests drop the flag that no longer
exists. Verified against each reporter's environment shape.

**Open security alerts (all three, OSSF Scorecard).**
- HIGH, binary artifact: an 8.8 MB compiled Go ELF wrapper had been committed at
  pkg/go/codebase-memory-mcp by accident. Removed, and both it and its .exe
  sibling are gitignored so `go build` in that directory cannot repeat it.
- HIGH, GHSA-2v37-7h3g-55p8: nanoid < 3.3.17 loops forever when a custom
  generator is called with size 0. It reaches us transitively (postcss -> vite),
  so it is pinned through the existing graph-ui overrides block rather than
  promoted to a direct dependency; the lockfile resolves 3.3.18.
- MEDIUM, unpinned pip command: the publish step installed build/twine by
  version only, leaving the whole transitive graph resolved at run time.
  pkg/pypi/requirements-publish.txt now hash-pins the complete toolchain (316
  hashes), generated on a linux/amd64 python:3.12 image so the wheels match what
  ubuntu-latest resolves, and the step runs pip with --require-hashes. Verified
  by installing from it in that same image.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-11 13:37:32 +02:00
Martin Vogel 3f54d7c7ea test(go): restore the publication concurrency tests for the single-file runtime
Same restoration as the pypi wrapper, same reasoning: serialization of
concurrent publishers, killed-publisher reconciliation via locked readiness,
the crash-phase matrix, and multiply-linked-leaf rejection are properties of
the LOCK design, not of how many files publish.

Adaptations for one file: the mid-publication pause hooks the binary rename
itself (the runtime lock is held until publish returns, so the serialization
property is exercised exactly as before); the "partial sidecar publish" crash
phase is gone with the sidecars; backup-member expectations drop to the single
member, including cleanup-interrupted leaving 0 rather than 1; and the
hardlink-rejection guard now targets the binary leaf.

go vet + gofmt clean; full Go suite ok including the restored
TestConcurrentRuntimePublishersAreSerialized,
TestKilledRuntimePublisherIsReconciledByLockedReadiness (5 crash phases),
TestRuntimePublicationCrashHelper and
TestRuntimeReadinessRejectsMultiplyLinkedLeaves.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-09 20:26:42 +02:00
Martin Vogel dce5518c56 test(pypi): restore the publication concurrency tests for the single-file runtime
Deleting these with the multi-file runtime set threw away properties that do not
depend on how many files publish: two wrappers racing to publish the SAME cache
still must serialize, a killed publisher still must be reconciled by locked
readiness rather than trusted, a failed publication still must restore the prior
binary, and a complete foreign winner still must never be deleted.

Restored from main and adapted: the staging fixtures publish one binary instead
of binary+integrations+pack, and the assertions that read sidecar bytes are gone
with the sidecars. Each surviving test keeps its lock/race logic intact
(7/10/5/3 assertions respectively, all still reading published binary bytes).

Revert-checked against production, not just green: disabling
_runtime_backup_target_name makes test_publication_failure_restores... fail and
restoring it passes. pypi suite: 28 tests, OK.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-09 20:21:39 +02:00
Martin Vogel 6415f94fef fix(packages): put every wrapper and its tests on the same base
Restoring files from two different pre-revert commits left the package wrappers
internally inconsistent, and one of them did not compile. Found by actually
building and running them rather than by grepping for removed symbols.

pkg/npm — bin.js was never restored, so main's version was destructuring
runtimeSetReady/acquireRuntimeLock out of an install.js restored from an older
base that does not export them. `npx codebase-memory-mcp` would have died at
require time on Windows. install.js, bin.js and the launcher test now all sit on
the pre-externalization base, where installWindowsBinaryAtomically is the right
primitive for publishing exactly one file. The runtime-set locking that goes
away with it existed only to publish binary+sidecars atomically.

pkg/go — main.go kept the Windows lock work while main_test.go had been reverted
past it, so the package did not build at all:
  main.go:805: no new variables on left side of :=      (my edit dropped uiPack)
  main_test.go: undefined: windowsPayloadName           (reverted past its removal)
The test is back on main's base, keeping every lock-race test from #1495/#1496
(TestRuntimeSetLock*, TestExpiredLease*, TestStalledRuntimeLockCreator*,
TestRuntimeSetLockSerializesProcesses). Dropped 19 whose premise -- a multi-file
runtime set with content-addressed sidecars -- no longer exists, and reduced the
writeTestRuntimeSet/assertRuntimeTag helpers to the single file that now ships.

pkg/pypi — same base repair, keeping the lock and orphan-reconciliation tests.
The three archive-SAFETY tests (hardlink members, symlink metadata, unexpected
root member) were ADAPTED rather than dropped: they only named a removed file in
their fixtures, and they guard traversal properties that still matter.
test_release_archives_require_the_integrations_sidecar is inverted rather than
deleted -- asserting the sidecar is ABSENT is the stronger guard, since a
returning sidecar is exactly what broke `pip install` on Windows.

Verified: go vet + go build + go test ok; pypi 24 tests OK; npm 10/10;
Windows single-binary bundle contract still passes.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-09 17:29:56 +02:00
Martin Vogel a4336dc40a feat(release): ship one archive set and tolerate a single Microsoft !ml verdict
Completes the collapse to a single shipped composition and replaces the
zero-tolerance VirusTotal gate with a narrow, disclosed policy.

Packaging and installers
  - package-release.sh loses --variant; archives are codebase-memory-mcp-<os>-<arch>
    with exactly four members. install.sh/install.ps1 lose --ui/--standard.
  - The extractor drops CBMUIPK pack parsing and --archive-scope; its scan-set and
    association manifests (which the gate depends on) are unchanged otherwise.
  - npm/PyPI/Go wrappers: the runtime "set" is one file again. The Windows lock
    and race fixes from #1495/#1496 are kept; only multi-file set membership goes.
    This also fixes `pip install` on Windows, which rejected the fifth archive
    member against a hardcoded four-name allowlist.
  - The wrappers' post-download probe moves from --verify-runtime-assets (removed)
    to --version, which proves the same thing: the binary executes.

VirusTotal gate
  - Exactly ONE detection is tolerated, and only when the engine is Microsoft AND
    the label ends in `!ml`. Two or more engines, any non-`!ml` label, any other
    vendor, any suspicious verdict and every infrastructure error still block.
  - A tolerated object prints TOLERATED:, never OK:, and its counts are recorded
    in vt-results.tsv exactly as a blocked one would be.
  - append-vt-notes.sh mirrors the policy. It previously hard-failed on any
    malicious count, so loosening only the gate would have passed the scan and
    then died at note publication. The notes now DISCLOSE a tolerated detection
    and link to SECURITY.md rather than claiming "0 malicious" for everything.

Rationale for the tolerance is in the gate itself: the verdict is not a property
of our bytes. It inverts across architectures and link modes, moves between
sibling artifacts of one build, and lands in different variant buckets for the
same source. The same `!ml` family hits llama.cpp, GitHub's own `gh`, Microsoft's
own Go toolchain and Anthropic's Claude installer.

The zero-tolerance contract becomes test_vt_gate_policy_contract.sh, asserting
the full matrix: 1x Microsoft !ml passes and reports TOLERATED; a Microsoft
signature label, a non-Microsoft engine, two engines, a suspicious verdict and
every malformed-response case still block. Its tripwire is narrowed to the
reverted endpoint-verification mechanism rather than the words "false positive",
so it no longer fires on a deliberate in-gate policy branch.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-09 16:16:08 +02:00
Martin Vogel d58afe562d revert(release): re-embed runtime assets into the single shipped binary
Externalizing the integration templates (#1492/#1493) and the UI bundle
(#1501/#1503) was done to reduce the Microsoft `Wacatac.B!ml` surface. It did
not work: across dry runs the flagged artifact count stayed at ~3 and the
detections merely moved between artifacts.

Dissection of run 31286803592 shows there is no structural cause to fix. The
verdicts split across every axis at once — linux-amd64 (dynamic) flagged while
linux-amd64-portable (static) is clean, but linux-arm64 (dynamic) clean while
linux-arm64-portable (static) is flagged. The two macOS binaries have identical
segment structure and split clean/flagged. Siblings from one build landed in
different variant buckets (.B vs .C). Entropy is low everywhere
(code_vectors.bin 4.166, grammar tables 3.464 bits/byte, against 7.5-8.0 for
packed payloads), so the packed-payload hypothesis is excluded too.

So the complexity bought nothing, and installation goes back to being
self-contained: one binary that carries its own UI and agent integration
templates, with no adjacent data file that has to resolve before `install`
works. Only the UI-capable composition ships from now on, under the historical
unsuffixed archive name.

Removed: src/ui/asset_pack.{c,h}, asset_pack_stub.c, asset_manifest_stub.c,
scripts/pack-ui-assets.mjs, src/cli/integration_assets.{c,h},
assets/cbm-integrations.json, scripts/gen-integrations-hash.sh, the
--verify-runtime-assets probe (nothing adjacent left to verify), and the
composition gates A6/A7 whose property is now deliberately inverted.

Restored: scripts/embed-frontend.sh, src/ui/embedded_{assets.h,stub.c}, the
compiled-in hook/adapter template bodies, and the embed/EMBED_OBJS build path.

Kept from the reverted commits, re-applied by hand where a wholesale file
restore would have dropped them:
  - cbm_module_path_utf8() in both self-path sites. GetModuleFileNameA renders
    through the ANSI code page and mangles non-ASCII install paths.
  - the /__cbm/ui-readiness HMAC proof, secure_random and cbm_hmac_sha256, so
    `daemon start --open` still waits for a genuine CBM listener.
  - X-Content-Type-Options: nosniff on served assets.
  - the MinGW noexecstack gate, -lbcrypt, and the cppcheck/zip CI fixes.

Archives are now codebase-memory-mcp-<os>-<arch>[-portable] with exactly four
members (binary, LICENSE, installer, THIRD_PARTY_NOTICES.md). That restores the
names every static package manifest already points at — aur, chocolatey,
homebrew, scoop, winget and glama were all broken by the -ui- rename.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-09 13:06:42 +02:00
Martin Vogel 079c6dcc7a fix(packages): make candidate probe test path portable
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-08 22:01:49 +02:00
Martin Vogel a1a5b7fdb4 fix(packages): eliminate wrapper lock sharing races
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-08 21:45:33 +02:00
Martin Vogel 7a62a085cf fix(packages): make wrapper locks Windows-safe
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-08 20:53:14 +02:00
Martin Vogel 8018561cfe fix(release): externalize runtime assets and harden VT verification
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-08 17:35:05 +02:00
Martin Vogel a54ea95719 fix(windows): ship one binary — remove the launcher stub flagged as a dropper
DCO / dco (push) Has been cancelled
Windows shipped a PAIR: a small permanent launcher (codebase-memory-mcp.exe)
plus the real product binary (codebase-memory-mcp.payload.exe). The launcher
existed for exactly one reason — a running .exe cannot replace its own image
on Windows, so an in-process self-update needs a second resident binary to do
the swap.

That stub is statically indistinguishable from a dropper: a small, unsigned,
zero-prevalence PE whose whole job is verify-and-execute another binary.
Defender's ML scored it Trojan:Win32/Wacatac.B!ml and blocked the v0.9.1-rc.1
release at the VirusTotal gate. It is not fixable in our code on x64 —
bcrypt-free, stripped, VERSIONINFO'd, minimal-resource and even
resource-FREE builds on CI's own MSYS2 CLANG64 toolchain were all flagged,
while the product binary scans clean on every platform.

So remove the stub and move self-update OUT of the process into install.ps1,
which runs while cbm is NOT running: Windows' image lock only blocks a
process from replacing ITSELF.  now prints the exact PowerShell
command (with the Unblock-File hint for Mark-of-the-Web); install.ps1 is
idempotent, so re-running it IS the update — it stops the daemon, renames the
running binary aside (the one mutation Windows permits on a running image),
publishes the new one, and sweeps retired copies.

Windows now matches Linux and macOS: ONE binary per platform.

  * packaging, install.ps1, npm and PyPI wrappers all carry a single binary
  * the launcher/payload ABI contract and ~2500 lines of stub state machinery
    are deleted
  * every daemon start, CLI call and hook fire loses a process spawn, a named
    pipe handshake and an stdio relay
  * test_windows_bundle_contract.sh is rewritten as an INVERTED contract: it
    now asserts no shipped surface can reintroduce a launcher/payload pair,
    and that install.ps1 retires the running binary before publishing

Verified: VirusTotal 0/67 on the packaged binary and 0/58 on install.ps1 (no
certificate involved); macOS and Linux full suites green; Windows guards all
green including the new update-handoff contract; npm 10/10; PyPI 3/3.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-28 17:03:01 +02:00
Martin Vogel 83c137d2a5 feat: complete shared daemon lifecycle
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-18 01:26:08 +02:00
Martin Vogel 0e00ef5702 feat: coordinate concurrent CBM sessions
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-16 19:20:46 +02:00
Martin Vogel a3903caa0b feat(cli): expand agent integration coverage
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-13 01:50:02 +02:00
Martin Vogel 6074f628ef packaging: opt into the UI build via CBM_VARIANT=ui on npm and PyPI
DCO / dco (push) Has been cancelled
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>
2026-07-08 13:56:58 +02:00
Martin Vogel f714bf3de1 Extend the notices bundle to the UI dependencies and the Glama image
The with-ui archives now append the per-package license texts of the
frontend bundle's production dependency tree to THIRD_PARTY_NOTICES.md
(platform-specific native build tooling is listed but excluded — its
code never reaches the browser bundle). The Glama check image carries
LICENSE and the notices file alongside the binary. Also renames a
TypeScript LSP test to describe its generic ODM-interface fixture.
2026-06-12 10:51:11 +02:00
Martin Vogel 92f38b63b3 Sync packaging metadata to v0.8.1
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.
2026-06-12 10:07:21 +02:00
Martin Vogel eb748cb532 Bump the go-install shim to v0.8.1 2026-06-12 09:59:51 +02:00
Martin Vogel 83eb998c2e Point package-manager manifests at v0.8.1
Homebrew formula, AUR PKGBUILD/.SRCINFO, Scoop, Winget (new 0.8.1
manifest folder), and Chocolatey all move from 0.6.1 to the current
release with refreshed checksums.
2026-06-12 09:57:59 +02:00
Martin Vogel 487f3f945b Bundle third-party notices into release archives; extend release tooling
Release archives now carry THIRD_PARTY_NOTICES.md, generated by
scripts/gen-third-party-notices.sh from THIRD_PARTY.md, the grammar
manifest, and the per-component license texts; the Homebrew formula
and AUR PKGBUILD install it alongside the binary. The SBOM gains
per-component license metadata, corrected versions, and the previously
missing vendored libraries. The security workflow gains a
vendored-license scan with an explicit allow-list policy, and the
release workflow exposes a skip_perf input for releases that do not
touch pipeline logic.
2026-06-12 02:17:39 +02:00
Martin Vogel 972577145f Bump registry packaging versions to 0.8.0
The release pipeline publishes pkg/npm and pkg/pypi as-is; both still carried 0.7.0, so npm rejected the publish with 'cannot publish over the previously published versions' and failed the publish-registries job (the GitHub release itself drafted fine with all assets). server.json bumped to match.
2026-06-11 19:50:58 +02:00
Martin Vogel 9dbed50068 Move Glama packaging into pkg/glama; drop redundant packaging/ dir
packaging/glama -> pkg/glama so all packaging lives under pkg/. Update the
_smoke.yml job command and the Dockerfile/comment references to the new path.
verify.sh is path-relative (dirname $0), so its logic is unchanged.
2026-06-09 13:57:12 +02:00
Martin Vogel 69c16cada4 fix: deliver the static -portable linux binary on all install/update paths
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.
2026-06-09 11:36:00 +02:00
Martin Vogel 9507c4b0a1 Add official MCP Registry publishing to the release pipeline
- Add `mcpName` to the npm package and an `mcp-name:` marker to the PyPI
  README so the registry can verify package ownership for both.
- Rewrite server.json to reference the npm + PyPI packages instead of
  five per-platform mcpb tarballs. The old form was pinned to a stale
  0.6.1 with hardcoded SHA-256s that needed manual updates every release;
  the package form only needs a version sync. Also trims the description
  to the registry's 100-character limit.
- Add a publish-mcp-registry job: downloads mcp-publisher, authenticates
  via GitHub Actions OIDC (no token, no device flow), syncs server.json's
  version to the just-published npm package, and publishes. It runs after
  npm/PyPI but does not gate the release un-draft, so a registry outage
  never blocks shipping and the job can be retried on its own.
2026-06-09 11:36:00 +02:00
Martin Vogel 0338685c06 Advertise semantic search and correct language count across docs
Surface previously undocumented capabilities on the site, README, and
package metadata: semantic vector search (bundled nomic-embed-code
embeddings, fully local), SEMANTICALLY_RELATED / SIMILAR_TO clone
detection, cross-repo intelligence, data-flow tracing, change-impact
analysis, and ADR management. Broaden the cross-service entry to cover
gRPC/GraphQL/tRPC and pub/sub channels. Add a "star on GitHub" nav CTA.

GEO/SEO: add a question-style semantic-search section, extend the
JSON-LD featureList and FAQPage, add semantic keywords, and refresh the
sitemap lastmod.

Correct the language count to 159 (was 155/158) everywhere while keeping
the 157 vendored-grammar figure, update the Linux-kernel index stats to
the measured 4.81M nodes / 7.72M edges, and make trace_path the canonical
tool name (trace_call_path noted as an alias).
2026-06-05 18:48:41 +02:00
Martin Vogel 600fe9033b docs: bump language count to 158 (add QML, CFML) and grammar count to 157
New languages added this round: Qt QML (.qml), CFML/ColdFusion (.cfc script +
.cfm tag). Update README, npm README, and chocolatey description to the
accurate distinct-language count (158) and vendored-grammar count (157).
2026-06-01 18:45:14 +02:00
Martin Vogel b133a204d2 release: bump pkg/npm + pkg/pypi to v0.7.0
The release.yml workflow publishes both wrappers as-is from the repo;
they were still pinned to 0.6.1, which made the npm publish step try to
republish v0.6.1 instead of v0.7.0.
2026-05-30 01:15:58 +02:00
Martin Vogel 331a581551 docs(post-release): align README, server.json, package wrappers with v0.6.1
- README badges: languages 66->155, agents 10->11, tests 2586->2812 (actual count from v0.6.1 test run)
- README: VirusTotal table, primary VT badge link, all '66 languages' references, expanded language list with all newly added grammars
- server.json: bump 0.5.5->0.6.1 (was two releases behind), update all download URLs and sha256s
- pkg/scoop, homebrew, aur, chocolatey, winget: bump to 0.6.1 with new sha256s from v0.6.1 checksums.txt
- pkg/winget: new 0.6.1/ manifest directory (winget keeps version history; 0.6.0/ retained)
- pkg/npm/README: language and agent counts
2026-05-05 00:44:35 +02:00
Martin Vogel 4cdc2dc9f3 feat(release): auto-publish npm and PyPI wrappers
Add publish-registries job to release.yml that runs after verify
(release published, VirusTotal table appended). Publishes:

- pkg/npm via 'npm publish --access public --provenance'
- pkg/pypi via 'twine upload' (sdist + wheel from hatchling)

Both wrappers fetch the binary at install time, so a version bump is
the only per-release change needed. Bumped to 0.6.1.

Requires NPM_TOKEN and PYPI_TOKEN repo secrets.
2026-05-04 23:17:51 +02:00
Martin Vogel c1eb3dd2d1 Comprehensive security hardening across all installers
install.sh:
- Wrap entire body in main() to prevent partial execution from curl|bash
  (interrupted download would execute truncated script)

install.ps1:
- Enforce TLS 1.2+ (older PowerShell defaults to TLS 1.0)

PyPI (_cli.py):
- Add SHA256 checksum verification against checksums.txt (was the only
  installer without checksums)

npm (install.js):
- Add SHA256 checksum verification against checksums.txt
- Validate HTTPS on every redirect hop (max 5 redirects)
- Replace execSync string interpolation with execFileSync array args
  (eliminates shell injection vector in tar/PowerShell calls)
- Add path traversal check on extracted binary

npm (bin.js):
- Auto-download binary if missing (handles --ignore-scripts / pnpm)

Go wrapper (main.go):
- Custom HTTP client with CheckRedirect that rejects non-HTTPS redirects
  (Go's default http.Get follows redirects without scheme validation)
- Fix variable name bug in error message (url → rawURL)
2026-04-14 21:00:42 +02:00
Martin Vogel ce20ff9bbe Harden npm installer: checksum verification, HTTPS-only redirects, no shell injection
- Add SHA256 checksum verification against checksums.txt (was missing,
  all other installers had it)
- Validate URL scheme on every redirect hop (HTTPS-only, max 5 redirects)
- Replace execSync string interpolation with execFileSync array args
  (eliminates shell injection vector)
- Add path traversal check on extracted binary (tar-slip defense)
2026-04-14 20:51:18 +02:00
Martin Vogel a89bf96382 Harden installers: remove Unblock-File, add HTTPS-only URL validation
install.ps1:
- Remove both Unblock-File calls (Defender ClickFix.R!ml signal)
- Remove ExecutionPolicy ByPass from usage comment (scanned by Defender)
- Add HTTPS-only URL scheme check on CBM_DOWNLOAD_URL

install.sh:
- Add HTTPS-only URL scheme check on CBM_DOWNLOAD_URL

pkg/go wrapper:
- Add validateURLScheme() to httpGet and fetchChecksums (defense-in-depth,
  matching Python wrapper's _validate_url_scheme from PR #248)

All installers now allow localhost/127.0.0.1 for testing but reject
http://, ftp://, file:// and other schemes.
2026-04-14 20:44:17 +02:00
Dustin c552db6447 fix: harden PyPI installer against tar-slip and scheme attacks (#246)
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"`.
2026-04-12 23:52:46 -07:00
DeusData aa0864d0bd Add README and fix files list for npm package
Adds a README.md to pkg/npm so the npmjs.com package page shows
documentation. Updates the files list in package.json to include it.
Will be published with the next version bump.
2026-04-12 18:29:10 +01:00
DeusData 39901769fd Add pkg/go — Go module wrapper for go install distribution
Adds a thin Go wrapper at pkg/go/cmd/codebase-memory-mcp that detects
the current platform and architecture, downloads the matching pre-built
binary from GitHub Releases, verifies its SHA-256 checksum, caches it,
and replaces the current process via exec.

Install via:
  go install github.com/DeusData/codebase-memory-mcp/pkg/go/cmd/codebase-memory-mcp@latest

The Go module proxy indexes this automatically once live on main.
2026-04-12 18:28:29 +01:00
DeusData 592be8ea42 Add pkg/ — distribution wrappers for npm, PyPI, Homebrew, Scoop, Winget, Chocolatey, AUR
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.
2026-04-12 17:54:53 +01:00