From 1c594e6c6fecde9ecaea0812d04e3248c2f712d2 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 23 Jul 2026 14:50:57 +0200 Subject: [PATCH] docs: changelog + README updates for PRs #150-#157 (Aikido security sweep) Documents the security hardening sweep and follow-up fixes that landed in develop since the [1.1.30] changelog entry, none of which had been changelogged or documented in README: - PR #151: critical path-traversal fixes (Rust + .NET) + CI persist-credentials - PR #152: ANSI-injection sanitization, .git-root rejection, Unix backslash path-cache collision fix - PR #153: CodeQL checkout SHA pinning - PR #154: rmcp 1.5.0->1.8.0 + ~100 transitive dependency CVE updates - PR #150 (external, @tony-nexartis): RLIMIT_NOFILE fd-exhaustion fix - PR #156: skip CodeQL analyze on fork PRs (restricted GITHUB_TOKEN can't upload SARIF to upstream) - PR #157: byte-boundary panic fix (#148, @tony-nexartis) + new CODESEARCH_ALLOWED_HOSTS / CODESEARCH_DISABLE_HOST_VALIDATION env vars (#149, @stdweird) Also bumps Cargo.toml to 1.1.31 for this documentation/version-tracking release. No functional code changes in this commit. --- CHANGELOG.md | 21 +++++++++++++++++++++ Cargo.toml | 2 +- README.md | 25 +++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20e41f81..34b0a17b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.1.31] - 2026-07-23 + +**Security hardening sweep (Aikido) + community bug/dependency fixes.** + +### Added + +- **EmbeddingGemma retrieval support (#155, original work by @markschroedr, superseding #147).** Adds support for Google's EmbeddingGemma embedding model as an additional embedder option, alongside model-selection hardening and improved error messages for unsupported/misconfigured embedding models. +- **`CODESEARCH_ALLOWED_HOSTS` / `CODESEARCH_DISABLE_HOST_VALIDATION` (#149, reported by @stdweird).** rmcp's DNS-rebinding defence defaults the MCP transport's `Host`-header allowlist to loopback-only, rejecting container/service hostnames in containerised deployments. `CODESEARCH_ALLOWED_HOSTS` lets you extend the allowlist with a comma-separated hostname list; `CODESEARCH_DISABLE_HOST_VALIDATION=1` disables the check entirely (only safe behind a reverse proxy). See README `## Security`. +- **`raise_fd_limit()` at serve startup (#150, contributed by @tony-nexartis).** `codesearch serve`'s fd demand scales with registered repo count; under process supervisors with a low default `ulimit -n` (notably macOS launchd, 256), this could silently exhaust file descriptors and wedge `accept()` with `EMFILE` while the daemon still looked healthy. Serve now raises its own soft `RLIMIT_NOFILE` to the hard limit at startup (Unix only) and warns if the effective limit still looks insufficient for the repo count. +- **`persist-credentials: false`** added to every `actions/checkout` step across all GitHub Actions workflows, and the CodeQL workflow's floating `actions/checkout@v4` pinned to the same SHA already used elsewhere — reduces the blast radius of a compromised CI step and closes a supply-chain drift gap. +- **CodeQL skipped on fork PRs.** Fork-originated PRs carry a restricted `GITHUB_TOKEN` that cannot upload SARIF results to the upstream repo, which was failing the CodeQL check on every external contribution (e.g. #150) with a confusing "Resource not accessible by integration" error unrelated to the PR's actual code. The analyze job is now skipped for fork PRs (still runs on `develop`/`master` push, same-repo PRs, and the schedule). + +### Fixed + +- **Panic on multi-byte UTF-8 boundary in search snippets (#148, reported by @tony-nexartis).** Search-result snippet truncation byte-sliced content at a fixed offset, panicking whenever that offset landed inside a multi-byte character (box-drawing glyphs, CJK, emoji). Now truncates on a char boundary. +- **Path-traversal hardening (critical).** `codesearch index`'s project-path resolution no longer silently falls back to the raw, unvalidated path when canonicalization fails — it now fails fast with an actionable error. The `.NET` symbol-helper CLI (`scip-csharp`) now canonicalizes every path argument (`--solution`, `--project`, `--output`, `--symbols-file`) before use, closing several path-traversal vectors flagged by Aikido SAST. +- **Registering a `.git`/build-artifact directory as a project root.** `codesearch index`/repo registration now rejects a root whose own directory name matches an always-excluded name (`.git`, `.svn`, `node_modules`, etc.), preventing accidental indexing and search-exposure of internal VCS metadata. +- **ANSI/control-sequence injection in terminal output.** Search results and sync/reindex logs now strip ANSI escape sequences (CSI, OSC, Fe) and stray control characters from indexed file content before printing, so a maliciously crafted file can no longer manipulate the user's terminal (clear screen, hide output, rewrite the title bar, etc.). +- **Unix path-cache key collision.** The path-normalization cache used for file metadata unconditionally converted `\` to `/`, which on Unix (where `\` is a legal filename character, not a separator) could collapse a literal-backslash filename with an unrelated subdirectory path into the same cache key. The conversion is now gated to Windows only. +- **Dependency CVE remediation.** `rmcp` floor bumped `1.5.0 → 1.8.0` (3 CVEs fixed); ~100 transitive dependencies refreshed via `cargo update`, including security-relevant bumps to `quinn-proto`, `h2`, `hyper`, `tokio`, `rustls`, `openssl`, `zerocopy`, `zeroize`, `webpki-roots`, `aws-lc-rs`. + ## [1.1.30] - 2026-07-10 ### Added diff --git a/Cargo.toml b/Cargo.toml index 56a5fc6d..3175657f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "codesearch" -version = "1.1.30" +version = "1.1.31" edition = "2021" authors = ["codesearch contributors"] license = "Apache-2.0" diff --git a/README.md b/README.md index 5173ae42..37e55351 100644 --- a/README.md +++ b/README.md @@ -550,6 +550,8 @@ In the `codesearch serve` TUI, mounts appear in **italic/cyan**, distinguishing | `CODESEARCH_SERVE_PORT` | Serve mode port (default: 39725) | | `CODESEARCH_SERVE_API_KEY` | API key for management endpoints + all endpoints when serve binds to a non-localhost address (unset = no auth) | | `CODESEARCH_ALLOWED_ROOTS` | Semicolon-separated allowed roots for repo registration (unset = all allowed) | +| `CODESEARCH_ALLOWED_HOSTS` | Comma-separated hostname allowlist for the MCP streamable-HTTP transport (unset = loopback only: `localhost`, `127.0.0.1`, `::1`). Set this to your container/service hostname when serve runs behind a container network or reverse proxy — see [Security](#security). | +| `CODESEARCH_DISABLE_HOST_VALIDATION` | `1`/`true` disables the MCP transport's Host-header allowlist entirely (DNS-rebinding protection off). Only safe behind a reverse proxy/firewall that already restricts inbound Host headers — see [Security](#security). | | `CODESEARCH_MCP_MODE` | MCP mode: auto, client, local | | `CODESEARCH_REPOS_CONFIG` | Path to repos.json | | `CODESEARCH_REPO_IDLE_TIMEOUT_SECS` | Idle eviction timeout (default: 1800) | @@ -627,6 +629,29 @@ When `codesearch serve` is exposed beyond a single trusted user (shared dev mach Both are backward compatible: unset means no restriction (on a localhost bind). +### MCP transport host allowlist (DNS-rebinding protection) + +The MCP streamable-HTTP transport (via `rmcp`) validates the incoming `Host` header against an allowlist to defend against DNS-rebinding attacks. By default this allowlist is **loopback-only** (`localhost`, `127.0.0.1`, `::1`), which rejects requests carrying a container hostname or service-discovery name — a common trip-up in containerised/orchestrated deployments (Docker, Kubernetes, etc.) where the client connects via a non-loopback Host header. + +- **`CODESEARCH_ALLOWED_HOSTS`** — comma-separated list of extra allowed hostnames (e.g. `codesearch-serve,codesearch-serve.internal`), replacing the loopback-only default. Prefer this over disabling validation. +- **`CODESEARCH_DISABLE_HOST_VALIDATION`** — set to `1` or `true` to disable Host-header validation entirely. This removes the DNS-rebinding protection outright; only use it when serve is already fenced off by a reverse proxy or network policy that restricts which Host headers can reach it. + +Precedence: disable > custom allowlist > default (loopback-only). + +### Hardening against path traversal and injection + +Beyond the access-control gates above, codesearch applies several defense-in-depth mitigations at the filesystem and CLI boundary: + +- Project-path resolution (`index`, repo registration) fails fast on an unresolvable/malformed path instead of silently falling back to the raw, unvalidated input. +- The `.NET` symbol-helper CLI (`scip-csharp`) canonicalizes every path argument (`--solution`, `--project`, `--output`, `--symbols-file`) before use. +- Registering a project root that is itself a VCS/build-artifact directory (`.git`, `.svn`, `node_modules`, etc.) is rejected, preventing accidental indexing/exposure of internal VCS metadata. +- Terminal output (search results, sync/reindex logs) strips ANSI/control-sequence injection from indexed file content before printing, so a maliciously crafted file can't manipulate the user's terminal. +- On Unix, path-cache keys no longer collapse a literal backslash in a filename with a path separator (a Windows-only normalization rule is now gated to Windows). + +### Operational note: file-descriptor limits under process supervisors + +`codesearch serve`'s file-descriptor demand scales with the number of registered repos (each warm repo holds LMDB + full-text-index + file-watcher handles). Under a process supervisor with a low default open-file limit (notably **macOS launchd**, default soft `ulimit -n 256`), a large repo count can silently exhaust file descriptors: `accept()` then fails with `EMFILE` and the daemon looks alive to its supervisor while refusing new connections. Serve now raises its own soft `RLIMIT_NOFILE` to the hard limit at startup (Unix only) and logs a warning if the effective limit still looks insufficient for the registered repo count — but if you see repeated `EMFILE`/"Too many open files" in the logs, raise the **hard** limit for the service (e.g. launchd `SoftResourceLimits`/`HardResourceLimits`, systemd `LimitNOFILE=`, or `ulimit -n` in the service's environment). + ### Federation security model Federation is **operator-to-operator**, not end-user-facing. The only inputs that decide *where* requests go and *which key* they carry are the peer entries you register locally with `codesearch remote add` (stored in `~/.codesearch/repos.json`). No search query, MCP argument, or remote response ever becomes a request target or selects a key.