Files
gitks/tests/bare_test.rs
T
zhenyi d243dce027 refactor(server): replace custom remote clients with macro-based implementation
- Replaced manual remote client functions with remote_client! macro for archive, blame, branch, commit, and diff services
- Simplified remote client creation logic using declarative macro approach
- Maintained same functionality while reducing code duplication across services

security(bare): enhance path traversal protection with comprehensive validation

- Added early relative_path validation to prevent path traversal attacks
- Implemented unified path validation to avoid TOCTOU race conditions
- Enhanced canonicalization checks for both existing and non-existent paths
- Added detailed logging for path traversal detection attempts

feat(cache): migrate from CLruCache to Moka with TTL and invalidation support

- Replaced clru dependency with moka for improved caching capabilities
- Added 300-second time-to-live for cache entries
- Implemented repository-specific cache invalidation mechanism
- Enhanced cache operations with thread-safe async support

refactor(commit): improve security validation for commit operations

- Added ref name validation to prevent command injection in cherry_pick_commit
- Implemented revision validation for commit selectors
- Added comprehensive input validation for create_commit parameters
- Enhanced file path validation to prevent traversal
2026-06-08 09:43:57 +08:00

150 lines
4.3 KiB
Rust

mod common;
use gitks::bare::GitBare;
use gitks::error::GitError;
use gitks::pb::RepositoryHeader;
#[test]
fn test_from_header_valid() {
let (_dir, gb) = common::setup_bare_repo();
let parent = gb.bare_dir.parent().unwrap().to_string_lossy().into_owned();
let name = gb
.bare_dir
.file_name()
.unwrap()
.to_string_lossy()
.into_owned();
let result = GitBare::from_repository_header(&RepositoryHeader {
storage_path: parent,
relative_path: name,
..Default::default()
});
assert!(result.is_ok());
}
#[test]
fn test_from_header_empty_path() {
let result = GitBare::from_repository_header(&RepositoryHeader {
storage_path: String::new(),
relative_path: String::new(),
..Default::default()
});
assert!(result.is_err());
}
#[test]
fn test_from_header_relative_storage_path() {
let result = GitBare::from_repository_header(&RepositoryHeader {
storage_path: "relative/path".into(),
relative_path: String::new(),
..Default::default()
});
assert!(result.is_err());
match result.unwrap_err() {
GitError::InvalidArgument(_) => {}
other => panic!("expected InvalidArgument, got: {:?}", other),
}
}
#[test]
fn test_from_header_relative_path_without_storage() {
let result = GitBare::from_repository_header(&RepositoryHeader {
storage_path: String::new(),
relative_path: "some/repo.git".into(),
..Default::default()
});
assert!(result.is_err());
}
#[test]
fn test_from_header_nonexistent_repo() {
let result = GitBare::from_repository_header(&RepositoryHeader {
storage_path: "/tmp".into(),
relative_path: "nonexistent_repo_12345".into(),
..Default::default()
});
assert!(result.is_err());
match result.unwrap_err() {
GitError::RepoNotFound => {}
other => panic!("expected RepoNotFound, got: {:?}", other),
}
}
#[test]
fn test_from_header_path_traversal() {
let result = GitBare::from_repository_header(&RepositoryHeader {
storage_path: "/tmp".into(),
relative_path: "../../../etc".into(),
..Default::default()
});
assert!(result.is_err());
match result.unwrap_err() {
GitError::InvalidArgument(msg) => {
assert!(
msg.contains("traversal"),
"should detect traversal, got: {}",
msg
);
}
other => panic!("expected InvalidArgument, got: {:?}", other),
}
}
#[test]
fn test_from_header_not_a_directory() {
let dir = tempfile::tempdir().unwrap();
let file_path = dir.path().join("not_a_dir.txt");
std::fs::write(&file_path, "content").unwrap();
let result = GitBare::from_repository_header(&RepositoryHeader {
storage_path: dir.path().to_string_lossy().into_owned(),
relative_path: "not_a_dir.txt".into(),
..Default::default()
});
assert!(result.is_err());
}
#[test]
fn test_from_header_dir_without_head() {
let dir = tempfile::tempdir().unwrap();
let subdir = dir.path().join("empty_dir");
std::fs::create_dir(&subdir).unwrap();
let result = GitBare::from_repository_header(&RepositoryHeader {
storage_path: dir.path().to_string_lossy().into_owned(),
relative_path: "empty_dir".into(),
..Default::default()
});
assert!(result.is_err());
match result.unwrap_err() {
GitError::NotBareRepository => {}
other => panic!("expected NotBareRepository, got: {:?}", other),
}
}
#[test]
fn test_object_format() {
let (_dir, gb) = common::setup_bare_repo();
let format = gb.object_format();
assert_eq!(format, gitks::pb::ObjectFormat::Sha1);
}
#[test]
fn test_oid_to_pb() {
let (_dir, gb) = common::setup_bare_repo();
let oid = gb.oid_to_pb("da39a3ee5e6b4b0d3255bfef95601890afd80709");
assert_eq!(oid.hex, "da39a3ee5e6b4b0d3255bfef95601890afd80709");
assert_eq!(oid.value.len(), 20);
assert_eq!(oid.format, gitks::pb::ObjectFormat::Sha1 as i32);
}
#[test]
fn test_oid_to_pb_invalid_hex() {
let (_dir, gb) = common::setup_bare_repo();
let oid = gb.oid_to_pb("not_hex");
// Should return default (empty) bytes on invalid hex
assert!(oid.value.is_empty());
assert_eq!(oid.hex, "not_hex");
}