mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-09 02:47:45 +08:00
fix: restore security hardening compatibility and validation
Restore authorized rule reveal, explicit full HTTP capture and retention, video task business fields, and valid payment URLs. Add opt-in credential preservation for trusted recovery, fix frontend type contracts and async races, and eliminate PostgreSQL test fixture resource leaks. Document audit coverage and successful fmt and CI-scoped Clippy checks.
This commit is contained in:
@@ -81,12 +81,12 @@ fn select_video_task_full_columns() -> String {
|
||||
|
||||
fn select_video_task_claim_columns() -> String {
|
||||
select_video_task_columns(
|
||||
"NULL::TEXT",
|
||||
"prompt",
|
||||
"NULL::jsonb",
|
||||
"NULL::INTEGER",
|
||||
"NULL::TEXT",
|
||||
"NULL::TEXT",
|
||||
"NULL::TEXT",
|
||||
"duration_seconds",
|
||||
"resolution",
|
||||
"aspect_ratio",
|
||||
"size",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1176,7 +1176,7 @@ fn map_video_task_row(row: &PgRow) -> Result<StoredVideoTask, DataLayerError> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{update_if_active_sql, upsert_sql, SqlxVideoTaskRepository};
|
||||
use super::{claim_due_sql, update_if_active_sql, upsert_sql, SqlxVideoTaskRepository};
|
||||
use crate::{PostgresPoolConfig, PostgresPoolFactory};
|
||||
use aether_data_contracts::repository::video_tasks::{
|
||||
UpsertVideoTask, VideoTaskLookupKey, VideoTaskQueryFilter, VideoTaskReadRepository,
|
||||
@@ -1240,6 +1240,142 @@ mod tests {
|
||||
assert!(update.contains("created_at = COALESCE(created_at, TO_TIMESTAMP($34))"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn poll_claim_returns_business_fields_required_by_identity_guards() {
|
||||
let sql = claim_due_sql();
|
||||
for field in [
|
||||
"prompt",
|
||||
"duration_seconds",
|
||||
"resolution",
|
||||
"aspect_ratio",
|
||||
"size",
|
||||
] {
|
||||
assert!(
|
||||
sql.contains(&format!("{field} AS {field}")),
|
||||
"claim must retain {field}"
|
||||
);
|
||||
}
|
||||
assert!(sql.contains("NULL::jsonb AS original_request_body"));
|
||||
assert!(sql.contains("FOR UPDATE SKIP LOCKED"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore = "requires AETHER_TEST_DATABASE_URL and PostgreSQL migrations"]
|
||||
async fn live_video_task_capture_claim_and_completion_preserve_business_fields() {
|
||||
let database_url = std::env::var("AETHER_TEST_DATABASE_URL")
|
||||
.expect("AETHER_TEST_DATABASE_URL must point at the test database");
|
||||
let pool = sqlx::postgres::PgPoolOptions::new()
|
||||
.max_connections(1)
|
||||
.connect(&database_url)
|
||||
.await
|
||||
.expect("test database should connect");
|
||||
crate::run_migrations(&pool)
|
||||
.await
|
||||
.expect("test database should migrate");
|
||||
sqlx::query("CREATE TEMP TABLE video_tasks (LIKE public.video_tasks INCLUDING ALL)")
|
||||
.execute(&pool)
|
||||
.await
|
||||
.expect("isolated task table should be created");
|
||||
let repository = SqlxVideoTaskRepository::new(pool);
|
||||
for api_format in ["openai:video", "gemini:video"] {
|
||||
let task_id = uuid::Uuid::new_v4().to_string();
|
||||
let original = UpsertVideoTask {
|
||||
id: task_id.clone(),
|
||||
short_id: Some(uuid::Uuid::new_v4().simple().to_string()[..16].to_string()),
|
||||
request_id: format!("request-{task_id}"),
|
||||
user_id: None,
|
||||
api_key_id: None,
|
||||
username: Some("alice".to_string()),
|
||||
api_key_name: Some("video-client".to_string()),
|
||||
external_task_id: Some("upstream-task-1".to_string()),
|
||||
provider_id: None,
|
||||
endpoint_id: None,
|
||||
key_id: None,
|
||||
client_api_format: Some(api_format.to_string()),
|
||||
provider_api_format: Some(api_format.to_string()),
|
||||
format_converted: false,
|
||||
model: Some("video-model".to_string()),
|
||||
prompt: Some("business prompt".to_string()),
|
||||
original_request_body: Some(serde_json::json!({"token": "private"})),
|
||||
duration_seconds: Some(8),
|
||||
resolution: Some("1080p".to_string()),
|
||||
aspect_ratio: Some("16:9".to_string()),
|
||||
size: Some("1920x1080".to_string()),
|
||||
status: VideoTaskStatus::Submitted,
|
||||
progress_percent: 0,
|
||||
progress_message: None,
|
||||
retry_count: 0,
|
||||
poll_interval_seconds: 10,
|
||||
next_poll_at_unix_secs: Some(10),
|
||||
poll_count: 0,
|
||||
max_poll_count: 360,
|
||||
created_at_unix_ms: 1,
|
||||
submitted_at_unix_secs: Some(1),
|
||||
completed_at_unix_secs: None,
|
||||
updated_at_unix_secs: 1,
|
||||
error_code: None,
|
||||
error_message: None,
|
||||
video_url: None,
|
||||
request_metadata: Some(serde_json::json!({"authorization": "private"})),
|
||||
};
|
||||
let stored = repository
|
||||
.upsert(original.clone())
|
||||
.await
|
||||
.expect("task should persist");
|
||||
assert_eq!(stored.prompt, original.prompt);
|
||||
assert_eq!(stored.username, original.username);
|
||||
assert_eq!(stored.api_key_name, original.api_key_name);
|
||||
assert!(stored.original_request_body.is_none());
|
||||
assert!(stored.request_metadata.is_none());
|
||||
|
||||
let mut claimed = repository
|
||||
.claim_due(20, 50, 10)
|
||||
.await
|
||||
.expect("task should be claimed");
|
||||
assert_eq!(claimed.len(), 1);
|
||||
let mut completion: UpsertVideoTask = claimed.pop().expect("claimed task").into();
|
||||
stored
|
||||
.ensure_immutable_identity_matches(&completion)
|
||||
.expect("claim must preserve task identity");
|
||||
assert_eq!(completion.prompt, original.prompt);
|
||||
let mut mismatched = completion.clone();
|
||||
mismatched.duration_seconds = Some(99);
|
||||
assert!(repository
|
||||
.update_if_active(mismatched)
|
||||
.await
|
||||
.expect("guarded update should execute")
|
||||
.is_none());
|
||||
completion.status = VideoTaskStatus::Completed;
|
||||
completion.progress_percent = 100;
|
||||
completion.next_poll_at_unix_secs = None;
|
||||
completion.completed_at_unix_secs = Some(21);
|
||||
completion.updated_at_unix_secs = 21;
|
||||
completion.video_url = Some(
|
||||
"https://cdn.example.test/video.mp4?alt=media&signature=a%2Fb%2Bc%3D&part=2&part=1"
|
||||
.to_string(),
|
||||
);
|
||||
let completed = repository
|
||||
.update_if_active(completion.clone())
|
||||
.await
|
||||
.expect("completion should execute")
|
||||
.expect("matching active task should complete");
|
||||
assert_eq!(completed.video_url, completion.video_url);
|
||||
let reloaded = repository
|
||||
.find(VideoTaskLookupKey::Id(&task_id))
|
||||
.await
|
||||
.expect("task should reload")
|
||||
.expect("task should exist");
|
||||
assert_eq!(reloaded.status, VideoTaskStatus::Completed);
|
||||
assert_eq!(reloaded.prompt, original.prompt);
|
||||
assert_eq!(reloaded.video_url, completion.video_url);
|
||||
assert_eq!(reloaded.duration_seconds, original.duration_seconds);
|
||||
assert_eq!(reloaded.size, original.size);
|
||||
assert_eq!(reloaded.username, original.username);
|
||||
assert!(reloaded.request_metadata.is_none());
|
||||
}
|
||||
repository.pool().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn repository_constructs_from_lazy_pool() {
|
||||
let repository = SqlxVideoTaskRepository::new(build_pool());
|
||||
|
||||
Reference in New Issue
Block a user