claude-connector: pin the config envelope, exact bounds and the prompt-dir type check

Mutation survivors of the first pass: each part of the typed envelope (key,
type, schema id, canonical encoding) is refused alone; the exact boundaries of
the child and caller bounds, every serve limit and the bare model prefix are
pinned; a private regular file is not a prompt directory; a short sha256 is
refused.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-29 11:35:44 +01:00
commit 83c93b2bb8

View file

@ -8,8 +8,11 @@ use std::process::{Command, Output};
use std::sync::atomic::{AtomicUsize, Ordering};
use claude_connector::{ClaudeConnectorConfig, ConfigError, load_config, write_config};
use cultcache_rs::{CultCache, DatabaseEntry, SingleFileMessagePackBackingStore};
use model_connector::{CallerConfig, ModelPrice, Rates};
use cultcache_rs::{
CacheBackingStore, CultCache, CultCacheEnvelope, DatabaseEntry,
SingleFileMessagePackBackingStore,
};
use model_connector::{CallerConfig, MIN_PAYLOAD_BYTES, ModelPrice, Rates};
const CANARY: &str = "canary-9f3b7c1d";
const OPUS: &str = "claude-opus-5";
@ -146,6 +149,85 @@ fn a_second_record_or_the_wrong_type_is_refused() {
));
}
fn store_raw(path: &Path, key: &str, ty: &str, schema: Option<&str>, payload: Vec<u8>) {
let mut store = SingleFileMessagePackBackingStore::new(path);
store
.push(&CultCacheEnvelope {
key: key.to_string(),
r#type: ty.to_string(),
payload,
stored_at: "2026-10-29T00:00:00Z".to_string(),
schema_id: schema.map(str::to_string),
})
.unwrap();
}
#[test]
fn the_envelope_must_be_exactly_the_documents_key_type_schema_and_encoding() {
let scratch = Scratch::new();
let canonical = rmp_serde::to_vec(&valid()).unwrap();
let ty = ClaudeConnectorConfig::TYPE;
let mut trailing = canonical.clone();
trailing.push(0xc0);
let cases = vec![
("wrong key", "other", ty, Some(ty), canonical.clone()),
(
"wrong type",
"runtime",
"gamecult.other.v1",
Some(ty),
canonical.clone(),
),
(
"wrong schema",
"runtime",
ty,
Some("gamecult.other.v1"),
canonical.clone(),
),
("not canonical", "runtime", ty, Some(ty), trailing),
];
for (name, key, ty, schema, payload) in cases {
let path = scratch.path(&format!("{}.cc", name.replace(' ', "-")));
store_raw(&path, key, ty, schema, payload);
assert!(
matches!(load_config(&path), Err(ConfigError::Store)),
"{name}"
);
}
// The same bytes with every part right load.
let path = scratch.path("right.cc");
store_raw(&path, "runtime", ty, Some(ty), canonical);
assert_eq!(load_config(&path).unwrap(), valid());
}
#[test]
fn bounds_sit_exactly_where_the_connector_needs_them() {
let mut config = valid();
config.max_total_bytes = config.max_line_bytes;
config.validate().unwrap();
config.callers[0].max_payload_bytes = MIN_PAYLOAD_BYTES;
config.validate().unwrap();
config.callers[0].max_payload_bytes = MIN_PAYLOAD_BYTES - 1;
assert_eq!(refused(&config), "caller bounds");
for set in [
(|c: &mut ClaudeConnectorConfig| c.max_connections = 0) as fn(&mut ClaudeConnectorConfig),
|c| c.socket_timeout_ms = 0,
|c| c.max_expiry_skew_ms = 0,
|c| c.max_frame_bytes = 0,
] {
let mut config = valid();
set(&mut config);
assert_eq!(refused(&config), "serve limits");
}
let mut config = valid();
config.prices.insert("claude-".to_string(), price());
assert_eq!(refused(&config), "prices");
}
#[test]
fn a_write_refuses_an_invalid_document_and_leaves_no_file() {
let scratch = Scratch::new();
@ -473,6 +555,7 @@ fn the_binary_refuses_without_echoing_what_it_was_given() {
let replaced: Vec<(&str, &str)> = vec![
("--listen", CANARY),
("--claude-sha256", CANARY),
("--claude-sha256", "abcd"),
("--price", CANARY),
("--instance", &spaced),
("--connection-key-epoch", CANARY),
@ -569,3 +652,15 @@ fn serve_refuses_an_unsafe_prompt_dir_and_prints_no_credential() {
);
}
}
#[cfg(unix)]
#[test]
fn a_private_regular_file_is_not_a_prompt_dir() {
use std::os::unix::fs::PermissionsExt;
let scratch = Scratch::new();
let file = scratch.path("file");
std::fs::write(&file, "x").unwrap();
std::fs::set_permissions(&file, std::fs::Permissions::from_mode(0o700)).unwrap();
assert!(claude_connector::check_prompt_dir(&file).is_err());
assert!(claude_connector::check_prompt_dir(&scratch.path("absent")).is_err());
}