Synced from monorepo
Synced from monorepo Changes: - Temporarily disable session share link creation in the TUI - Do not approve plan on empty Enter from the revise prompt - Expose chat product Skills via ACP available_commands_update - Return immediately from a blocking wait on an already-completed ACP task - Split headless pager module for clearer structure - Stop git worktree prune from removing user registrations on resume - Use compaction sampler tokenizer for item token counts - Opt-in extra root CAs via GROK_EXTRA_CA_BUNDLE - Cancel all session subagents when the user stops - Let the session persistence actor exit when its session ends - Make fullscreen terminal resize much cheaper on long sessions - Report honestly from kill_task when an ACP task does not exist - Hide /usage for external-auth deployments - Forward the history-load trailer’s computer_reason to the client - Remove ineffective no-op tool reminder - Declare slash-command screen-mode support in one place - Keep settings enum picker on the committed value until Enter - Reap a PTY’s full process tree - Stream tool calls from headless mode over ACP - Bridge gateway task lifecycle to ACP for chat session background tasks - Don’t warn about truncated history on a suppressed replay - Fit full-replace summarizer input and recover on context-length errors - Stop dropping agents over an unrecognized frontmatter color - Add /undo as a slash alias for /rewind - Harden sleep/wake token-refresh paths against forced re-login - Add session/list ACP method - Give each sampling backend its own conversion module - Treat an unenrolled child process as a lint error - Suppress the cancelled marker on send-now wake turns - Stop tearing down Roslyn on every edit, and read C# diagnostics Source-Revision: 2a28b4a86cfc4a4c133c35b7fc2a6a9964387c39
This commit is contained in:
parent
500129c714
commit
dd04f397b1
367 changed files with 29489 additions and 10051 deletions
20
crates/codegen/xai-grok-extra-ca/Cargo.toml
Normal file
20
crates/codegen/xai-grok-extra-ca/Cargo.toml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
license = "Apache-2.0"
|
||||
name = "xai-grok-extra-ca"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
description = "Opt-in extra TLS roots from GROK_EXTRA_CA_BUNDLE (validated DER cache + reqwest 0.12 adapters)"
|
||||
|
||||
[dependencies]
|
||||
reqwest = { workspace = true }
|
||||
# Explicit pin (not `workspace = true`): the workspace rustls pin enables
|
||||
# aws-lc-rs. This crate only needs RootCertStore + PEM parse, so stay
|
||||
# default-features = false with `std` (enables pki-types/std for PEM).
|
||||
rustls = { version = "0.23", default-features = false, features = ["std"] }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
184
crates/codegen/xai-grok-extra-ca/src/lib.rs
Normal file
184
crates/codegen/xai-grok-extra-ca/src/lib.rs
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
//! Opt-in extra TLS roots via `GROK_EXTRA_CA_BUNDLE` (PEM path).
|
||||
//!
|
||||
//! Default-off (unset/empty env → no I/O); parsed once into a process
|
||||
//! `OnceLock`; additive to webpki roots. Each DER is validated with
|
||||
//! `rustls::RootCertStore::add` before caching so a bad bundle cannot fail
|
||||
//! `ClientBuilder::build()`. Unreadable/oversized/empty/unparsable → warn and
|
||||
//! continue. Size cap: [`MAX_EXTRA_CA_BUNDLE_BYTES`].
|
||||
//!
|
||||
//! Source of truth is validated DER ([`extra_root_ders`]) so reqwest 0.12
|
||||
//! (this crate's adapters) and MCP's 0.13 can each build their own
|
||||
//! `Certificate`s.
|
||||
|
||||
use std::io::Read;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use rustls::RootCertStore;
|
||||
use rustls::pki_types::CertificateDer;
|
||||
use rustls::pki_types::pem::PemObject;
|
||||
|
||||
/// Hard cap on `GROK_EXTRA_CA_BUNDLE` (1 MiB) — avoids unbounded startup reads.
|
||||
pub const MAX_EXTRA_CA_BUNDLE_BYTES: u64 = 1024 * 1024;
|
||||
|
||||
/// Env var name for the opt-in extra CA bundle (PEM path).
|
||||
pub const ENV_GROK_EXTRA_CA_BUNDLE: &str = "GROK_EXTRA_CA_BUNDLE";
|
||||
|
||||
/// Process-wide extra roots as validated DER, parsed once.
|
||||
///
|
||||
/// Empty when the env var is unset/empty or the file yields no usable certs.
|
||||
pub fn extra_root_ders() -> &'static [Vec<u8>] {
|
||||
static DERS: OnceLock<Vec<Vec<u8>>> = OnceLock::new();
|
||||
DERS.get_or_init(load_extra_root_ders).as_slice()
|
||||
}
|
||||
|
||||
/// Apply [`extra_root_ders`] to a workspace (reqwest 0.12) async `ClientBuilder`.
|
||||
pub fn with_extra_root_certificates(mut builder: reqwest::ClientBuilder) -> reqwest::ClientBuilder {
|
||||
for der in extra_root_ders() {
|
||||
match reqwest::Certificate::from_der(der) {
|
||||
Ok(cert) => builder = builder.add_root_certificate(cert),
|
||||
// WHY: rustls already accepted this DER; skip rather than poison build.
|
||||
Err(e) => tracing::warn!(
|
||||
error = %e,
|
||||
"GROK_EXTRA_CA_BUNDLE: validated DER rejected by reqwest; skipping cert"
|
||||
),
|
||||
}
|
||||
}
|
||||
builder
|
||||
}
|
||||
|
||||
/// Apply [`extra_root_ders`] to a workspace (reqwest 0.12) blocking `ClientBuilder`.
|
||||
pub fn with_extra_root_certificates_blocking(
|
||||
mut builder: reqwest::blocking::ClientBuilder,
|
||||
) -> reqwest::blocking::ClientBuilder {
|
||||
for der in extra_root_ders() {
|
||||
match reqwest::Certificate::from_der(der) {
|
||||
Ok(cert) => builder = builder.add_root_certificate(cert),
|
||||
// WHY: rustls already accepted this DER; skip rather than poison build.
|
||||
Err(e) => tracing::warn!(
|
||||
error = %e,
|
||||
"GROK_EXTRA_CA_BUNDLE: validated DER rejected by reqwest; skipping cert"
|
||||
),
|
||||
}
|
||||
}
|
||||
builder
|
||||
}
|
||||
|
||||
fn load_extra_root_ders() -> Vec<Vec<u8>> {
|
||||
let path = match std::env::var_os(ENV_GROK_EXTRA_CA_BUNDLE) {
|
||||
Some(p) if !p.is_empty() => std::path::PathBuf::from(p),
|
||||
_ => return Vec::new(),
|
||||
};
|
||||
|
||||
let bytes = match read_bundle_capped(&path) {
|
||||
Ok(b) => b,
|
||||
Err(BundleReadError::Io(e)) => {
|
||||
// WHY: MITM CA is optional; a missing path must not brick HTTP.
|
||||
tracing::warn!(
|
||||
path = %path.display(),
|
||||
error = %e,
|
||||
"GROK_EXTRA_CA_BUNDLE unreadable; continuing without extra roots"
|
||||
);
|
||||
return Vec::new();
|
||||
}
|
||||
Err(BundleReadError::TooLarge) => {
|
||||
tracing::warn!(
|
||||
path = %path.display(),
|
||||
max_bytes = MAX_EXTRA_CA_BUNDLE_BYTES,
|
||||
"GROK_EXTRA_CA_BUNDLE exceeds size cap; continuing without extra roots"
|
||||
);
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
|
||||
let outcome = parse_and_validate_pem(&bytes);
|
||||
if outcome.no_pem_blocks {
|
||||
tracing::warn!(
|
||||
path = %path.display(),
|
||||
"GROK_EXTRA_CA_BUNDLE contains no PEM certificate blocks; continuing without extra roots"
|
||||
);
|
||||
return outcome.accepted;
|
||||
}
|
||||
if outcome.rejected > 0 {
|
||||
tracing::warn!(
|
||||
path = %path.display(),
|
||||
accepted = outcome.accepted.len(),
|
||||
rejected = outcome.rejected,
|
||||
"GROK_EXTRA_CA_BUNDLE: dropped unusable certificate block(s)"
|
||||
);
|
||||
}
|
||||
if outcome.accepted.is_empty() {
|
||||
tracing::warn!(
|
||||
path = %path.display(),
|
||||
"GROK_EXTRA_CA_BUNDLE produced zero usable certificates; continuing without extra roots"
|
||||
);
|
||||
} else {
|
||||
tracing::info!(
|
||||
path = %path.display(),
|
||||
accepted = outcome.accepted.len(),
|
||||
"GROK_EXTRA_CA_BUNDLE: loaded extra root certificate(s)"
|
||||
);
|
||||
}
|
||||
outcome.accepted
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum BundleReadError {
|
||||
Io(std::io::Error),
|
||||
TooLarge,
|
||||
}
|
||||
|
||||
fn read_bundle_capped(path: &std::path::Path) -> Result<Vec<u8>, BundleReadError> {
|
||||
let file = std::fs::File::open(path).map_err(BundleReadError::Io)?;
|
||||
let mut buf = Vec::new();
|
||||
let n = file
|
||||
.take(MAX_EXTRA_CA_BUNDLE_BYTES + 1)
|
||||
.read_to_end(&mut buf)
|
||||
.map_err(BundleReadError::Io)?;
|
||||
if (n as u64) > MAX_EXTRA_CA_BUNDLE_BYTES {
|
||||
return Err(BundleReadError::TooLarge);
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// Result of parsing a PEM bundle into rustls-validated DER roots.
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct ParseOutcome {
|
||||
pub(crate) accepted: Vec<Vec<u8>>,
|
||||
/// PEM blocks that failed decode or rustls X.509 validation.
|
||||
pub(crate) rejected: usize,
|
||||
/// Input (non-empty) contained no PEM certificate blocks at all.
|
||||
pub(crate) no_pem_blocks: bool,
|
||||
}
|
||||
|
||||
/// Parse PEM into rustls-validated DER (no env / OnceLock). Input with no PEM
|
||||
/// certificate blocks (including empty) → empty accepted, zero rejected,
|
||||
/// `no_pem_blocks` set.
|
||||
pub(crate) fn parse_and_validate_pem(pem: &[u8]) -> ParseOutcome {
|
||||
let mut accepted = Vec::new();
|
||||
let mut rejected = 0usize;
|
||||
let mut saw_block = false;
|
||||
|
||||
// WHY: reject non-X.509 DER before any ClientBuilder sees it; `add`
|
||||
// validates per certificate, so one store serves the whole bundle.
|
||||
let mut store = RootCertStore::empty();
|
||||
for item in CertificateDer::pem_slice_iter(pem) {
|
||||
saw_block = true;
|
||||
match item {
|
||||
Ok(der) => match store.add(der.clone()) {
|
||||
Ok(()) => accepted.push(der.as_ref().to_vec()),
|
||||
Err(_) => rejected += 1,
|
||||
},
|
||||
Err(_) => rejected += 1,
|
||||
}
|
||||
}
|
||||
|
||||
ParseOutcome {
|
||||
accepted,
|
||||
rejected,
|
||||
no_pem_blocks: !saw_block,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "lib_tests.rs"]
|
||||
mod tests;
|
||||
130
crates/codegen/xai-grok-extra-ca/src/lib_tests.rs
Normal file
130
crates/codegen/xai-grok-extra-ca/src/lib_tests.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
use super::*;
|
||||
|
||||
// Self-signed PEMs for unit tests only (CN=test-extra-ca-1 / -2).
|
||||
const VALID_CERT_1: &str = "-----BEGIN CERTIFICATE-----\n\
|
||||
MIIDFTCCAf2gAwIBAgIUT2czXTuxSAjDjEh92UMB1OVahZYwDQYJKoZIhvcNAQEL\n\
|
||||
BQAwGjEYMBYGA1UEAwwPdGVzdC1leHRyYS1jYS0xMB4XDTI2MDcyOTE4MzUwNFoX\n\
|
||||
DTM2MDcyNjE4MzUwNFowGjEYMBYGA1UEAwwPdGVzdC1leHRyYS1jYS0xMIIBIjAN\n\
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1gNk2BQwUy+n5cCaTFtGpSzVQv//\n\
|
||||
d7QD+3QWeE411wIGJzp3nrd7np55X8JHxeg/pRhspQvLQAF7bt55LSkL/+sSth3S\n\
|
||||
QTbBqhftic9CXik3llAwbdQkAM9srz5zXWW9KVjZ57dxjjxrS15SCXu/UmvGZy98\n\
|
||||
faJcS++TRkczsNFzwQEqeDYARVc/no0C0I++NhGLPaNMfFAevvnu6Kt3CYMI5ls4\n\
|
||||
KCFgnlau4CjgRCMSfRDCRcwEwUAp+DyX9IU+tvDAQY1ncVoa/05tvaEvw7pQ+UgW\n\
|
||||
0wRG0lk7PLlcWmUkLcFpO+sL5GRkC8RoWM4cFbIOiXoVxUFks/z2y0GCEQIDAQAB\n\
|
||||
o1MwUTAdBgNVHQ4EFgQU+lyC70W5aR6BIf4VNtjfiWMNzzkwHwYDVR0jBBgwFoAU\n\
|
||||
+lyC70W5aR6BIf4VNtjfiWMNzzkwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B\n\
|
||||
AQsFAAOCAQEA02972nA7LshRgubz6BwXbh1gA5pLzTd5KEae+94Hq6mP2zJ1T0gk\n\
|
||||
x+me0NtSgG4BJLdBIylUzo2UmsfB/sz+ght6WX1uB38Vc2UQsp0sRPeeiMovSd6n\n\
|
||||
I7xZyuZEF3noYJVBBlKQ8XsCUIBNIROlyKlNjNcWY8tGqPh9cepvtZYkBgRZr1vW\n\
|
||||
hJAE3EOL2ZddrMPF64QeU9UhvCm0Ch+Ceqa1ZWE0MygccggX5s2yQwtXO2ovJdjH\n\
|
||||
6vW0I02r8sE+NX0d1u8rIPJEKlp89UwCwniD7SxHTNw8bbsTCWz+AMod7vC7De3X\n\
|
||||
4Daxme+vD8adOfCeOIu5vNrlXLNST2yaTw==\n\
|
||||
-----END CERTIFICATE-----\n";
|
||||
|
||||
const VALID_CERT_2: &str = "-----BEGIN CERTIFICATE-----\n\
|
||||
MIIDFTCCAf2gAwIBAgIUKckMakNVssdBbRUlVtyWZZPx7EcwDQYJKoZIhvcNAQEL\n\
|
||||
BQAwGjEYMBYGA1UEAwwPdGVzdC1leHRyYS1jYS0yMB4XDTI2MDcyOTE4MzUwNFoX\n\
|
||||
DTM2MDcyNjE4MzUwNFowGjEYMBYGA1UEAwwPdGVzdC1leHRyYS1jYS0yMIIBIjAN\n\
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3pVKr4xNdWm+RIYVRuOv+8Pg3I3/\n\
|
||||
wsmC7m84I4bw6EofraYY1vTT8XYcWAspo++Tj1hYNAyfdtdrgdZT8dgsTqsVPzYz\n\
|
||||
rluGu03mu0aE9Ix2IieLvR9C0s+mYpsfCQYRjsL2wDD6fOAWN4wjj1R4XGgUZKCF\n\
|
||||
q8JirftcRBLGjAa8XXD496dUGXzURQ7C9jAxFmPWGbyz3f1ymOLBvp8RdzrJNCsA\n\
|
||||
zdEjqJODMMf0czJH5gtt06hIQG9JkPHNqZXVxEIBIDlkmkr9Nk/asqZGhbHILkHX\n\
|
||||
/jqfdOMb4Xu95iglbwbACgAtfysNQdjUU7hbjKxx4S4FCjf+gyb4whQo/QIDAQAB\n\
|
||||
o1MwUTAdBgNVHQ4EFgQUVrqEwVrKpoc/GinOYZR13TkjdwgwHwYDVR0jBBgwFoAU\n\
|
||||
VrqEwVrKpoc/GinOYZR13TkjdwgwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B\n\
|
||||
AQsFAAOCAQEAtK9ylmMIEQsuYm5Qo1pi4xp5rFywO0g5zkWEl/fIMBevP9Thhnco\n\
|
||||
gHiOFBhQcuo+Go65p3Fbbt3Vrx30Oi0hQUlYLlY44BO3/TgfZ0VbIheeDfyYaq97\n\
|
||||
S3I1cLHJ1qmKq99zKcqvCcD+NmifbuMi03Zo35Kp+jm8GXpONumnPlu17WZLw5N7\n\
|
||||
KFHbC1eO3iat27z4WRhPHG4vmPfMHIIvrbA+aEwc1b88NO5UdRmSHvkt4MDEOsIe\n\
|
||||
IgKmdcW5+BG5ffCRJ9wNsCCy165AFUmuNWz0aqDWybjK4eiEb88sHKbVv7fyXpwi\n\
|
||||
YwiFroodmakt1behpPy1p9Ih94MTqy9pQw==\n\
|
||||
-----END CERTIFICATE-----\n";
|
||||
|
||||
/// Valid PEM framing / base64, but DER is not an X.509 certificate.
|
||||
const INVALID_DER_PEM: &str = "-----BEGIN CERTIFICATE-----\n\
|
||||
MAMBAf8=\n\
|
||||
-----END CERTIFICATE-----\n";
|
||||
|
||||
#[test]
|
||||
fn parse_empty_bytes_returns_empty() {
|
||||
let o = parse_and_validate_pem(b"");
|
||||
assert!(o.accepted.is_empty());
|
||||
assert_eq!(o.rejected, 0);
|
||||
assert!(o.no_pem_blocks);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_garbage_non_pem_flags_no_blocks_without_panic() {
|
||||
let o = parse_and_validate_pem(b"this is not a certificate");
|
||||
assert!(o.accepted.is_empty());
|
||||
assert_eq!(o.rejected, 0);
|
||||
assert!(o.no_pem_blocks);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_valid_single_cert_pem() {
|
||||
let o = parse_and_validate_pem(VALID_CERT_1.as_bytes());
|
||||
assert_eq!(o.accepted.len(), 1);
|
||||
assert_eq!(o.rejected, 0);
|
||||
assert!(!o.no_pem_blocks);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multi_cert_bundle() {
|
||||
let o = parse_and_validate_pem(format!("{VALID_CERT_1}\n{VALID_CERT_2}").as_bytes());
|
||||
assert_eq!(o.accepted.len(), 2);
|
||||
assert_eq!(o.rejected, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_invalid_der_pem_rejected() {
|
||||
let o = parse_and_validate_pem(INVALID_DER_PEM.as_bytes());
|
||||
assert!(o.accepted.is_empty());
|
||||
assert!(o.rejected >= 1);
|
||||
assert!(!o.no_pem_blocks);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_mixed_bundle_keeps_valid_drops_invalid() {
|
||||
let o = parse_and_validate_pem(
|
||||
format!("{VALID_CERT_1}\n{INVALID_DER_PEM}\n{VALID_CERT_2}").as_bytes(),
|
||||
);
|
||||
assert_eq!(o.accepted.len(), 2);
|
||||
assert!(o.rejected >= 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validated_ders_build_reqwest_client() {
|
||||
let o = parse_and_validate_pem(VALID_CERT_1.as_bytes());
|
||||
assert_eq!(o.accepted.len(), 1);
|
||||
let mut builder = reqwest::Client::builder();
|
||||
for der in &o.accepted {
|
||||
builder = builder.add_root_certificate(
|
||||
reqwest::Certificate::from_der(der).expect("from_der after rustls validation"),
|
||||
);
|
||||
}
|
||||
builder
|
||||
.build()
|
||||
.expect("client with validated roots must construct");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_bundle_capped_rejects_oversized() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let path = dir.path().join("huge.pem");
|
||||
std::fs::write(&path, vec![b'A'; (MAX_EXTRA_CA_BUNDLE_BYTES as usize) + 1]).unwrap();
|
||||
match read_bundle_capped(&path) {
|
||||
Err(BundleReadError::TooLarge) => {}
|
||||
other => panic!("expected TooLarge, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_bundle_capped_accepts_at_limit() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let path = dir.path().join("ok.pem");
|
||||
std::fs::write(&path, vec![b'B'; MAX_EXTRA_CA_BUNDLE_BYTES as usize]).unwrap();
|
||||
let got = read_bundle_capped(&path).expect("at-limit read");
|
||||
assert_eq!(got.len(), MAX_EXTRA_CA_BUNDLE_BYTES as usize);
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
//! Process-isolated: missing GROK_EXTRA_CA_BUNDLE path → fail-open client build.
|
||||
|
||||
#[test]
|
||||
fn missing_bundle_path_builds_clients_without_panic() {
|
||||
// Safety: sole test in this binary; set before any OnceLock resolve.
|
||||
unsafe {
|
||||
std::env::set_var(
|
||||
xai_grok_extra_ca::ENV_GROK_EXTRA_CA_BUNDLE,
|
||||
"/nonexistent/grok-extra-ca-bundle-invalid-file.pem",
|
||||
);
|
||||
}
|
||||
|
||||
assert!(xai_grok_extra_ca::extra_root_ders().is_empty());
|
||||
|
||||
xai_grok_extra_ca::with_extra_root_certificates(reqwest::Client::builder())
|
||||
.build()
|
||||
.expect("async client builds when bundle is unreadable");
|
||||
|
||||
xai_grok_extra_ca::with_extra_root_certificates_blocking(reqwest::blocking::Client::builder())
|
||||
.build()
|
||||
.expect("blocking client builds when bundle is unreadable");
|
||||
}
|
||||
34
crates/codegen/xai-grok-extra-ca/tests/extra_ca_oversized.rs
Normal file
34
crates/codegen/xai-grok-extra-ca/tests/extra_ca_oversized.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
//! Process-isolated: oversize GROK_EXTRA_CA_BUNDLE → ignored; client still builds.
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
#[test]
|
||||
fn oversized_bundle_ignored_clients_build() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let path = dir.path().join("oversized.pem");
|
||||
{
|
||||
let mut f = std::fs::File::create(&path).expect("create");
|
||||
let chunk = vec![b'X'; 64 * 1024];
|
||||
let mut written = 0u64;
|
||||
let target = xai_grok_extra_ca::MAX_EXTRA_CA_BUNDLE_BYTES + 1;
|
||||
while written < target {
|
||||
let n = ((target - written) as usize).min(chunk.len());
|
||||
f.write_all(&chunk[..n]).expect("write");
|
||||
written += n as u64;
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: sole test in this binary; set before any OnceLock resolve.
|
||||
unsafe {
|
||||
std::env::set_var(
|
||||
xai_grok_extra_ca::ENV_GROK_EXTRA_CA_BUNDLE,
|
||||
path.as_os_str(),
|
||||
);
|
||||
}
|
||||
|
||||
assert!(xai_grok_extra_ca::extra_root_ders().is_empty());
|
||||
|
||||
xai_grok_extra_ca::with_extra_root_certificates(reqwest::Client::builder())
|
||||
.build()
|
||||
.expect("client builds after oversized reject");
|
||||
}
|
||||
41
crates/codegen/xai-grok-extra-ca/tests/extra_ca_valid_env.rs
Normal file
41
crates/codegen/xai-grok-extra-ca/tests/extra_ca_valid_env.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//! Process-isolated: valid GROK_EXTRA_CA_BUNDLE loads one root via OnceLock.
|
||||
|
||||
#[test]
|
||||
fn valid_bundle_loads_one_root() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let path = dir.path().join("ca.pem");
|
||||
const CERT: &str = "-----BEGIN CERTIFICATE-----\n\
|
||||
MIIDFTCCAf2gAwIBAgIUT2czXTuxSAjDjEh92UMB1OVahZYwDQYJKoZIhvcNAQEL\n\
|
||||
BQAwGjEYMBYGA1UEAwwPdGVzdC1leHRyYS1jYS0xMB4XDTI2MDcyOTE4MzUwNFoX\n\
|
||||
DTM2MDcyNjE4MzUwNFowGjEYMBYGA1UEAwwPdGVzdC1leHRyYS1jYS0xMIIBIjAN\n\
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1gNk2BQwUy+n5cCaTFtGpSzVQv//\n\
|
||||
d7QD+3QWeE411wIGJzp3nrd7np55X8JHxeg/pRhspQvLQAF7bt55LSkL/+sSth3S\n\
|
||||
QTbBqhftic9CXik3llAwbdQkAM9srz5zXWW9KVjZ57dxjjxrS15SCXu/UmvGZy98\n\
|
||||
faJcS++TRkczsNFzwQEqeDYARVc/no0C0I++NhGLPaNMfFAevvnu6Kt3CYMI5ls4\n\
|
||||
KCFgnlau4CjgRCMSfRDCRcwEwUAp+DyX9IU+tvDAQY1ncVoa/05tvaEvw7pQ+UgW\n\
|
||||
0wRG0lk7PLlcWmUkLcFpO+sL5GRkC8RoWM4cFbIOiXoVxUFks/z2y0GCEQIDAQAB\n\
|
||||
o1MwUTAdBgNVHQ4EFgQU+lyC70W5aR6BIf4VNtjfiWMNzzkwHwYDVR0jBBgwFoAU\n\
|
||||
+lyC70W5aR6BIf4VNtjfiWMNzzkwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B\n\
|
||||
AQsFAAOCAQEA02972nA7LshRgubz6BwXbh1gA5pLzTd5KEae+94Hq6mP2zJ1T0gk\n\
|
||||
x+me0NtSgG4BJLdBIylUzo2UmsfB/sz+ght6WX1uB38Vc2UQsp0sRPeeiMovSd6n\n\
|
||||
I7xZyuZEF3noYJVBBlKQ8XsCUIBNIROlyKlNjNcWY8tGqPh9cepvtZYkBgRZr1vW\n\
|
||||
hJAE3EOL2ZddrMPF64QeU9UhvCm0Ch+Ceqa1ZWE0MygccggX5s2yQwtXO2ovJdjH\n\
|
||||
6vW0I02r8sE+NX0d1u8rIPJEKlp89UwCwniD7SxHTNw8bbsTCWz+AMod7vC7De3X\n\
|
||||
4Daxme+vD8adOfCeOIu5vNrlXLNST2yaTw==\n\
|
||||
-----END CERTIFICATE-----\n";
|
||||
std::fs::write(&path, CERT).expect("write cert");
|
||||
|
||||
// Safety: sole test in this binary; set before any OnceLock resolve.
|
||||
unsafe {
|
||||
std::env::set_var(
|
||||
xai_grok_extra_ca::ENV_GROK_EXTRA_CA_BUNDLE,
|
||||
path.as_os_str(),
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(xai_grok_extra_ca::extra_root_ders().len(), 1);
|
||||
|
||||
xai_grok_extra_ca::with_extra_root_certificates(reqwest::Client::builder())
|
||||
.build()
|
||||
.expect("client with env-loaded root builds");
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
//! Process-isolated: configured garbage file → zero roots; client still builds.
|
||||
|
||||
#[test]
|
||||
fn configured_garbage_file_yields_zero_roots_and_builds() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let path = dir.path().join("garbage.pem");
|
||||
std::fs::write(&path, b"not a pem at all").expect("write");
|
||||
|
||||
// Safety: sole test in this binary; set before any OnceLock resolve.
|
||||
unsafe {
|
||||
std::env::set_var(
|
||||
xai_grok_extra_ca::ENV_GROK_EXTRA_CA_BUNDLE,
|
||||
path.as_os_str(),
|
||||
);
|
||||
}
|
||||
|
||||
assert!(xai_grok_extra_ca::extra_root_ders().is_empty());
|
||||
|
||||
xai_grok_extra_ca::with_extra_root_certificates(reqwest::Client::builder())
|
||||
.build()
|
||||
.expect("client builds after zero-cert configured file");
|
||||
}
|
||||
Loading…
Reference in a new issue