Synced from monorepo
Changes: - Persist submitter identity for /feedback - Let custom models use rotating tokens from named auth providers - Template stale tool/param name literals in server-native descriptions - Minimal mode commits thinking in full, lookups as one-liners - Tighten durable append internals - Nudge model to end turn on no-op bash commands - Per-fetch signing nonce in the managed-config envelope, with a server-side replay probe - Include working tree in startup status
This commit is contained in:
parent
ba76b0a683
commit
a881e6703f
140 changed files with 6746 additions and 2377 deletions
|
|
@ -75,14 +75,12 @@ pub fn patch_touches_any(patch: &toml::Table, paths: &[PatchPath]) -> bool {
|
|||
paths.iter().any(|p| patch_touches_path(patch, p))
|
||||
}
|
||||
|
||||
/// Keys stripped from every applied patch so an override can't re-introduce a
|
||||
/// nested `version_overrides`/`campaigns` array (recursive re-injection). This
|
||||
/// const owns the recursive-injection keys for every override kind; [`apply_patches`]
|
||||
/// takes the strip list as a parameter so the strip step itself stays key-agnostic.
|
||||
pub const PATCH_STRIP_KEYS: &[&str] = &["version_overrides", "campaigns"];
|
||||
/// Keys stripped from every applied patch: an override cannot re-inject nested
|
||||
/// `version_overrides`/`campaigns` or define `[auth_provider.*]` command tables.
|
||||
pub const PATCH_STRIP_KEYS: &[&str] = &["version_overrides", "campaigns", "auth_provider"];
|
||||
|
||||
/// Deep-merge each patch in iteration order (later wins on a leaf), stripping
|
||||
/// `strip_keys` from every patch first.
|
||||
/// `strip_keys` (top level) first.
|
||||
pub fn apply_patches(
|
||||
config: &mut toml::Value,
|
||||
patches: impl IntoIterator<Item = toml::Table>,
|
||||
|
|
@ -133,10 +131,28 @@ mod tests {
|
|||
let mut p = toml::Table::new();
|
||||
p.insert("version_overrides".into(), toml::Value::Array(vec![]));
|
||||
p.insert("campaigns".into(), toml::Value::Array(vec![]));
|
||||
p.insert(
|
||||
"auth_provider".into(),
|
||||
toml::Value::Table(toml::Table::new()),
|
||||
);
|
||||
p.insert("keep".into(), toml::Value::Boolean(true));
|
||||
apply_patches(&mut cfg2, std::iter::once(p), PATCH_STRIP_KEYS);
|
||||
assert!(cfg2.get("version_overrides").is_none());
|
||||
assert!(cfg2.get("campaigns").is_none());
|
||||
assert!(cfg2.get("auth_provider").is_none());
|
||||
assert_eq!(cfg2["keep"].as_bool(), Some(true));
|
||||
|
||||
// Top-level strip only: a model may still reference a local provider by name.
|
||||
let mut cfg3 = toml::Value::Table(toml::Table::new());
|
||||
let p = table(
|
||||
"[auth_provider.injected]\ncommand = \"evil\"\n\
|
||||
[model.x]\nauth_provider = \"local-name\"\n",
|
||||
);
|
||||
apply_patches(&mut cfg3, std::iter::once(p), PATCH_STRIP_KEYS);
|
||||
assert!(cfg3.get("auth_provider").is_none());
|
||||
assert_eq!(
|
||||
cfg3["model"]["x"]["auth_provider"].as_str(),
|
||||
Some("local-name")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
//! marker stays the (best-effort) authority.
|
||||
use base64::Engine;
|
||||
pub use prod_mc_cli_chat_proxy_types::{
|
||||
MANAGED_IDENTITY_TYP, MANAGED_POLICY_TYP, ManagedIdentityClaim, SignatureEnvelope,
|
||||
SignedPayload, now_unix,
|
||||
MANAGED_CONFIG_NONCE_ECHO_HEADER, MANAGED_IDENTITY_TYP, MANAGED_POLICY_TYP,
|
||||
ManagedIdentityClaim, SignatureEnvelope, SignedPayload, is_server_nonce_shape, now_unix,
|
||||
};
|
||||
/// Compiled-in trusted Ed25519 public keys, `(key_id, raw 32 bytes)`; more than one
|
||||
/// entry only during a rotation. Empty ships dark (see [`verification_active`]).
|
||||
|
|
@ -327,6 +327,27 @@ fn write_envelope_at(path: &std::path::Path, sidecar: &SignatureEnvelope) -> std
|
|||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
|
||||
crate::fs_atomic::write_atomically(path, &json, Some(0o600))
|
||||
}
|
||||
/// Persisted envelope nonce for [`MANAGED_CONFIG_NONCE_ECHO_HEADER`] (unverified;
|
||||
/// telemetry only, never a trust input). Both guards fail open by skipping the
|
||||
/// echo: only the server mint shape (header-safe, so a corrupt sidecar can't brick
|
||||
/// the fetch), and only a payload issued to `fetch_principal`. A leftover sidecar
|
||||
/// from a prior identity must not read as a cross-tenant replay upstream.
|
||||
pub fn stored_envelope_nonce(
|
||||
home: &std::path::Path,
|
||||
fetch_principal: Option<&str>,
|
||||
) -> Option<String> {
|
||||
let fetch_principal = fetch_principal?;
|
||||
let SidecarRead::Present(sidecar) = read_sidecar(home) else {
|
||||
return None;
|
||||
};
|
||||
let payload: SignedPayload = serde_json::from_str(&sidecar.signed_payload).ok()?;
|
||||
let issued_to = payload
|
||||
.deployment_id
|
||||
.as_deref()
|
||||
.or(payload.team_id.as_deref());
|
||||
(issued_to == Some(fetch_principal) && is_server_nonce_shape(&payload.nonce))
|
||||
.then_some(payload.nonce)
|
||||
}
|
||||
/// Whether an authentic claim IMPOSES fail-closed enforcement: verified, bound to
|
||||
/// the KNOWN `expected_principal`, in-date vs the caller-clamped `now_unix`, and
|
||||
/// `fail_closed`. Anything else imposes nothing: permissive (must not override a
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ fn payload() -> SignedPayload {
|
|||
requirements: Some("[features]\nweb_fetch = false\n".into()),
|
||||
fail_closed: false,
|
||||
expires_at: 4_000_000_000,
|
||||
nonce: String::new(),
|
||||
key_id: "v1".into(),
|
||||
}
|
||||
}
|
||||
|
|
@ -888,6 +889,7 @@ fn unknown_signed_key_id_is_rejected() {
|
|||
let home = dir.path();
|
||||
let (kp, pubkey) = test_keypair();
|
||||
let p = SignedPayload {
|
||||
nonce: String::new(),
|
||||
key_id: "v9".into(),
|
||||
fail_closed: true,
|
||||
..payload()
|
||||
|
|
@ -930,6 +932,7 @@ fn rotation_selects_the_trusted_key_by_signed_key_id() {
|
|||
|
||||
let v1 = sign(&kp1, &payload());
|
||||
let v2_payload = SignedPayload {
|
||||
nonce: String::new(),
|
||||
key_id: "v2".into(),
|
||||
..payload()
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue