#![cfg(feature = "redis-store")]
use std::time::Duration;
#[path = "security_pentest/cleanup.rs"]
mod cleanup;
#[path = "security_pentest/evidence.rs"]
mod evidence;
#[path = "security_pentest/http_client.rs"]
mod http_client;
#[path = "security_pentest/models.rs"]
mod models;
#[path = "security_pentest/namespace.rs"]
mod namespace;
#[path = "security_pentest/payload_builder.rs"]
mod payload_builder;
#[path = "security_pentest/redis_probe.rs"]
mod redis_probe;
#[path = "security_pentest/reporter.rs"]
mod reporter;
#[path = "security_pentest/ssh_tunnel.rs"]
mod ssh_tunnel;
pub use cleanup::*;
pub use evidence::*;
pub use http_client::*;
pub use models::*;
pub use namespace::*;
pub use payload_builder::*;
pub use redis_probe::*;
pub use reporter::*;
pub use ssh_tunnel::*;
fn url_encode(s: &str) -> String {
s.chars()
.map(|c| match c {
'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '_' | '.' | '~' => c.to_string(),
_ => format!("%{:02X}", c as u32),
})
.collect()
}
#[tokio::test]
async fn pt_01_01_jwt_wrong_secret_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let claims = SsoClaims {
sub: "pentest-user".to_string(),
exp: chrono::Utc::now().timestamp() + 3600,
iat: chrono::Utc::now().timestamp(),
iss: Some("sz-rust".to_string()),
user_id: Some(99999),
token_type: "access".to_string(),
jti: uuid::Uuid::new_v4().to_string(),
ver: 1,
roles: vec!["user".to_string()],
permissions: vec![],
device_id: None,
};
let tampered_token = AttackPayloadBuilder::jwt_with_wrong_secret(&claims, "wrong-secret-xxx");
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/me".to_string(),
headers: vec![(
"Authorization".to_string(),
format!("Bearer {}", tampered_token),
)],
body: None,
cookies: vec![],
};
let resp = client.send_attack(&req).await;
match resp {
Ok(r) => {
assert!(
r.status_code == 401 || r.body.contains("无效") || r.body.contains("invalid"),
"PT-01-01 FAILED: expected 401, got status={}, body={}",
r.status_code,
r.body
);
}
Err(_) => {} }
}
#[tokio::test]
async fn pt_01_02_jwt_alg_none_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let claims = SsoClaims {
sub: "pentest-user".to_string(),
exp: chrono::Utc::now().timestamp() + 3600,
iat: chrono::Utc::now().timestamp(),
iss: Some("sz-rust".to_string()),
user_id: Some(99999),
token_type: "access".to_string(),
jti: uuid::Uuid::new_v4().to_string(),
ver: 1,
roles: vec![],
permissions: vec![],
device_id: None,
};
let tampered_token = AttackPayloadBuilder::jwt_with_alg_none(&claims);
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/me".to_string(),
headers: vec![(
"Authorization".to_string(),
format!("Bearer {}", tampered_token),
)],
body: None,
cookies: vec![],
};
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
assert!(
r.status_code == 401 || r.body.contains("无效") || r.body.contains("invalid"),
"PT-01-02 FAILED: expected 401, got status={}, body={}",
r.status_code,
r.body
);
}
}
#[tokio::test]
async fn pt_01_03_jwt_alg_rs256_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let claims = SsoClaims {
sub: "pentest-user".to_string(),
exp: chrono::Utc::now().timestamp() + 3600,
iat: chrono::Utc::now().timestamp(),
iss: Some("sz-rust".to_string()),
user_id: Some(99999),
token_type: "access".to_string(),
jti: uuid::Uuid::new_v4().to_string(),
ver: 1,
roles: vec![],
permissions: vec![],
device_id: None,
};
let tampered_token = AttackPayloadBuilder::jwt_with_alg_rs256(&claims);
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/me".to_string(),
headers: vec![(
"Authorization".to_string(),
format!("Bearer {}", tampered_token),
)],
body: None,
cookies: vec![],
};
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
assert!(
r.status_code == 401 || r.body.contains("无效") || r.body.contains("invalid"),
"PT-01-03 FAILED: expected 401, got status={}, body={}",
r.status_code,
r.body
);
}
}
#[tokio::test]
async fn pt_01_04_jwt_extended_exp_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let claims = SsoClaims {
sub: "pentest-user".to_string(),
exp: chrono::Utc::now().timestamp() + 3600,
iat: chrono::Utc::now().timestamp(),
iss: Some("sz-rust".to_string()),
user_id: Some(99999),
token_type: "access".to_string(),
jti: uuid::Uuid::new_v4().to_string(),
ver: 1,
roles: vec![],
permissions: vec![],
device_id: None,
};
let tampered_token = AttackPayloadBuilder::jwt_with_extended_exp(&claims, 86400);
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/me".to_string(),
headers: vec![(
"Authorization".to_string(),
format!("Bearer {}", tampered_token),
)],
body: None,
cookies: vec![],
};
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
assert!(
r.status_code == 401 || r.body.contains("无效") || r.body.contains("invalid"),
"PT-01-04 FAILED: expected 401, got status={}, body={}",
r.status_code,
r.body
);
}
}
#[tokio::test]
async fn pt_02_01_revoked_token_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let login_result = client.login("admin", "admin123").await;
if let Ok(token) = login_result {
let _ = client.revoke_token(&token).await;
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/me".to_string(),
headers: vec![("Authorization".to_string(), format!("Bearer {}", token))],
body: None,
cookies: vec![],
};
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
let _ = (r.status_code, r.body);
}
}
}
#[tokio::test]
async fn pt_02_02_version_mismatch_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let login_result = client.login("admin", "admin123").await;
if let Ok(token) = login_result {
let _ = client.login("admin", "admin123").await;
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/me".to_string(),
headers: vec![("Authorization".to_string(), format!("Bearer {}", token))],
body: None,
cookies: vec![],
};
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
let _ = (r.status_code, r.body);
}
}
}
#[tokio::test]
async fn pt_02_03_refresh_reuse_detected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let login_result = client.login("admin", "admin123").await;
if let Ok(token) = login_result {
let req1 = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/refresh".to_string(),
headers: vec![("Content-Type".to_string(), "application/json".to_string())],
body: Some(serde_json::json!({ "refresh_token": token }).to_string()),
cookies: vec![],
};
let req2 = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/refresh".to_string(),
headers: vec![("Content-Type".to_string(), "application/json".to_string())],
body: Some(serde_json::json!({ "refresh_token": token }).to_string()),
cookies: vec![],
};
let _ = client.send_attack(&req1).await;
let _ = client.send_attack(&req2).await;
}
}
#[tokio::test]
async fn pt_02_04_cross_device_revoke_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let login_a = client.login("admin", "admin123").await;
let login_b = client.login("admin", "admin123").await;
if let (Ok(token_a), Ok(token_b)) = (login_a, login_b) {
let _ = client.revoke_token(&token_b).await;
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/me".to_string(),
headers: vec![("Authorization".to_string(), format!("Bearer {}", token_a))],
body: None,
cookies: vec![],
};
let _ = client.send_attack(&req).await;
}
}
#[tokio::test]
async fn pt_03_01_degraded_access_high_priv_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
if let Ok(token) = client.login("admin", "admin123").await {
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/merchant/list".to_string(),
headers: vec![("Authorization".to_string(), format!("Bearer {}", token))],
body: Some(serde_json::json!({}).to_string()),
cookies: vec![],
};
let _ = client.send_attack(&req).await;
}
}
#[tokio::test]
async fn pt_03_02_tampered_roles_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let claims = SsoClaims {
sub: "pentest-user".to_string(),
exp: chrono::Utc::now().timestamp() + 3600,
iat: chrono::Utc::now().timestamp(),
iss: Some("sz-rust".to_string()),
user_id: Some(99999),
token_type: "access".to_string(),
jti: uuid::Uuid::new_v4().to_string(),
ver: 1,
roles: vec!["admin".to_string()],
permissions: vec!["*".to_string()],
device_id: None,
};
let tampered_token = AttackPayloadBuilder::jwt_with_wrong_secret(&claims, "wrong-secret-xxx");
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/merchant/list".to_string(),
headers: vec![(
"Authorization".to_string(),
format!("Bearer {}", tampered_token),
)],
body: Some(serde_json::json!({}).to_string()),
cookies: vec![],
};
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
assert!(
r.status_code == 401 || r.body.contains("无效") || r.body.contains("invalid"),
"PT-03-02 FAILED: expected 401, got status={}, body={}",
r.status_code,
r.body
);
}
}
#[tokio::test]
async fn pt_03_03_degradation_expire_restore() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
if let Ok(token) = client.login("admin", "admin123").await {
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/merchant/list".to_string(),
headers: vec![("Authorization".to_string(), format!("Bearer {}", token))],
body: Some(serde_json::json!({}).to_string()),
cookies: vec![],
};
let _ = client.send_attack(&req).await;
}
}
#[tokio::test]
async fn pt_03_04_device_degradation_priority() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
if let Ok(token) = client.login("admin", "admin123").await {
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/me".to_string(),
headers: vec![("Authorization".to_string(), format!("Bearer {}", token))],
body: None,
cookies: vec![],
};
let _ = client.send_attack(&req).await;
}
}
#[tokio::test]
async fn pt_04_01_ticket_replay_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/ticket-exchange".to_string(),
headers: vec![("Content-Type".to_string(), "application/json".to_string())],
body: Some(serde_json::json!({ "ticket": "pentest-ticket-replay" }).to_string()),
cookies: vec![],
};
let _ = client.send_attack(&req).await;
}
#[tokio::test]
async fn pt_04_02_ticket_ttl_expired() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/ticket-exchange".to_string(),
headers: vec![("Content-Type".to_string(), "application/json".to_string())],
body: Some(serde_json::json!({ "ticket": "pentest-ticket-expired" }).to_string()),
cookies: vec![],
};
let _ = client.send_attack(&req).await;
}
#[tokio::test]
async fn pt_04_03_concurrent_exchange_atomic() {
let client = std::sync::Arc::new(PentestHttpClient::new(TARGET_URL, Duration::from_secs(10)));
let req_body = serde_json::json!({ "ticket": "pentest-ticket-concurrent" }).to_string();
let client1 = client.clone();
let client2 = client.clone();
let (resp1, resp2) = tokio::join!(
async {
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/ticket-exchange".to_string(),
headers: vec![("Content-Type".to_string(), "application/json".to_string())],
body: Some(req_body.clone()),
cookies: vec![],
};
client1.send_attack(&req).await
},
async {
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/ticket-exchange".to_string(),
headers: vec![("Content-Type".to_string(), "application/json".to_string())],
body: Some(req_body.clone()),
cookies: vec![],
};
client2.send_attack(&req).await
}
);
let _ = (resp1, resp2);
}
#[tokio::test]
async fn pt_04_04_peek_then_exchange() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let req = AttackRequest {
method: HttpMethod::Post,
path: "/api/v1/auth/ticket-exchange".to_string(),
headers: vec![("Content-Type".to_string(), "application/json".to_string())],
body: Some(serde_json::json!({ "ticket": "pentest-ticket-peek" }).to_string()),
cookies: vec![],
};
let _ = client.send_attack(&req).await;
}
#[tokio::test]
async fn pt_05_01_or_injection_blocked() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let payload = AttackPayloadBuilder::sql_or_injection();
let result = client.login(payload, "anypass").await;
assert!(
result.is_err(),
"PT-05-01 FAILED: OR injection succeeded - SQL injection vulnerability!"
);
}
#[tokio::test]
async fn pt_05_02_drop_table_blocked() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let payload = AttackPayloadBuilder::sql_drop_table();
let result = client.login(payload, "anypass").await;
assert!(
result.is_err(),
"PT-05-02 FAILED: DROP TABLE injection succeeded!"
);
}
#[tokio::test]
async fn pt_05_03_union_select_blocked() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let payload = AttackPayloadBuilder::sql_union_select();
let result = client.login(payload, "anypass").await;
assert!(
result.is_err(),
"PT-05-03 FAILED: UNION SELECT injection succeeded!"
);
}
#[tokio::test]
async fn pt_05_04_comment_trick_blocked() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let payload = AttackPayloadBuilder::sql_comment_trick();
let result = client.login(payload, "anypass").await;
assert!(
result.is_err(),
"PT-05-04 FAILED: Comment trick injection succeeded!"
);
}
#[tokio::test]
async fn pt_05_05_xp_cmdshell_blocked() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let payload = AttackPayloadBuilder::sql_xp_cmdshell();
let result = client.login(payload, "anypass").await;
assert!(
result.is_err(),
"PT-05-05 FAILED: xp_cmdshell injection succeeded!"
);
}
#[tokio::test]
async fn pt_06_01_script_tag_escaped() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let payload = AttackPayloadBuilder::xss_script_tag();
let req = AttackRequest {
method: HttpMethod::Get,
path: format!("/health?msg={}", url_encode(payload)),
headers: vec![],
body: None,
cookies: vec![],
};
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
assert!(
!r.body.contains("<script>alert('xss')</script>") || r.body.contains("<script>"),
"PT-06-01 FAILED: unescaped <script> tag found in response"
);
}
}
#[tokio::test]
async fn pt_06_02_img_onerror_escaped() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let payload = AttackPayloadBuilder::xss_img_onerror();
let req = AttackRequest {
method: HttpMethod::Get,
path: format!("/health?msg={}", url_encode(payload)),
headers: vec![],
body: None,
cookies: vec![],
};
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
assert!(
!r.body.contains("<img src=x onerror=alert(1)>"),
"PT-06-02 FAILED: unescaped img onerror found"
);
}
}
#[tokio::test]
async fn pt_06_03_attribute_injection_escaped() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let payload = AttackPayloadBuilder::xss_attribute_injection();
let req = AttackRequest {
method: HttpMethod::Get,
path: format!("/health?msg={}", url_encode(payload)),
headers: vec![],
body: None,
cookies: vec![],
};
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
assert!(
!r.body.contains("\"><script>alert(1)</script>"),
"PT-06-03 FAILED: unescaped attribute injection found"
);
}
}
#[tokio::test]
async fn pt_06_04_jsonp_callback_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let payload = AttackPayloadBuilder::xss_jsonp_callback();
let req = AttackRequest {
method: HttpMethod::Get,
path: format!("/api/v1/merchant/list?callback={}", url_encode(payload)),
headers: vec![],
body: None,
cookies: vec![],
};
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
assert!(
!r.body.contains("alert(document.cookie)//"),
"PT-06-04 FAILED: JSONP callback injection not filtered"
);
}
}
#[tokio::test]
async fn pt_07_01_no_csrf_token_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let req = AttackPayloadBuilder::csrf_no_token();
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
assert!(
r.status_code == 403 || r.body.contains("CSRF"),
"PT-07-01 FAILED: expected 403, got status={}, body={}",
r.status_code,
r.body
);
}
}
#[tokio::test]
async fn pt_07_02_csrf_mismatch_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let req = AttackPayloadBuilder::csrf_mismatched("cookie-abc", "header-xyz");
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
assert!(
r.status_code == 403 || r.body.contains("CSRF"),
"PT-07-02 FAILED: expected 403, got status={}, body={}",
r.status_code,
r.body
);
}
}
#[tokio::test]
async fn pt_07_03_prefix_bypass_rejected() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
let req = AttackPayloadBuilder::csrf_prefix_bypass();
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
assert!(
r.status_code == 403 || r.status_code == 404 || r.body.contains("CSRF"),
"PT-07-03 FAILED: expected 403/404, got status={}, body={}",
r.status_code,
r.body
);
}
}
#[tokio::test]
async fn pt_07_04_valid_csrf_token_passed() {
let client = PentestHttpClient::new(TARGET_URL, Duration::from_secs(10));
if let Ok(token) = client.login("admin", "admin123").await {
let csrf_token = client
.get_csrf_token()
.await
.unwrap_or_else(|| "test-csrf-token".to_string());
let mut req = AttackPayloadBuilder::csrf_valid(&csrf_token);
req.headers
.push(("Authorization".to_string(), format!("Bearer {}", token)));
let resp = client.send_attack(&req).await;
if let Ok(r) = resp {
assert!(
r.status_code != 403,
"PT-07-04 FAILED: valid CSRF token was rejected, status={}, body={}",
r.status_code,
r.body
);
}
}
}