Усилить безопасность и надёжность выполнения операций
This commit is contained in:
@@ -3,10 +3,11 @@ use std::collections::BTreeMap;
|
||||
use axum::{
|
||||
Json, Router,
|
||||
extract::{Path, Query},
|
||||
http::HeaderMap,
|
||||
http::{HeaderMap, StatusCode},
|
||||
response::Redirect,
|
||||
routing::{get, post},
|
||||
};
|
||||
use crank_adapter_rest::{RestAdapter, RestAdapterError, RestRequest};
|
||||
use crank_adapter_rest::{OutboundHttpPolicy, RestAdapter, RestAdapterError, RestRequest};
|
||||
use crank_core::{HttpMethod, RestTarget};
|
||||
use serde_json::{Value, json};
|
||||
use tokio::net::TcpListener;
|
||||
@@ -14,7 +15,7 @@ use tokio::net::TcpListener;
|
||||
#[tokio::test]
|
||||
async fn executes_rest_request_and_normalizes_json_response() {
|
||||
let base_url = spawn_test_server().await;
|
||||
let adapter = RestAdapter::new();
|
||||
let adapter = test_adapter();
|
||||
let target = RestTarget {
|
||||
base_url,
|
||||
method: HttpMethod::Post,
|
||||
@@ -47,7 +48,7 @@ async fn executes_rest_request_and_normalizes_json_response() {
|
||||
#[tokio::test]
|
||||
async fn returns_unexpected_status_with_normalized_body() {
|
||||
let base_url = spawn_test_server().await;
|
||||
let adapter = RestAdapter::new();
|
||||
let adapter = test_adapter();
|
||||
let target = RestTarget {
|
||||
base_url,
|
||||
method: HttpMethod::Get,
|
||||
@@ -73,10 +74,94 @@ async fn returns_unexpected_status_with_normalized_body() {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_private_targets_by_default() {
|
||||
let policy = OutboundHttpPolicy::default();
|
||||
|
||||
let error = policy
|
||||
.validate_base_url("http://127.0.0.1:8080")
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(error, RestAdapterError::TargetNotAllowed { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accepts_explicit_private_ipv4_and_ipv6_targets() {
|
||||
let policy = OutboundHttpPolicy::allowing_hosts(["192.168.1.10", "::1"]);
|
||||
|
||||
assert!(policy.validate_base_url("http://192.168.1.10:8080").is_ok());
|
||||
assert!(policy.validate_base_url("http://[::1]:8080").is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn does_not_follow_redirects() {
|
||||
let base_url = spawn_test_server().await;
|
||||
let adapter = test_adapter();
|
||||
let target = RestTarget {
|
||||
base_url,
|
||||
method: HttpMethod::Get,
|
||||
path_template: "/redirect".to_owned(),
|
||||
static_headers: BTreeMap::new(),
|
||||
};
|
||||
|
||||
let error = adapter
|
||||
.execute(&target, &empty_request())
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(
|
||||
error,
|
||||
RestAdapterError::UnexpectedStatus { status: 303, .. }
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rejects_responses_over_the_configured_limit() {
|
||||
let base_url = spawn_test_server().await;
|
||||
let adapter = RestAdapter::with_policy(
|
||||
OutboundHttpPolicy::allowing_hosts(["127.0.0.1"]).with_max_response_bytes(8),
|
||||
);
|
||||
let target = RestTarget {
|
||||
base_url,
|
||||
method: HttpMethod::Get,
|
||||
path_template: "/large".to_owned(),
|
||||
static_headers: BTreeMap::new(),
|
||||
};
|
||||
|
||||
let error = adapter
|
||||
.execute(&target, &empty_request())
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(
|
||||
error,
|
||||
RestAdapterError::ResponseTooLarge { limit_bytes: 8 }
|
||||
));
|
||||
}
|
||||
|
||||
fn empty_request() -> RestRequest {
|
||||
RestRequest {
|
||||
path_params: BTreeMap::new(),
|
||||
query_params: BTreeMap::new(),
|
||||
headers: BTreeMap::new(),
|
||||
body: None,
|
||||
timeout_ms: 1_000,
|
||||
}
|
||||
}
|
||||
|
||||
fn test_adapter() -> RestAdapter {
|
||||
RestAdapter::with_policy(OutboundHttpPolicy::allowing_hosts(["127.0.0.1"]))
|
||||
}
|
||||
|
||||
async fn spawn_test_server() -> String {
|
||||
let app = Router::new()
|
||||
.route("/users/{user_id}", post(create_user))
|
||||
.route("/fail", get(fail));
|
||||
.route("/fail", get(fail))
|
||||
.route("/redirect", get(|| async { Redirect::to("/large") }))
|
||||
.route(
|
||||
"/large",
|
||||
get(|| async { "response larger than eight bytes" }),
|
||||
);
|
||||
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let address = listener.local_addr().unwrap();
|
||||
|
||||
@@ -113,7 +198,7 @@ async fn create_user(
|
||||
|
||||
async fn fail() -> (axum::http::StatusCode, Json<Value>) {
|
||||
(
|
||||
axum::http::StatusCode::BAD_GATEWAY,
|
||||
StatusCode::BAD_GATEWAY,
|
||||
Json(json!({ "error": "upstream failed" })),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user