#![forbid(unsafe_code)]
use std::path::PathBuf;
fn root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
fn read(rel: &str) -> String {
std::fs::read_to_string(root().join(rel)).unwrap_or_else(|e| panic!("read {rel}: {e}"))
}
#[test]
fn lockfile_forbids_openssl_and_native_tls() {
let lock = read("Cargo.lock");
for bad in [
"name = \"native-tls\"",
"name = \"openssl\"",
"name = \"openssl-sys\"",
"name = \"libssh2-sys\"",
] {
assert!(
!lock.contains(bad),
"G-TLS-03: forbidden crate present in Cargo.lock: {bad}"
);
}
}
#[test]
fn lockfile_has_rustls_floor() {
let lock = read("Cargo.lock");
assert!(
lock.contains("name = \"rustls\""),
"G-TLS: rustls must be in Cargo.lock when feature tls is default"
);
let mut found = false;
let mut lines = lock.lines();
while let Some(line) = lines.next() {
if line.trim() == "name = \"rustls\"" {
if let Some(ver) = lines.next() {
let v = ver
.split('"')
.nth(1)
.expect("version string");
let parts: Vec<_> = v.split('.').collect();
assert!(parts.len() >= 3, "unexpected rustls version {v}");
let major: u32 = parts[0].parse().unwrap();
let minor: u32 = parts[1].parse().unwrap();
let patch: u32 = parts[2]
.split(|c: char| !c.is_ascii_digit())
.next()
.unwrap()
.parse()
.unwrap();
assert!(
major > 0 || minor > 23 || (minor == 23 && patch >= 18),
"rustls {v} below floor 0.23.18"
);
if major == 0 && minor == 23 {
assert!(
!(13..=17).contains(&patch),
"rustls {v} in CVE Acceptor window 0.23.13–0.23.17"
);
}
found = true;
break;
}
}
}
assert!(found, "could not parse rustls version from Cargo.lock");
}
#[test]
fn deny_toml_lists_tls_bans() {
let deny = read("deny.toml");
for name in [
"openssl-sys",
"openssl",
"native-tls",
"libssh2-sys",
"ring",
] {
assert!(
deny.contains(name),
"G-TLS-02: deny.toml missing ban for {name}"
);
}
let bans_section = deny
.split("[bans]")
.nth(1)
.and_then(|s| s.split('[').next())
.unwrap_or("");
assert!(
!bans_section.contains("\"rustls\"") && !bans_section.contains("name = \"rustls\""),
"G-TLS: deny.toml must not ban product rustls"
);
}
#[test]
fn cargo_toml_russh_without_flate2() {
let cargo = read("Cargo.toml");
let russh_line = cargo
.lines()
.find(|l| l.trim_start().starts_with("russh ="))
.expect("russh dependency line");
assert!(
!russh_line.contains("flate2"),
"G-TLS-05: russh features still enable flate2: {russh_line}"
);
assert!(
russh_line.contains("aws-lc-rs"),
"G-TLS-05: russh must keep aws-lc-rs: {russh_line}"
);
}
#[test]
fn cargo_toml_tls_feature_default() {
let cargo = read("Cargo.toml");
assert!(
cargo.contains("default = [\"ssh-real\", \"tls\"]")
|| cargo.contains("default = [\"tls\", \"ssh-real\"]"),
"default features must include tls"
);
assert!(
cargo.contains("rustls ="),
"Cargo.toml must declare rustls"
);
assert!(
cargo.contains("instant-acme"),
"Cargo.toml must declare instant-acme for ACME"
);
let has_dep = cargo.lines().any(|l| {
let t = l.trim_start();
t.starts_with("rustls-pemfile") || t.starts_with("rustls-pemfile =")
});
assert!(!has_dep, "must not depend on unmaintained rustls-pemfile");
}
#[test]
fn security_md_documents_transport_crypto_policy() {
let sec = read("SECURITY.md");
assert!(
sec.contains("Transport & crypto policy") || sec.contains("Transport and crypto policy"),
"G-TLS-01: SECURITY.md missing Transport & crypto policy section"
);
assert!(
sec.contains("aws-lc-rs") || sec.contains("aws_lc_rs"),
"G-TLS-06: SECURITY.md must name aws-lc-rs provider"
);
assert!(
sec.to_lowercase().contains("rustls"),
"G-TLS-01: SECURITY.md must mention rustls"
);
assert!(
sec.to_lowercase().contains("compression") || sec.contains("none"),
"G-TLS-04: SECURITY.md should document compression policy"
);
}
#[test]
fn readme_mentions_ssh_crypto_policy() {
let readme = read("README.md");
assert!(
readme.contains("aws-lc-rs") || readme.contains("aws_lc_rs"),
"G-TLS-06: README must mention aws-lc-rs"
);
let ok = readme.contains("crypto policy")
|| readme.contains("Crypto policy")
|| readme.contains("Transport & crypto")
|| readme.contains("SSH-over-TLS")
|| (readme.contains("SSH") && readme.to_lowercase().contains("rustls"));
assert!(
ok,
"G-TLS-01: README should state SSH / rustls crypto policy"
);
}
#[test]
fn main_installs_crypto_provider() {
let main = read("src/main.rs");
assert!(
main.contains("install_default_provider"),
"main must call install_default_provider before runtime"
);
}
#[test]
fn connection_config_has_tls_field() {
let conn = read("src/ssh/connection.rs");
assert!(
conn.contains("tls: Option") || conn.contains("pub tls:"),
"ConnectionConfig must expose tls options"
);
}