#![cfg(feature = "owasp-pentest-suite")]
use sz_orm_auth::jwt::{JwtClaims, JwtEncoder};
use sz_orm_auth::{
AuthError, AuthorizationRequest, JwtAuthenticator, OAuth2Server, TokenRequest, TokenStore,
TotpVerifier,
};
#[test]
fn a09_session_fixation_replay_detected() {
let store = TokenStore::new();
store.issue_family("rt_initial", 42).unwrap();
let result1 = store.refresh("rt_initial", "rt_v2").unwrap();
assert_eq!(result1.user_id, 42);
let result2 = store.refresh("rt_initial", "rt_v3");
assert!(
matches!(
result2,
Err(sz_orm_auth::TokenFamilyError::ReplayDetected(_))
),
"旧刷新令牌重放应被检测并返回 ReplayDetected"
);
assert!(!store.is_valid("rt_v2"), "重放后整个家族应被撤销");
assert!(!store.is_valid("rt_initial"));
}
#[test]
fn a09_session_timeout_expired_jwt_rejected() {
let encoder = JwtEncoder::new("test-secret-with-sufficient-length-32bytes!");
let past_exp = 1000;
let claims = JwtClaims::new("user1", past_exp);
let token = encoder.encode(&claims).unwrap();
let result = encoder.decode(&token);
assert!(
matches!(result, Err(AuthError::TokenExpired(_))),
"过期 JWT 应返回 TokenExpired,实际: {:?}",
result
);
}
#[test]
fn a09_concurrent_session_revocation() {
let store = TokenStore::new();
store.issue_family("rt_session_1", 99).unwrap();
store.issue_family("rt_session_2", 99).unwrap();
store.issue_family("rt_session_3", 99).unwrap();
assert!(store.is_valid("rt_session_1"));
assert!(store.is_valid("rt_session_2"));
assert!(store.is_valid("rt_session_3"));
let revoked_count = store.revoke_user(99);
assert_eq!(revoked_count, 3, "应撤销用户 99 的全部 3 个会话");
assert!(!store.is_valid("rt_session_1"));
assert!(!store.is_valid("rt_session_2"));
assert!(!store.is_valid("rt_session_3"));
}
#[test]
fn a09_weak_secret_rejected() {
let weak_secret = "short";
let result = JwtAuthenticator::try_new(weak_secret, "issuer", 3600);
assert!(
matches!(result, Err(AuthError::SecretTooShort(_))),
"短密钥应被拒绝"
);
let strong_secret = "this-is-a-very-strong-secret-32+bytes!!";
assert!(strong_secret.len() >= 32);
let result2 = JwtAuthenticator::try_new(strong_secret, "issuer", 3600);
assert!(result2.is_ok(), "≥32 字节密钥应被接受");
}
#[test]
fn a09_jwt_alg_none_bypass_blocked() {
let encoder = JwtEncoder::new("test-secret-with-sufficient-length-32bytes!");
let alg_none_token = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbiIsImV4cCI6OTk5OTk5OTk5OSwiaWF0IjoxMDAwfQ.";
let result = encoder.decode(alg_none_token);
assert!(
result.is_err(),
"alg=none 伪造 token 应被拒绝,实际: {:?}",
result
);
}
#[test]
fn a09_oauth2_authorization_code_replay_blocked() {
let mut clients = std::collections::HashMap::new();
clients.insert("client1".to_string(), "secret1".to_string());
let server = OAuth2Server::new(clients);
let req = AuthorizationRequest::new("client1", "https://app.com/cb", "read", "state123");
let code = server.create_authorization_code(&req, 1).unwrap();
let token_req = TokenRequest::new(&code.code, "https://app.com/cb", "client1");
let result1 = server.exchange_code(&token_req);
assert!(result1.is_ok(), "首次兑换应成功");
let result2 = server.exchange_code(&token_req);
assert!(
result2.is_err(),
"授权码重放应被拒绝(一次性消费),实际: {:?}",
result2
);
}
#[test]
fn a09_mfa_empty_secret_bypass_blocked() {
let verifier = TotpVerifier::new();
let result = verifier.verify_at("", "000000", 1700000000);
assert!(!result, "空 base32 secret 的 TOTP 验证应返回 false");
let result2 = verifier.verify_at("", "123456", 1700000000);
assert!(!result2, "空 secret 对任意码都应返回 false");
}
#[test]
fn a09_oauth2_redirect_uri_and_pkce_enforced() {
let mut clients = std::collections::HashMap::new();
clients.insert("client1".to_string(), "secret1".to_string());
let server = OAuth2Server::new(clients);
let challenge = "E9Melhoa2OwvFrEMTJguCHaoinK1rN6Mi5Y1QQJ0m2_3Q";
let req = AuthorizationRequest::new("client1", "https://app.com/cb", "read", "state")
.with_pkce(challenge, "S256");
let code = server.create_authorization_code(&req, 1).unwrap();
let wrong_redirect = TokenRequest::new(&code.code, "https://evil.com/cb", "client1");
assert!(
server.exchange_code(&wrong_redirect).is_err(),
"redirect_uri 不匹配应被拒绝"
);
let req2 = AuthorizationRequest::new("client1", "https://app.com/cb", "read", "state2")
.with_pkce(challenge, "S256");
let code2 = server.create_authorization_code(&req2, 2).unwrap();
let no_verifier = TokenRequest::new(&code2.code, "https://app.com/cb", "client1");
assert!(
server.exchange_code(&no_verifier).is_err(),
"缺少 PKCE verifier 应被拒绝"
);
let wrong_verifier = TokenRequest::new(&code2.code, "https://app.com/cb", "client1")
.with_code_verifier("attacker-guessed-verifier");
assert!(
server.exchange_code(&wrong_verifier).is_err(),
"错误 PKCE verifier 应被拒绝"
);
}