Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion crates/openshell-bootstrap/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,9 @@ fn glob_match(pattern: &str, path: &str, is_dir: bool) -> bool {
return true;
}
for (idx, _) in path.match_indices('/') {
if glob_match(rest, &path[idx + 1..], is_dir) {
if let Some(suffix) = path.get(idx + 1..)
&& glob_match(rest, suffix, is_dir)
{
return true;
}
}
Expand Down Expand Up @@ -592,6 +594,7 @@ mod tests {
assert!(glob_match("**/*.log", "a/b/c.log", false));
assert!(glob_match("**/*.log", "c.log", false));
assert!(!glob_match("**/*.log", "c.txt", false));
assert!(glob_match("**/föö.log", "a/b/föö.log", false));
}

#[test]
Expand Down
8 changes: 7 additions & 1 deletion crates/openshell-bootstrap/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ pub fn extract_host_from_ssh_destination(destination: &str) -> String {

// Handle user@host format
dest.find('@')
.map_or_else(|| dest.to_string(), |at_pos| dest[at_pos + 1..].to_string())
.and_then(|at_pos| dest.get(at_pos + 1..))
.map_or_else(|| dest.to_string(), ToString::to_string)
}

/// Resolve an SSH host alias to the actual hostname or IP address.
Expand Down Expand Up @@ -457,6 +458,11 @@ mod tests {
);
}

#[test]
fn extract_host_user_at_multi_byte_hostname() {
assert_eq!(extract_host_from_ssh_destination("user@mülti"), "mülti");
}

#[test]
fn metadata_roundtrip() {
let meta = GatewayMetadata {
Expand Down
8 changes: 6 additions & 2 deletions crates/openshell-router/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -824,8 +824,12 @@ fn build_backend_url(endpoint: &str, path: &str) -> String {
let base_path = &base[path_start..];
base_path.starts_with("/v1/") || base_path.ends_with("/v1")
});
if base_path_has_v1_edge_segment && (path == "/v1" || path.starts_with("/v1/")) {
return format!("{base}{}", &path[3..]);
if base_path_has_v1_edge_segment
&& let Some(rest) = path
.strip_prefix("/v1")
.filter(|rest| rest.is_empty() || rest.starts_with('/'))
{
return format!("{base}{rest}");
}

format!("{base}{path}")
Expand Down
5 changes: 4 additions & 1 deletion crates/openshell-supervisor-network/src/l7/inference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,10 @@ pub fn detect_inference_pattern<'a>(
let Some(slash_at) = rest.find('/') else {
return false;
};
return slash_at > 0 && rest[slash_at + 1..] == *after;
let Some(after_segment) = rest.get(slash_at + 1..) else {
return false;
};
return slash_at > 0 && after_segment == after;
}

path_only == p.path_glob
Expand Down
35 changes: 28 additions & 7 deletions crates/openshell-supervisor-process/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1101,11 +1101,8 @@ fn rewrite_passwd_at(path: &Path, uid: &str, gid: &str) -> Result<()> {
if line.starts_with("sandbox:") {
found = true;
let fields: Vec<&str> = line.split(':').collect();
if fields.len() >= 7 {
format!(
"{}:{}:{}:{}:{}:{}:{}",
fields[0], fields[1], uid, gid, fields[4], fields[5], fields[6]
)
if let [name, pass, _, _, gecos, home, shell, ..] = fields.as_slice() {
format!("{name}:{pass}:{uid}:{gid}:{gecos}:{home}:{shell}")
} else {
line.to_string()
}
Expand Down Expand Up @@ -1139,8 +1136,8 @@ fn rewrite_group_at(path: &Path, gid: &str) -> Result<()> {
if line.starts_with("sandbox:") {
found = true;
let fields: Vec<&str> = line.split(':').collect();
if fields.len() >= 4 {
format!("{}:{}:{}:{}", fields[0], fields[1], gid, fields[3])
if let [name, pass, _, members, ..] = fields.as_slice() {
format!("{name}:{pass}:{gid}:{members}")
} else {
line.to_string()
}
Expand Down Expand Up @@ -2302,6 +2299,30 @@ mod tests {
assert!(content.contains("sandbox:x:6000:"));
}

#[cfg(unix)]
#[test]
fn rewrite_passwd_leaves_malformed_entry_unchanged() {
let dir = tempfile::tempdir().unwrap();
let passwd = dir.path().join("passwd");
// Only 3 fields — slice pattern should fall through instead of panic.
std::fs::write(&passwd, "sandbox:x:1000\n").unwrap();
rewrite_passwd_at(&passwd, "5000", "6000").unwrap();
let content = std::fs::read_to_string(&passwd).unwrap();
assert!(content.contains("sandbox:x:1000"));
}

#[cfg(unix)]
#[test]
fn rewrite_group_leaves_malformed_entry_unchanged() {
let dir = tempfile::tempdir().unwrap();
let group = dir.path().join("group");
// Only 2 fields — slice pattern should fall through instead of panic.
std::fs::write(&group, "sandbox:x\n").unwrap();
rewrite_group_at(&group, "6000").unwrap();
let content = std::fs::read_to_string(&group).unwrap();
assert!(content.contains("sandbox:x"));
}

#[cfg(unix)]
#[test]
fn rewrite_passwd_preserves_other_entries() {
Expand Down
Loading