From 9ff4d73d5c2ebd5622e53cb4c1c835c6ce690798 Mon Sep 17 00:00:00 2001 From: elky Date: Sat, 5 Sep 2026 01:31:21 +0800 Subject: [PATCH] fix(merge): align security contracts with latest main --- .../execution_runtime/attempt_cancellation.rs | 19 +++++++++---------- .../admin/provider/oauth/quota/antigravity.rs | 5 +++-- .../admin/provider/oauth/state/exchange.rs | 5 +++++ .../src/tests/control/admin/oauth.rs | 18 +++++++----------- apps/aether-gateway/src/tests/frontdoor/ai.rs | 2 -- apps/aether-gateway/src/tests/proxy.rs | 12 ------------ .../src/repository/candidates/types.rs | 1 + .../src/provider/providers/antigravity.rs | 1 - 8 files changed, 25 insertions(+), 38 deletions(-) diff --git a/apps/aether-gateway/src/execution_runtime/attempt_cancellation.rs b/apps/aether-gateway/src/execution_runtime/attempt_cancellation.rs index f538810e6..7bd968827 100644 --- a/apps/aether-gateway/src/execution_runtime/attempt_cancellation.rs +++ b/apps/aether-gateway/src/execution_runtime/attempt_cancellation.rs @@ -426,11 +426,11 @@ mod tests { assert!(candidate.finished_at_unix_ms.is_some()); } - /// The guard holds no request body, so its settlement write must describe the - /// capture rather than deny it: a typed `none` capture state would clear the - /// stored request body instead of leaving it alone. + /// The guard holds no request body, and the persistence boundary intentionally + /// rejects request/response capture material. A dropped-attempt settlement + /// must not re-introduce an inline body or a caller-controlled body reference. #[tokio::test] - async fn settling_a_dropped_attempt_leaves_the_captured_request_body_alone() { + async fn settling_a_dropped_attempt_does_not_reintroduce_request_body_capture() { let usage_repository = Arc::new(InMemoryUsageReadRepository::default()); let request_candidate_repository = Arc::new(InMemoryRequestCandidateRepository::default()); let state = test_state(&usage_repository, &request_candidate_repository); @@ -444,7 +444,8 @@ mod tests { candidate_started_unix_ms, ) .await; - // Stand in for a write that already captured this request's body. + // This deliberately supplies capture material to prove that the usage + // persistence boundary strips it before either lifecycle write stores it. let captured_body = json!({"stream": true, "service_tier": "priority"}); let mut capture = build_pending_usage_record( &plan, @@ -478,11 +479,9 @@ mod tests { ) .await .expect("cancelled usage should be recorded"); - assert_eq!(usage.provider_request_body, Some(captured_body)); - assert_ne!( - usage.provider_request_body_state, - Some(UsageBodyCaptureState::None) - ); + assert_eq!(usage.provider_request_body, None); + assert_eq!(usage.provider_request_body_ref, None); + assert_eq!(usage.provider_request_body_state, None); } #[tokio::test] diff --git a/apps/aether-gateway/src/handlers/admin/provider/oauth/quota/antigravity.rs b/apps/aether-gateway/src/handlers/admin/provider/oauth/quota/antigravity.rs index c5e957a43..ee24a7161 100644 --- a/apps/aether-gateway/src/handlers/admin/provider/oauth/quota/antigravity.rs +++ b/apps/aether-gateway/src/handlers/admin/provider/oauth/quota/antigravity.rs @@ -16,8 +16,9 @@ use aether_contracts::ProxySnapshot; use aether_data_contracts::repository::provider_catalog::{ StoredProviderCatalogEndpoint, StoredProviderCatalogKey, StoredProviderCatalogProvider, }; -use aether_provider_pool::build_antigravity_pool_quota_request; -use aether_provider_pool::build_antigravity_pool_quota_summary_request; +use aether_provider_pool::{ + build_antigravity_pool_quota_request, build_antigravity_pool_quota_summary_request, +}; use serde_json::json; use std::collections::BTreeMap; use std::time::{SystemTime, UNIX_EPOCH}; diff --git a/apps/aether-gateway/src/handlers/admin/provider/oauth/state/exchange.rs b/apps/aether-gateway/src/handlers/admin/provider/oauth/state/exchange.rs index 110ca6ec8..789b7b8e7 100644 --- a/apps/aether-gateway/src/handlers/admin/provider/oauth/state/exchange.rs +++ b/apps/aether-gateway/src/handlers/admin/provider/oauth/state/exchange.rs @@ -46,6 +46,11 @@ fn provider_oauth_service_for_template( let adapter = AntigravityProviderOAuthAdapter::default() .with_token_url_override(token_url) .with_user_info_url_override(antigravity_user_info_url); + #[cfg(test)] + let adapter = adapter.with_oauth_credentials_for_tests( + "gateway-test-antigravity-client-id", + "gateway-test-antigravity-client-secret", + ); return Ok(ProviderOAuthService::new().with_adapter(Arc::new(adapter))); } GenericProviderOAuthAdapter::for_provider_type(template.provider_type) diff --git a/apps/aether-gateway/src/tests/control/admin/oauth.rs b/apps/aether-gateway/src/tests/control/admin/oauth.rs index fbb468012..b15aef987 100644 --- a/apps/aether-gateway/src/tests/control/admin/oauth.rs +++ b/apps/aether-gateway/src/tests/control/admin/oauth.rs @@ -4000,13 +4000,16 @@ async fn gateway_names_new_antigravity_oauth_account_from_google_userinfo_email_ .with_encryption_key_for_tests(DEVELOPMENT_ENCRYPTION_KEY), ) .with_provider_oauth_state_entry_for_tests( - "nonce-antigravity-123", + "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", json!({ - "nonce": "nonce-antigravity-123", + "nonce": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", "key_id": "", "provider_id": "provider-antigravity", "provider_type": "antigravity", "pkce_verifier": "verifier-antigravity-123", + "initiated_by_user_id": "admin-user-123", + "initiated_by_session_id": "session-123", + "created_at": aether_admin::provider::state::current_unix_secs(), }), ) .with_provider_oauth_token_url_for_tests( @@ -4029,7 +4032,7 @@ async fn gateway_names_new_antigravity_oauth_account_from_google_userinfo_email_ .header(TRUSTED_ADMIN_USER_ROLE_HEADER, "admin") .header(TRUSTED_ADMIN_SESSION_ID_HEADER, "session-123") .json(&json!({ - "callback_url": "http://localhost:51121/oauth2callback?code=antigravity-code-123&state=nonce-antigravity-123" + "callback_url": "http://localhost:51121/oauth2callback?code=antigravity-code-123&state=cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" })) .send() .await @@ -4062,14 +4065,7 @@ async fn gateway_names_new_antigravity_oauth_account_from_google_userinfo_email_ .expect("created key should load"); let persisted = persisted_keys.first().expect("created key should exist"); assert_eq!(persisted.name, "new-antigravity@example.com"); - let decrypted_auth_config = decrypt_python_fernet_ciphertext( - DEVELOPMENT_ENCRYPTION_KEY, - persisted - .encrypted_auth_config - .as_deref() - .expect("auth config should be stored"), - ) - .expect("auth config should decrypt"); + let decrypted_auth_config = decrypt_persisted_provider_auth_config(persisted); let auth_config: Value = serde_json::from_str(&decrypted_auth_config).expect("auth config json should parse"); assert_eq!(auth_config["email"], "new-antigravity@example.com"); diff --git a/apps/aether-gateway/src/tests/frontdoor/ai.rs b/apps/aether-gateway/src/tests/frontdoor/ai.rs index 2c0cf93df..1ba25afc6 100644 --- a/apps/aether-gateway/src/tests/frontdoor/ai.rs +++ b/apps/aether-gateway/src/tests/frontdoor/ai.rs @@ -47,8 +47,6 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use tokio::sync::oneshot; use wreq::ws::message::Message as WreqWsMessage; -use crate::data::GatewayDataState; - fn codex_models_snapshot( api_key_id: &str, user_id: &str, diff --git a/apps/aether-gateway/src/tests/proxy.rs b/apps/aether-gateway/src/tests/proxy.rs index 4d9f02cb4..281d69a60 100644 --- a/apps/aether-gateway/src/tests/proxy.rs +++ b/apps/aether-gateway/src/tests/proxy.rs @@ -289,18 +289,6 @@ fn tunnel_attachment_key(node_id: &str) -> String { format!("tunnel.attachments.{node_id}") } -fn system_default_affinity_cache_key(api_key_id: &str, api_format: &str, model: &str) -> String { - let scope = aether_scheduler_core::SchedulerAffinityScope::new("system-default", Some(1)); - aether_scheduler_core::build_scheduler_affinity_cache_key_for_api_key_id_with_client_session_and_scope( - api_key_id, - api_format, - model, - None, - Some(&scope), - ) - .expect("system-default affinity cache key should build") -} - fn sample_tunnel_proxy_node(node_id: &str, tunnel_generation: &str) -> StoredProxyNode { StoredProxyNode::new( node_id.to_string(), diff --git a/crates/aether-data/contracts/src/repository/candidates/types.rs b/crates/aether-data/contracts/src/repository/candidates/types.rs index 48ea74355..58bf33b6f 100644 --- a/crates/aether-data/contracts/src/repository/candidates/types.rs +++ b/crates/aether-data/contracts/src/repository/candidates/types.rs @@ -122,6 +122,7 @@ define_candidate_diagnostic_categories!( "invalid_request_error", "kiro_web_search_mcp_unavailable", "local_stream_candidate_watchdog_timeout", + "local_stream_attempt_cancelled", "local_sync_attempt_aborted", "local_sync_attempt_cancelled", "not_found_error", diff --git a/crates/aether-oauth/src/provider/providers/antigravity.rs b/crates/aether-oauth/src/provider/providers/antigravity.rs index b53eb1088..972633092 100644 --- a/crates/aether-oauth/src/provider/providers/antigravity.rs +++ b/crates/aether-oauth/src/provider/providers/antigravity.rs @@ -31,7 +31,6 @@ impl AntigravityProviderOAuthAdapter { /// Supply deterministic OAuth client credentials for tests without /// requiring a process-wide environment variable. Production callers /// continue to resolve the secret from the configured environment. - #[cfg(test)] #[doc(hidden)] pub fn with_oauth_credentials_for_tests( mut self,