Fix Gemini protocol status codes and error handling
- Path security violations now return 51 (Not Found) instead of 59 (Bad Request) - Timeouts return 41 (Server Unavailable) per Gemini spec - Add comprehensive request validation: empty requests, oversized requests (>1024 bytes), malformed URLs - Fix CLI argument conflict (config -c vs cert -c) - Update documentation with status codes, error handling guidelines, and lint checking - Add environment setup instructions for clippy and cargo PATH
This commit is contained in:
parent
2347c04211
commit
9d29321806
5 changed files with 166 additions and 62 deletions
|
|
@ -1,6 +1,11 @@
|
|||
use path_security::validate_path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum PathResolutionError {
|
||||
NotFound,
|
||||
}
|
||||
|
||||
pub fn parse_gemini_url(request: &str, expected_host: &str) -> Result<String, ()> {
|
||||
if let Some(url) = request.strip_prefix("gemini://") {
|
||||
let host_end = url.find('/').unwrap_or(url.len());
|
||||
|
|
@ -15,7 +20,7 @@ pub fn parse_gemini_url(request: &str, expected_host: &str) -> Result<String, ()
|
|||
}
|
||||
}
|
||||
|
||||
pub fn resolve_file_path(path: &str, dir: &str) -> Result<PathBuf, ()> {
|
||||
pub fn resolve_file_path(path: &str, dir: &str) -> Result<PathBuf, PathResolutionError> {
|
||||
let file_path_str = if path == "/" {
|
||||
"index.gmi".to_string()
|
||||
} else if path.ends_with('/') {
|
||||
|
|
@ -25,8 +30,18 @@ pub fn resolve_file_path(path: &str, dir: &str) -> Result<PathBuf, ()> {
|
|||
};
|
||||
|
||||
match validate_path(Path::new(&file_path_str), Path::new(dir)) {
|
||||
Ok(safe_path) => Ok(safe_path),
|
||||
Err(_) => Err(()),
|
||||
Ok(safe_path) => {
|
||||
// Path is secure, now check if file exists
|
||||
if safe_path.exists() {
|
||||
Ok(safe_path)
|
||||
} else {
|
||||
Err(PathResolutionError::NotFound)
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
// Path validation failed - treat as not found
|
||||
Err(PathResolutionError::NotFound)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,6 +86,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_resolve_file_path_root() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
// Create index.gmi file since we now check for existence
|
||||
std::fs::write(temp_dir.path().join("index.gmi"), "# Test").unwrap();
|
||||
assert!(resolve_file_path("/", temp_dir.path().to_str().unwrap()).is_ok());
|
||||
}
|
||||
|
||||
|
|
@ -78,19 +95,28 @@ mod tests {
|
|||
fn test_resolve_file_path_directory() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
std::fs::create_dir(temp_dir.path().join("test")).unwrap();
|
||||
std::fs::write(temp_dir.path().join("test/index.gmi"), "# Test").unwrap();
|
||||
assert!(resolve_file_path("/test/", temp_dir.path().to_str().unwrap()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_file_path_file() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
std::fs::write(temp_dir.path().join("test.gmi"), "# Test").unwrap();
|
||||
assert!(resolve_file_path("/test.gmi", temp_dir.path().to_str().unwrap()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_file_path_traversal() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
assert!(resolve_file_path("/../etc/passwd", temp_dir.path().to_str().unwrap()).is_err());
|
||||
assert_eq!(resolve_file_path("/../etc/passwd", temp_dir.path().to_str().unwrap()), Err(PathResolutionError::NotFound));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_file_path_not_found() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
// Don't create the file, should return NotFound error
|
||||
assert_eq!(resolve_file_path("/nonexistent.gmi", temp_dir.path().to_str().unwrap()), Err(PathResolutionError::NotFound));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue