diff --git a/crates/worker-runtime/src/working_directory.rs b/crates/worker-runtime/src/working_directory.rs index 1257af26..147dfb77 100644 --- a/crates/worker-runtime/src/working_directory.rs +++ b/crates/worker-runtime/src/working_directory.rs @@ -484,6 +484,18 @@ impl RuntimeGitMaterializer { Ok(binding) } + fn validate_plain_http_source( + request: &WorkingDirectoryRequest, + ) -> Result<(), WorkingDirectoryDiagnostic> { + if url::Url::parse(&request.repository.source.uri).is_ok_and(|url| url.scheme() == "http") { + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_repository_plain_http_unsupported", + "plain HTTP Repository sources are not executable; register an HTTPS or SSH source instead", + )); + } + Ok(()) + } + fn validate_request( request: &WorkingDirectoryRequest, ) -> Result<(), WorkingDirectoryDiagnostic> { @@ -499,6 +511,7 @@ impl RuntimeGitMaterializer { "the configured Repository provider is unsupported", )); } + Self::validate_plain_http_source(request)?; if matches!( request.repository.source.kind, workspace_api::RepositorySourceKind::Https | workspace_api::RepositorySourceKind::Ssh @@ -540,20 +553,10 @@ impl RuntimeGitMaterializer { validate_ssh_materialization_access(ssh)?; } workspace_api::RepositorySourceKind::Invalid => { - let is_plain_http = url::Url::parse(&request.repository.source.uri) - .is_ok_and(|url| url.scheme() == "http"); - let (code, message) = if is_plain_http { - ( - "working_directory_repository_plain_http_unsupported", - "plain HTTP Repository sources are not executable; register an HTTPS or SSH source instead", - ) - } else { - ( - "working_directory_repository_source_invalid", - "configured Repository source is invalid and cannot be materialized", - ) - }; - return Err(WorkingDirectoryDiagnostic::new(code, message)); + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_repository_source_invalid", + "configured Repository source is invalid and cannot be materialized", + )); } } validate_selector(request.repository.selector.as_deref().unwrap_or("HEAD")) @@ -627,6 +630,7 @@ impl RuntimeGitMaterializer { request: &WorkingDirectoryRequest, ) -> Result { validate_working_directory_id(&working_directory_id)?; + Self::validate_plain_http_source(request)?; let request = self.request_with_authorized_repository_access(&working_directory_id, request)?; Self::validate_request(&request)?; @@ -3812,6 +3816,34 @@ mod tests { "plain HTTP rejection must occur before invoking Git" ); + for (offset, kind) in [ + workspace_api::RepositorySourceKind::LocalPath, + workspace_api::RepositorySourceKind::File, + workspace_api::RepositorySourceKind::Https, + ] + .into_iter() + .enumerate() + { + let mut mismatched_http = request(repo.path()); + mismatched_http.repository.source = workspace_api::RepositorySource { + kind, + uri: "http://example.test/repo.git".to_string(), + }; + let git_invocations_before = test_repository_git_invocation_count(); + let error = materializer + .materialize(&worker_ref(4 + offset as u64), &mismatched_http) + .expect_err("plain HTTP URI must fail regardless of its declared source kind"); + assert_eq!( + error.code, + "working_directory_repository_plain_http_unsupported" + ); + assert_eq!( + test_repository_git_invocation_count(), + git_invocations_before, + "mismatched plain HTTP source must fail before invoking Git" + ); + } + let mut ssh = request(repo.path()); ssh.repository.source = workspace_api::RepositorySource { kind: workspace_api::RepositorySourceKind::Ssh, diff --git a/crates/workspace-server/src/repository_access.rs b/crates/workspace-server/src/repository_access.rs index 9dd302a4..7bea3352 100644 --- a/crates/workspace-server/src/repository_access.rs +++ b/crates/workspace-server/src/repository_access.rs @@ -1980,6 +1980,17 @@ mod tests { let error = validate_repository_access_source("remote", &source).unwrap_err(); assert!(error.to_string().contains("unsupported plain HTTP")); assert!(error.to_string().contains("HTTPS or SSH")); + + let mismatched = workspace_api::RepositorySource { + kind: workspace_api::RepositorySourceKind::Https, + uri: "http://git.example.test/team/project.git".to_string(), + }; + let error = validate_repository_access_source("remote", &mismatched).unwrap_err(); + assert!( + error + .to_string() + .contains("repository_source_plain_http_unsupported") + ); } #[test] diff --git a/crates/workspace-server/src/repository_source.rs b/crates/workspace-server/src/repository_source.rs index 2db070a3..7d3bf407 100644 --- a/crates/workspace-server/src/repository_source.rs +++ b/crates/workspace-server/src/repository_source.rs @@ -113,8 +113,7 @@ pub fn classify_legacy_repository_source(value: &str) -> RepositorySource { } pub(crate) fn is_plain_http_repository_source(source: &RepositorySource) -> bool { - source.kind == RepositorySourceKind::Invalid - && Url::parse(&source.uri).is_ok_and(|url| url.scheme() == "http") + Url::parse(&source.uri).is_ok_and(|url| url.scheme() == "http") } pub fn repository_source_fingerprint(source: &RepositorySource) -> String {