KeiSeiKit-1.0/_primitives/_rust/kei-cortex/Cargo.toml
Parfii-bot 03d57c7395 fix(kei-cortex): SSRF + atomic token + body limits + capped reads
Group C — kei-cortex daemon security hardening (post-audit 2026-05-02).

- fal_ssrf.rs (new): validate_fal_url whitelist (fal.ai/.media/.run only).
                      Applied to upload_url, file_url, status_url, images[0].url,
                      and download_image. Closes SSRF where compromised fal response
                      could direct daemon to fetch IMDSv1 (169.254.169.254) and
                      stream cloud creds.
- fal_pipeline.rs (new): HTTP step functions extracted from fal.rs; fal.rs trimmed
                          to thin orchestrator (101 LOC, was over 200 LOC limit).
- auth.rs: save_token now writes to <path>.<nanos>.tmp + sync_all + rename. Was
            non-atomic OpenOptions truncate+write — crash mid-write produced empty
            token file -> bootstrap rotated -> stale clients locked out.
- routes.rs + routes_auth.rs (new): explicit DefaultBodyLimit per route — chat 256 KiB,
                                     tool/apply 11 MiB, pet/interaction 64 KiB, tts 32 KiB.
                                     Bearer auth middleware extracted to routes_auth.
- handlers/chat.rs: validate_body enforces MAX_MESSAGE_CHARS = 50_000. Closed cost
                     amplification where 1.99 MiB chat message billed 500K tokens
                     ($1.50/turn at Sonnet pricing) on every send.
- anthropic_sse.rs: SseParser MAX_BUF = 1 MiB cap; was unbounded — peer streaming
                     1 GB without \\n\\n would OOM daemon.
- http_helpers.rs (new): HTTP_CLIENT: Lazy<reqwest::Client> shared across handlers
                          (was per-request Client::new() => 100-300ms TLS handshake
                          per chat turn, no HTTP/2 multiplexing, fd leak risk on
                          macOS TIME_WAIT).
- http_helpers.rs::read_capped: per-response body cap (16 KiB error / 64 MiB success).
                                  Applied to anthropic, anthropic_invoker, elevenlabs,
                                  fal_pipeline. Closed unbounded resp.text() / .bytes()
                                  pattern that compromised upstream could exploit.

Test results: 462 passed; 0 failed (single-threaded). cargo check clean.
2 pre-existing port-binding flakes in openai_loop_wiring tests are unrelated.

Findings consensus: fal SSRF + body-size + bearer-token-atomicity appeared in
Wave-A retest; chat message cap + SSE buf cap appeared in Wave-A only. Would have
been missed by single audit pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 21:39:57 +08:00

67 lines
2.1 KiB
TOML

[package]
name = "kei-cortex"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
description = "Local HTTP daemon exposing cortex state for UI consumption"
authors = ["Denis Parfionovich <info@greendragon.info>"]
[[bin]]
name = "kei-cortex"
path = "src/main.rs"
[lib]
name = "kei_cortex"
path = "src/lib.rs"
[dependencies]
axum = { version = "0.7", features = ["multipart", "ws"] }
tokio = { workspace = true }
tokio-util = { version = "0.7", features = ["rt"] }
tower = { workspace = true }
tower-http = { version = "0.5", features = ["cors", "trace"] }
serde = { workspace = true }
serde_json = { workspace = true }
clap = { workspace = true }
thiserror = { workspace = true }
rusqlite = { workspace = true }
anyhow = { workspace = true }
rand = "0.8"
reqwest = { workspace = true }
tokio-stream = { workspace = true }
futures = { workspace = true }
uuid = { version = "1", features = ["v4"] }
async-stream = "0.3"
toml = { workspace = true }
bytes = { workspace = true }
tempfile = { workspace = true }
dashmap = { workspace = true }
walkdir = { workspace = true }
which = "6"
once_cell = "1"
regex = { workspace = true }
portable-pty = { workspace = true }
# Wave 44a — tool-sandbox hardening
shell-words = { workspace = true }
url = { workspace = true }
lru = { workspace = true }
# Wave 44b — symlink-safe writes
nix = { workspace = true }
# Wave 44d — calendar usage boundaries
chrono = { workspace = true }
kei-pet = { path = "../kei-pet" }
kei-router = { path = "../kei-router" }
kei-shared = { path = "../kei-shared" }
kei-ledger = { path = "../kei-ledger" }
# Wave 55 Stage 2 — universal model registry. `default_model()` in
# `anthropic.rs` consults this for the `kei-cortex-default` role before
# falling back to the literal pin.
kei-model = { path = "../kei-model" }
# Phase 2 — per-turn token telemetry. Every chat handler fires a
# fire-and-forget `Store::record_event` after Done so sleep-report has
# real data. Open lazily on AppState init; tracker IO failures must
# never break the chat call.
kei-token-tracker = { path = "../kei-token-tracker" }
[dev-dependencies]
reqwest = { workspace = true, features = ["blocking"] }