use guix_scheme::validate;
#[test]
fn url_accepts_known_schemes() {
for u in [
"https://example.org/foo.git",
"http://example.org/foo.git",
"git://example.org/foo.git",
"ssh://git@example.org/foo.git",
"file:///srv/repos/foo.git",
] {
assert!(validate::url(u).is_ok(), "{u}");
}
}
#[test]
fn url_rejects_unknown_scheme_control_deceptive() {
assert!(validate::url("javascript:alert(1)").is_err());
assert!(validate::url("https://example.org/foo\nbar").is_err());
assert!(validate::url("https://example.org/\u{202E}tig.git").is_err());
assert!(validate::url(&format!("https://e.org/{}", "a".repeat(2048))).is_err());
}
#[test]
fn branch_charset_and_length() {
assert!(validate::branch("feature/foo-1.2+x_y").is_ok());
assert!(validate::branch("").is_err());
assert!(validate::branch(&"a".repeat(201)).is_err());
assert!(validate::branch("no spaces").is_err());
}
#[test]
fn commit_and_fingerprint_rules() {
assert!(validate::introduction_commit("9edb3f66fd807b096b48283debdcddccfea34bad").is_ok());
assert!(validate::introduction_commit("abc").is_err()); assert!(validate::introduction_commit("xyz4567").is_err()); assert!(validate::introduction_fingerprint(
"BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA"
)
.is_ok());
assert!(validate::introduction_fingerprint("short").is_err());
}
#[test]
fn channel_name_accepts_scheme_safe() {
assert!(validate::channel_name("good-name_1.2").is_ok());
assert!(validate::channel_name("guix").is_ok());
assert!(validate::channel_name("non+guix").is_ok());
}
#[test]
fn channel_name_rejects_pathological() {
assert!(validate::channel_name("").is_err());
assert!(validate::channel_name("bad name").is_err());
assert!(validate::channel_name("nope;(drop)").is_err());
assert!(validate::channel_name("with/slash").is_err());
}
mod libguix_parity {
use guix_scheme::{validate, Channel, Error};
#[test]
fn url_scheme_length_and_deceptive() {
assert!(validate::url("https://git.example/x").is_ok());
assert!(validate::url("ssh://h/x").is_ok());
assert!(validate::url("file:///srv/x").is_ok());
assert!(validate::url("ftp://x").is_err());
assert!(validate::url("https://git.example/\u{202E}x").is_err());
assert!(validate::url("https://git.example/\u{200B}x").is_err());
assert!(validate::url("https://git.example/\u{7}x").is_err());
let too_long = format!("https://e/{}", "a".repeat(2049 - "https://e/".len()));
assert_eq!(too_long.len(), 2049);
assert!(validate::url(&too_long).is_err());
}
#[test]
fn insecure_schemes_accepted_silently() {
assert!(validate::url("http://x/y").is_ok());
assert!(validate::url("git://x/y").is_ok());
}
#[test]
fn branch_charset_and_length() {
assert!(validate::branch("main").is_ok());
assert!(validate::branch("feature/x-1.2").is_ok());
assert!(validate::branch("").is_err());
assert!(validate::branch("has space").is_err());
assert!(validate::branch(&"a".repeat(201)).is_err());
}
#[test]
fn commit_hex_and_length() {
assert!(validate::introduction_commit("9edb3f66fd807b096b48283debdcddccfea34bad").is_ok());
assert!(validate::introduction_commit("abc").is_err());
assert!(validate::introduction_commit("9edb3f66fd807b096b48283debdcddccfea34baz").is_err());
assert!(validate::introduction_commit(&"a".repeat(65)).is_err());
}
#[test]
fn fingerprint_hexspace_and_length() {
assert!(validate::introduction_fingerprint("AB CD 12 34 56 78").is_ok());
assert!(
validate::introduction_fingerprint("9edb3f66fd807b096b48283debdcddccfea34bad").is_ok()
);
assert!(validate::introduction_fingerprint("short").is_err());
assert!(validate::introduction_fingerprint("AB CD 12 3g 56 78").is_err());
}
#[test]
fn channel_name_scheme_symbol() {
assert!(validate::channel_name("nonguix").is_ok());
assert!(validate::channel_name("my-channel.1").is_ok());
assert!(validate::channel_name("").is_err());
assert!(validate::channel_name("bad name").is_err());
assert!(validate::channel_name("emoji-\u{1F600}").is_err());
}
fn field_of(err: Error) -> String {
match err {
Error::Invalid { field, .. } => field,
other => panic!("expected Error::Invalid, got {other:?}"),
}
}
fn valid_channel() -> Channel {
Channel {
name: "nonguix".into(),
url: "https://gitlab.com/nonguix/nonguix.git".into(),
branch: Some("master".into()),
commit: None,
introduction_commit: Some("897c1a470da759236cc11798f4e0a5f7d4d59fbc".into()),
introduction_fingerprint: Some(
"2A39 3FFF 68F4 EF7A 3D29 12AF 6F51 20A0 22FB B2D5".into(),
),
}
}
#[test]
fn channel_fields_ok_and_field_names() {
assert!(validate::channel_fields(&valid_channel()).is_ok());
let mut bad_url = valid_channel();
bad_url.url = "ftp://x".into();
assert_eq!(
field_of(validate::channel_fields(&bad_url).unwrap_err()),
"url"
);
let mut bad_name = valid_channel();
bad_name.name = "bad name".into();
assert_eq!(
field_of(validate::channel_fields(&bad_name).unwrap_err()),
"name"
);
let mut bad_branch = valid_channel();
bad_branch.branch = Some("has space".into());
assert_eq!(
field_of(validate::channel_fields(&bad_branch).unwrap_err()),
"branch"
);
let mut bad_commit = valid_channel();
bad_commit.introduction_commit = Some("abc".into());
assert_eq!(
field_of(validate::channel_fields(&bad_commit).unwrap_err()),
"introduction.commit"
);
let mut bad_fpr = valid_channel();
bad_fpr.introduction_fingerprint = Some("short".into());
assert_eq!(
field_of(validate::channel_fields(&bad_fpr).unwrap_err()),
"introduction.fingerprint"
);
}
}