super-tool 0.14.0

The Super CLI for verifying software behavior
use super::*;

#[test]
fn callback_requires_the_exact_host_state_path_and_unambiguous_parameters() {
    let request = |target: &str, host: &str| format!("GET {target} HTTP/1.1\r\nHost: {host}\r\n\r\n");
    let host = "127.0.0.1:12345";
    assert_eq!(
        callback_code(&request("/callback?state=expected&code=abc", host), host, "expected").unwrap(),
        Some("abc".into())
    );
    assert_eq!(
        callback_code(
            &request("/callback?state=expected&error=access_denied", host),
            host,
            "expected"
        )
        .unwrap(),
        None
    );
    for target in [
        "/favicon.ico",
        "/callback?state=wrong&code=abc",
        "/callback?code=abc",
        "/callback?state=expected&state=expected&code=abc",
        "/callback?state=expected&code=a&code=b",
        "/callback?state=expected&code=a&error=access_denied",
        "/callback?state=expected&code=",
        "/callback?state=expected&code=%0Asecret",
        "/callback?state=expected&error=unknown",
    ] {
        assert!(
            callback_code(&request(target, host), host, "expected").is_err(),
            "{target}"
        );
    }
    assert!(
        callback_code(
            &request("/callback?state=expected&code=abc", "attacker.test"),
            host,
            "expected"
        )
        .is_err()
    );
    assert!(
        callback_code(
            "GET /callback?state=expected&code=abc HTTP/1.1\r\nHost: 127.0.0.1:12345\r\nHost: attacker.test\r\n\r\n",
            host,
            "expected"
        )
        .is_err()
    );
}

#[tokio::test]
async fn invalid_callback_does_not_consume_the_waiting_login() {
    let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
    let address = listener.local_addr().unwrap();
    let task = tokio::spawn(async move {
        receive_callback(
            &listener,
            "expected",
            &Url::parse("https://platform.example.test/cli/result/").unwrap(),
        )
        .await
        .unwrap()
    });
    let mut socket = TcpStream::connect(address).await.unwrap();
    socket
        .write_all(format!("GET /callback?state=wrong&code=abc HTTP/1.1\r\nHost: {address}\r\n\r\n").as_bytes())
        .await
        .unwrap();
    let mut body = String::new();
    socket.read_to_string(&mut body).await.unwrap();
    assert!(body.starts_with("HTTP/1.1 303"));
    assert!(body.contains("Location: https://platform.example.test/cli/result/?outcome=invalid\r\n"));
    assert!(!body.contains("state=") && !body.contains("code="));
    let mut socket = TcpStream::connect(address).await.unwrap();
    socket
        .write_all(format!("GET /callback?state=expected&code=abc HTTP/1.1\r\nHost: {address}\r\n\r\n").as_bytes())
        .await
        .unwrap();
    let (code, _) = task.await.unwrap();
    assert_eq!(code, "abc");
}

#[test]
fn pkce_matches_the_rfc7636_test_vector() {
    let verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
    assert_eq!(
        URL_SAFE_NO_PAD.encode(Sha256::digest(verifier.as_bytes())),
        "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
    );
    assert_eq!(random_secret().unwrap().len(), 43);
}