mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-09 18:59:50 +08:00
feat(security): harden gateway boundaries and usage policies
Consolidate subscription usage policy enforcement, privacy-safe persistence, and gateway security hardening into one reviewable change. Includes bounded HTTP and execution envelopes, header and protocol guards, DNS and relay validation, authentication and secret projection hardening, secure backup/install paths, and regression coverage.
This commit is contained in:
@@ -587,7 +587,7 @@ pub(crate) async fn resolve_local_standard_candidate_payload_parts(
|
||||
}
|
||||
};
|
||||
crate::ai_serving::hydrate_openai_response_history(
|
||||
state.runtime_state(),
|
||||
state,
|
||||
body_json,
|
||||
spec_metadata.api_format,
|
||||
provider_api_format,
|
||||
|
||||
+12
-27
@@ -1,7 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use aether_contracts::RequestBody;
|
||||
use tracing::debug;
|
||||
|
||||
use super::super::{
|
||||
augment_sync_report_context, build_ai_execution_plan_from_decision,
|
||||
@@ -10,7 +9,6 @@ use super::super::{
|
||||
AiStreamAttempt,
|
||||
};
|
||||
use crate::ai_serving::planner::common::enforce_provider_body_stream_policy;
|
||||
use crate::ai_serving::planner::redaction::sanitize_upstream_url_for_log;
|
||||
use crate::ai_serving::provider_adaptation_requires_eventstream_accept;
|
||||
use crate::ai_serving::transport::{
|
||||
build_standard_plan_fallback_headers, build_standard_plan_fallback_openai_chat_url,
|
||||
@@ -157,21 +155,16 @@ pub(crate) fn build_openai_responses_stream_plan_from_decision(
|
||||
let Some(auth_pair) = take_ai_upstream_auth_pair(&mut payload) else {
|
||||
return Ok(None);
|
||||
};
|
||||
let (url, url_source) = if let Some(upstream_url) =
|
||||
take_non_empty_string(&mut payload.upstream_url)
|
||||
{
|
||||
(upstream_url, "upstream_url")
|
||||
let url = if let Some(upstream_url) = take_non_empty_string(&mut payload.upstream_url) {
|
||||
upstream_url
|
||||
} else {
|
||||
let Some(upstream_base_url) = take_non_empty_string(&mut payload.upstream_base_url) else {
|
||||
return Ok(None);
|
||||
};
|
||||
(
|
||||
build_standard_plan_fallback_openai_responses_url(
|
||||
&upstream_base_url,
|
||||
parts.uri.query(),
|
||||
compact,
|
||||
),
|
||||
"upstream_base_url",
|
||||
build_standard_plan_fallback_openai_responses_url(
|
||||
&upstream_base_url,
|
||||
parts.uri.query(),
|
||||
compact,
|
||||
)
|
||||
};
|
||||
let Some(provider_request_body_value) = payload.provider_request_body.take() else {
|
||||
@@ -238,16 +231,7 @@ pub(crate) fn build_openai_responses_stream_plan_from_decision(
|
||||
.uri
|
||||
.query()
|
||||
.and_then(crate::ai_serving::api::sanitize_request_query_string);
|
||||
let log_decision_upstream_base_url = payload
|
||||
.upstream_base_url
|
||||
.as_deref()
|
||||
.map(sanitize_upstream_url_for_log);
|
||||
let log_decision_upstream_url = payload
|
||||
.upstream_url
|
||||
.as_deref()
|
||||
.map(sanitize_upstream_url_for_log);
|
||||
let log_plan_url = sanitize_upstream_url_for_log(plan.url.as_str());
|
||||
debug!(
|
||||
tracing::debug!(
|
||||
event_name = "local_openai_responses_stream_plan_built",
|
||||
log_type = "debug",
|
||||
request_id = %plan.request_id,
|
||||
@@ -255,12 +239,13 @@ pub(crate) fn build_openai_responses_stream_plan_from_decision(
|
||||
provider_id = %plan.provider_id,
|
||||
endpoint_id = %plan.endpoint_id,
|
||||
key_id = %plan.key_id,
|
||||
downstream_path_and_query = %crate::ai_serving::pure::sanitize_request_path_and_query(
|
||||
parts.uri.path(),
|
||||
parts.uri.query(),
|
||||
).unwrap_or_else(|| "/".to_string()),
|
||||
upstream_origin = %crate::handlers::shared::security_log_url_origin(&plan.url),
|
||||
downstream_path = %parts.uri.path(),
|
||||
downstream_query = ?log_downstream_query,
|
||||
url_source,
|
||||
decision_upstream_base_url = ?log_decision_upstream_base_url,
|
||||
decision_upstream_url = ?log_decision_upstream_url,
|
||||
plan_url = %log_plan_url,
|
||||
client_api_format = %plan.client_api_format,
|
||||
provider_api_format = %plan.provider_api_format,
|
||||
upstream_is_stream = effective_upstream_is_stream,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use aether_contracts::RequestBody;
|
||||
use tracing::debug;
|
||||
|
||||
use super::super::{
|
||||
augment_sync_report_context, build_ai_execution_plan_from_decision,
|
||||
@@ -10,7 +9,6 @@ use super::super::{
|
||||
AiSyncAttempt,
|
||||
};
|
||||
use crate::ai_serving::planner::common::enforce_provider_body_stream_policy;
|
||||
use crate::ai_serving::planner::redaction::sanitize_upstream_url_for_log;
|
||||
use crate::ai_serving::transport::{
|
||||
build_standard_plan_fallback_headers, build_standard_plan_fallback_openai_chat_url,
|
||||
build_standard_plan_fallback_openai_responses_url, StandardPlanFallbackAcceptPolicy,
|
||||
@@ -142,21 +140,16 @@ pub(crate) fn build_openai_responses_sync_plan_from_decision(
|
||||
let Some(auth_pair) = take_ai_upstream_auth_pair(&mut payload) else {
|
||||
return Ok(None);
|
||||
};
|
||||
let (url, url_source) = if let Some(upstream_url) =
|
||||
take_non_empty_string(&mut payload.upstream_url)
|
||||
{
|
||||
(upstream_url, "upstream_url")
|
||||
let url = if let Some(upstream_url) = take_non_empty_string(&mut payload.upstream_url) {
|
||||
upstream_url
|
||||
} else {
|
||||
let Some(upstream_base_url) = take_non_empty_string(&mut payload.upstream_base_url) else {
|
||||
return Ok(None);
|
||||
};
|
||||
(
|
||||
build_standard_plan_fallback_openai_responses_url(
|
||||
&upstream_base_url,
|
||||
parts.uri.query(),
|
||||
compact,
|
||||
),
|
||||
"upstream_base_url",
|
||||
build_standard_plan_fallback_openai_responses_url(
|
||||
&upstream_base_url,
|
||||
parts.uri.query(),
|
||||
compact,
|
||||
)
|
||||
};
|
||||
let Some(provider_request_body_value) = payload.provider_request_body.take() else {
|
||||
@@ -205,16 +198,7 @@ pub(crate) fn build_openai_responses_sync_plan_from_decision(
|
||||
.uri
|
||||
.query()
|
||||
.and_then(crate::ai_serving::api::sanitize_request_query_string);
|
||||
let log_decision_upstream_base_url = payload
|
||||
.upstream_base_url
|
||||
.as_deref()
|
||||
.map(sanitize_upstream_url_for_log);
|
||||
let log_decision_upstream_url = payload
|
||||
.upstream_url
|
||||
.as_deref()
|
||||
.map(sanitize_upstream_url_for_log);
|
||||
let log_plan_url = sanitize_upstream_url_for_log(plan.url.as_str());
|
||||
debug!(
|
||||
tracing::debug!(
|
||||
event_name = "local_openai_responses_sync_plan_built",
|
||||
log_type = "debug",
|
||||
request_id = %plan.request_id,
|
||||
@@ -222,12 +206,13 @@ pub(crate) fn build_openai_responses_sync_plan_from_decision(
|
||||
provider_id = %plan.provider_id,
|
||||
endpoint_id = %plan.endpoint_id,
|
||||
key_id = %plan.key_id,
|
||||
downstream_path_and_query = %crate::ai_serving::pure::sanitize_request_path_and_query(
|
||||
parts.uri.path(),
|
||||
parts.uri.query(),
|
||||
).unwrap_or_else(|| "/".to_string()),
|
||||
upstream_origin = %crate::handlers::shared::security_log_url_origin(&plan.url),
|
||||
downstream_path = %parts.uri.path(),
|
||||
downstream_query = ?log_downstream_query,
|
||||
url_source,
|
||||
decision_upstream_base_url = ?log_decision_upstream_base_url,
|
||||
decision_upstream_url = ?log_decision_upstream_url,
|
||||
plan_url = %log_plan_url,
|
||||
client_api_format = %plan.client_api_format,
|
||||
provider_api_format = %plan.provider_api_format,
|
||||
upstream_is_stream = payload.upstream_is_stream,
|
||||
|
||||
+5
-5
@@ -3,7 +3,6 @@ use tracing::debug;
|
||||
|
||||
use crate::ai_serving::build_request_trace_proxy_value;
|
||||
use crate::ai_serving::planner::decision_input::apply_provider_request_routing_policy_to_decision_with_websocket_mode;
|
||||
use crate::ai_serving::planner::redaction::sanitize_upstream_url_for_log;
|
||||
use crate::ai_serving::planner::report_context::{
|
||||
build_local_execution_report_context, insert_native_client_envelope_name,
|
||||
insert_provider_stream_event_api_format, LocalExecutionReportContextParts,
|
||||
@@ -205,12 +204,10 @@ pub(crate) async fn maybe_build_local_openai_responses_decision_payload_for_cand
|
||||
&resolved.transport,
|
||||
);
|
||||
|
||||
let log_base_url = sanitize_upstream_url_for_log(resolved.transport.endpoint.base_url.as_str());
|
||||
let log_request_query = parts
|
||||
.uri
|
||||
.query()
|
||||
.and_then(crate::ai_serving::api::sanitize_request_query_string);
|
||||
let log_upstream_url = sanitize_upstream_url_for_log(resolved.upstream_url.as_str());
|
||||
debug!(
|
||||
event_name = "local_openai_responses_decision_payload_built",
|
||||
log_type = "debug",
|
||||
@@ -227,9 +224,12 @@ pub(crate) async fn maybe_build_local_openai_responses_decision_payload_for_cand
|
||||
client_api_format = spec_metadata.api_format,
|
||||
provider_api_format = %resolved.provider_api_format,
|
||||
request_path = %parts.uri.path(),
|
||||
request_path_and_query = %crate::ai_serving::pure::sanitize_request_path_and_query(
|
||||
parts.uri.path(),
|
||||
parts.uri.query(),
|
||||
).unwrap_or_else(|| "/".to_string()),
|
||||
upstream_origin = %crate::handlers::shared::security_log_url_origin(&resolved.upstream_url),
|
||||
request_query = ?log_request_query,
|
||||
upstream_base_url = %log_base_url,
|
||||
upstream_url = %log_upstream_url,
|
||||
upstream_is_stream = resolved.upstream_is_stream,
|
||||
has_envelope = resolved.envelope_name.is_some(),
|
||||
"gateway built local openai responses decision payload"
|
||||
|
||||
+7
-15
@@ -24,7 +24,6 @@ use crate::ai_serving::planner::gemini_cli::{
|
||||
};
|
||||
use crate::ai_serving::planner::redaction::{
|
||||
request_identity_response_encoding_when_redacted, resolve_provider_chat_pii_redaction,
|
||||
sanitize_upstream_url_for_log,
|
||||
};
|
||||
use crate::ai_serving::planner::spec_metadata::local_openai_responses_spec_metadata;
|
||||
use crate::ai_serving::planner::standard::{
|
||||
@@ -428,7 +427,7 @@ pub(crate) async fn resolve_local_openai_responses_candidate_payload_parts_with_
|
||||
}
|
||||
};
|
||||
crate::ai_serving::hydrate_openai_response_history(
|
||||
state.runtime_state(),
|
||||
state,
|
||||
body_json,
|
||||
spec_metadata.api_format,
|
||||
provider_api_format,
|
||||
@@ -867,17 +866,10 @@ pub(crate) async fn resolve_local_openai_responses_candidate_payload_parts_with_
|
||||
|
||||
let (execution_strategy, conversion_mode) =
|
||||
ai_local_execution_contract_for_formats(spec_metadata.api_format, provider_api_format);
|
||||
let log_base_url = sanitize_upstream_url_for_log(transport.endpoint.base_url.as_str());
|
||||
let log_custom_path = transport
|
||||
.endpoint
|
||||
.custom_path
|
||||
.as_deref()
|
||||
.map(sanitize_upstream_url_for_log);
|
||||
let log_request_query = parts
|
||||
.uri
|
||||
.query()
|
||||
.and_then(crate::ai_serving::api::sanitize_request_query_string);
|
||||
let log_upstream_url = sanitize_upstream_url_for_log(upstream_url.as_str());
|
||||
|
||||
debug!(
|
||||
event_name = "local_openai_responses_upstream_url_resolved",
|
||||
@@ -893,12 +885,14 @@ pub(crate) async fn resolve_local_openai_responses_candidate_payload_parts_with_
|
||||
provider_api_format = %provider_api_format,
|
||||
execution_strategy = execution_strategy.as_str(),
|
||||
conversion_mode = conversion_mode.as_str(),
|
||||
base_url = %log_base_url,
|
||||
custom_path = ?log_custom_path,
|
||||
request_path_and_query = %crate::ai_serving::pure::sanitize_request_path_and_query(
|
||||
parts.uri.path(),
|
||||
parts.uri.query(),
|
||||
).unwrap_or_else(|| "/".to_string()),
|
||||
upstream_origin = %crate::handlers::shared::security_log_url_origin(&upstream_url),
|
||||
request_path = %parts.uri.path(),
|
||||
request_query = ?log_request_query,
|
||||
mapped_model = %mapped_model,
|
||||
upstream_url = %log_upstream_url,
|
||||
upstream_is_stream,
|
||||
"gateway resolved local openai responses upstream url"
|
||||
);
|
||||
@@ -2011,8 +2005,6 @@ async fn build_kiro_openai_responses_payload_parts(
|
||||
};
|
||||
let (execution_strategy, conversion_mode) =
|
||||
ai_local_execution_contract_for_formats(client_api_format, provider_api_format);
|
||||
let log_upstream_url = sanitize_upstream_url_for_log(upstream_url.as_str());
|
||||
|
||||
debug!(
|
||||
event_name = "local_openai_responses_kiro_upstream_url_resolved",
|
||||
log_type = "debug",
|
||||
@@ -2027,7 +2019,7 @@ async fn build_kiro_openai_responses_payload_parts(
|
||||
provider_api_format = %provider_api_format,
|
||||
execution_strategy = execution_strategy.as_str(),
|
||||
conversion_mode = conversion_mode.as_str(),
|
||||
upstream_url = %log_upstream_url,
|
||||
upstream_origin = %crate::handlers::shared::security_log_url_origin(&upstream_url),
|
||||
upstream_is_stream,
|
||||
"gateway resolved local openai responses kiro upstream url"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user