fix: reject unknown profile archive selectors

This commit is contained in:
2026-07-08 19:21:52 +09:00
parent a823d414d0
commit a2833dadca
10 changed files with 189 additions and 81 deletions
+11 -1
View File
@@ -323,7 +323,6 @@ impl VerifiedProfileSourceArchive {
.manifest
.entrypoints
.get(selector)
.or_else(|| self.manifest.entrypoints.get("default"))
.ok_or_else(|| ProfileArchiveError::MissingEntrypoint(selector.to_string()))?;
let mut engine = Engine::new(ArchiveSourceLoader { archive: self });
let source = self
@@ -534,6 +533,17 @@ mod tests {
assert_eq!(manifest.worker.name, "archive-worker");
}
#[test]
fn unknown_selector_does_not_fallback_to_default() {
let archive = sample_archive();
let verified = archive.verify().unwrap();
let root = tempfile::tempdir().unwrap();
assert!(matches!(
verified.resolve_profile("missing", root.path(), "archive-worker"),
Err(ProfileArchiveError::MissingEntrypoint(selector)) if selector == "missing"
));
}
#[test]
fn archive_loader_rejects_undeclared_import() {
let archive = sample_archive();
+87 -25
View File
@@ -1339,13 +1339,18 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime {
.profile
.clone()
.unwrap_or_else(|| embedded_profile_selector(&request.intent));
let config_bundle = match self
.runtime
.store_config_bundle(default_embedded_config_bundle(&profile))
{
let config_bundle = match default_embedded_config_bundle(&profile).and_then(|bundle| {
self.runtime
.store_config_bundle(bundle)
.map_err(|err| err.to_string())
}) {
Ok(availability) => availability.reference,
Err(error) => {
diagnostics.push(embedded_runtime_diagnostic(&error));
diagnostics.push(diagnostic(
"embedded_profile_source_archive_invalid",
DiagnosticSeverity::Error,
error,
));
return WorkerSpawnResult {
state: WorkerOperationState::Rejected,
worker: None,
@@ -2020,7 +2025,18 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime {
.profile
.clone()
.unwrap_or_else(|| embedded_profile_selector(&request.intent));
let sync = self.sync_config_bundle(default_embedded_config_bundle(&profile));
let sync = match default_embedded_config_bundle(&profile) {
Ok(bundle) => self.sync_config_bundle(bundle),
Err(error) => ConfigBundleSyncResult {
state: WorkerOperationState::Rejected,
availability: None,
diagnostics: vec![diagnostic(
"remote_profile_source_archive_invalid",
DiagnosticSeverity::Error,
error,
)],
},
};
let Some(config_bundle) = sync.availability.map(|availability| availability.reference)
else {
return WorkerSpawnResult {
@@ -2349,14 +2365,14 @@ fn embedded_worker_execution_status_label(
}
}
fn default_embedded_config_bundle(profile: &ProfileSelector) -> ConfigBundle {
fn default_embedded_config_bundle(profile: &ProfileSelector) -> Result<ConfigBundle, String> {
let id = format!(
"workspace-runtime-{}",
embedded_profile_label(profile)
.unwrap_or_else(|| "default".to_string())
.replace([':', '/', ' '], "-")
);
ConfigBundle {
Ok(ConfigBundle {
metadata: ConfigBundleMetadata {
id,
digest: String::new(),
@@ -2373,13 +2389,16 @@ fn default_embedded_config_bundle(profile: &ProfileSelector) -> ConfigBundle {
label: embedded_profile_label(profile),
}],
declarations: Vec::new(),
profile_source_archive: Some(default_profile_source_archive(profile)),
profile_source_archive: Some(default_profile_source_archive(profile)?),
}
.with_computed_digest()
.with_computed_digest())
}
fn default_profile_source_archive(profile: &ProfileSelector) -> ProfileSourceArchive {
fn default_profile_source_archive(
profile: &ProfileSelector,
) -> Result<ProfileSourceArchive, String> {
let selected = embedded_profile_label(profile).unwrap_or_else(|| "default".to_string());
let selected_path = embedded_profile_path(profile)?;
let mut entrypoints = BTreeMap::new();
entrypoints.insert("default".to_string(), "profiles/default.dcdl".to_string());
entrypoints.insert(
@@ -2389,7 +2408,7 @@ fn default_profile_source_archive(profile: &ProfileSelector) -> ProfileSourceArc
for slug in ["companion", "intake", "orchestrator", "coder", "reviewer"] {
entrypoints.insert(format!("builtin:{slug}"), format!("profiles/{slug}.dcdl"));
}
entrypoints.insert(selected, embedded_profile_path(profile));
entrypoints.insert(selected, selected_path);
let mut sources = BTreeMap::new();
sources.insert(
@@ -2423,22 +2442,22 @@ fn default_profile_source_archive(profile: &ProfileSelector) -> ProfileSourceArc
imports: BTreeMap::new(),
sources,
})
.expect("builtin Decodal profile source archive is valid")
.map_err(|err| err.to_string())
}
fn embedded_profile_path(profile: &ProfileSelector) -> String {
fn embedded_profile_path(profile: &ProfileSelector) -> Result<String, String> {
match profile {
ProfileSelector::RuntimeDefault => "profiles/default.dcdl".to_string(),
ProfileSelector::Builtin(name) | ProfileSelector::Named(name) => {
match name.strip_prefix("builtin:").unwrap_or(name) {
"companion" => "profiles/companion.dcdl".to_string(),
"intake" => "profiles/intake.dcdl".to_string(),
"orchestrator" => "profiles/orchestrator.dcdl".to_string(),
"coder" => "profiles/coder.dcdl".to_string(),
"reviewer" => "profiles/reviewer.dcdl".to_string(),
_ => "profiles/default.dcdl".to_string(),
}
}
ProfileSelector::RuntimeDefault => Ok("profiles/default.dcdl".to_string()),
ProfileSelector::Builtin(name) => match name.strip_prefix("builtin:").unwrap_or(name) {
"default" => Ok("profiles/default.dcdl".to_string()),
"companion" => Ok("profiles/companion.dcdl".to_string()),
"intake" => Ok("profiles/intake.dcdl".to_string()),
"orchestrator" => Ok("profiles/orchestrator.dcdl".to_string()),
"coder" => Ok("profiles/coder.dcdl".to_string()),
"reviewer" => Ok("profiles/reviewer.dcdl".to_string()),
other => Err(format!("unknown builtin profile selector: builtin:{other}")),
},
ProfileSelector::Named(name) => Err(format!("unknown named profile selector: {name}")),
}
}
@@ -2945,6 +2964,49 @@ mod tests {
use std::sync::{Arc, Mutex};
use std::thread;
#[test]
fn embedded_builtin_decodal_profiles_resolve_through_archive() {
let root = tempfile::tempdir().unwrap();
for selector in [
ProfileSelector::RuntimeDefault,
ProfileSelector::Builtin("builtin:companion".to_string()),
ProfileSelector::Builtin("builtin:intake".to_string()),
ProfileSelector::Builtin("builtin:orchestrator".to_string()),
ProfileSelector::Builtin("builtin:coder".to_string()),
ProfileSelector::Builtin("builtin:reviewer".to_string()),
] {
let bundle = default_embedded_config_bundle(&selector).unwrap();
let archive = bundle
.profile_source_archive
.as_ref()
.unwrap()
.verify()
.unwrap();
let selector_key = match &selector {
ProfileSelector::RuntimeDefault => "default".to_string(),
ProfileSelector::Builtin(name) => name.clone(),
ProfileSelector::Named(name) => name.clone(),
};
let manifest = archive
.resolve_profile(&selector_key, root.path(), "embedded-test-worker")
.unwrap();
assert_eq!(manifest.worker.name, "embedded-test-worker");
}
}
#[test]
fn embedded_archive_rejects_unknown_selectors() {
assert!(
default_embedded_config_bundle(&ProfileSelector::Builtin(
"builtin:missing".to_string()
))
.is_err()
);
assert!(
default_embedded_config_bundle(&ProfileSelector::Named("custom".to_string())).is_err()
);
}
fn test_config_bundle() -> ConfigBundle {
ConfigBundle {
metadata: worker_runtime::config_bundle::ConfigBundleMetadata {