Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "codesearch"
version = "1.1.30"
version = "1.1.31"
edition = "2021"
authors = ["codesearch contributors"]
license = "Apache-2.0"
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down Expand Up @@ -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.
Expand Down
Loading