fix: retain byte command transport and explicit authorization fixtures

This commit is contained in:
2026-10-20 06:38:12 +09:00
parent f38d9a16ea
commit 223a74a9e2
3 changed files with 186 additions and 37 deletions
+183 -4
View File
@@ -5047,6 +5047,65 @@ mod tests {
const SAVED_SUBJEKTIV_SYSTEM_PROMPT: &str = "Committed saved Subject Worker system prompt";
// Substituted Host transport for restore/controller component tests. This
// fixture has exactly the two activation grants requested by the saved
// Manifest; it grants no protected operations or resources. It does not
// exercise authenticated source derivation, Subject policy, or revocation
// in the real Workspace Host.
fn saved_subjektiv_authorization_fixture(
input: server_api::authorization::OperationAuthorizationRequest,
) -> Result<server_api::authorization::OperationAuthorizationResponse, &'static str> {
use server_api::authorization::{OperationAuthorizationResponse, PermissionResource};
if input.validate().is_ok()
&& matches!(input.operation.as_str(), "subjektiv" | "workdir_catalog")
&& input.resource == PermissionResource::Feature
{
Ok(OperationAuthorizationResponse {
scope: Some(PermissionResource::Feature),
})
} else {
Err("saved Subject fixture has no matching exact Feature grant")
}
}
#[test]
fn saved_subjektiv_authorization_fixture_grants_only_exact_feature_activation() {
use server_api::authorization::{OperationAuthorizationRequest, PermissionResource};
for operation in ["subjektiv", "workdir_catalog"] {
let response = saved_subjektiv_authorization_fixture(OperationAuthorizationRequest {
operation: operation.into(),
resource: PermissionResource::Feature,
})
.unwrap();
assert_eq!(response.scope, Some(PermissionResource::Feature));
}
for (operation, resource) in [
("ticket", PermissionResource::Feature),
("subjektiv.journal", PermissionResource::Feature),
("subjektiv.*", PermissionResource::Feature),
(
"subjektiv",
PermissionResource::Workspace {
workspace_id: "workspace-saved-subjektiv".into(),
},
),
(
"subjektiv.journal",
PermissionResource::Workspace {
workspace_id: "workspace-saved-subjektiv".into(),
},
),
] {
assert!(
saved_subjektiv_authorization_fixture(OperationAuthorizationRequest {
operation: operation.into(),
resource,
})
.is_err()
);
}
}
fn saved_subjektiv_policy(
root: &Path,
worker_ref: &WorkerRef,
@@ -5211,6 +5270,25 @@ mod tests {
}))
}),
);
let app = app.route(
"/api/w/workspace-saved-subjektiv/authorization/check",
axum::routing::post(
|axum::Json(input): axum::Json<
server_api::authorization::OperationAuthorizationRequest,
>| async move {
match saved_subjektiv_authorization_fixture(input) {
Ok(response) => (
axum::http::StatusCode::OK,
axum::Json(serde_json::to_value(response).unwrap()),
),
Err(message) => (
axum::http::StatusCode::FORBIDDEN,
axum::Json(serde_json::json!({ "message": message })),
),
}
},
),
);
tokio::spawn(async move {
axum::serve(listener, app).await.unwrap();
});
@@ -6865,10 +6943,10 @@ mod tests {
working_directory_id: workdir_id,
capabilities: WorkdirSessionCapabilities::READ_ONLY,
}];
let client = WorkerWorkspaceContext::local_filesystem(Some(
WorkspaceId::new("workspace-a").unwrap(),
))
.client_handle();
// Direct local-router capability test: no Workspace Host transport is
// installed. Use an explicit standalone binding, not an unavailable
// Workspace-bound client pretending to authorize operations.
let client = WorkerWorkspaceContext::no_workspace().client_handle();
let router = restored_workdir_router(&attachments, &logical, &[], client).unwrap();
@@ -7473,6 +7551,91 @@ mod tests {
);
}
// Generic controller tests explicitly substitute the Host's non-Subject
// legacy decision. No Subject authority is modeled here; all other requests
// still use the original transport and its Backend/provider checks.
#[derive(Debug)]
struct LegacyNonSubjectAuthorizationClient(Arc<dyn WorkspaceClient>);
impl WorkspaceClient for LegacyNonSubjectAuthorizationClient {
fn workspace_id(&self) -> Option<&str> {
self.0.workspace_id()
}
fn kind(&self) -> &str {
self.0.kind()
}
fn is_available(&self) -> bool {
self.0.is_available()
}
fn execute(
&self,
request: worker::WorkspaceRequest,
) -> Result<worker::WorkspaceResponse, worker::WorkspaceClientError> {
if request.method == worker::WorkspaceRequestMethod::Post
&& request.path
== format!(
"/api/w/{}/authorization/check",
self.workspace_id()
.expect("legacy fixture needs Workspace identity")
)
{
let input: server_api::authorization::OperationAuthorizationRequest =
serde_json::from_str(request.body.as_deref().unwrap_or_default()).map_err(
|error| worker::WorkspaceClientError::Request(error.to_string()),
)?;
input
.validate()
.map_err(worker::WorkspaceClientError::Request)?;
return Ok(worker::WorkspaceResponse {
status: 200,
body: serde_json::to_string(
&server_api::authorization::OperationAuthorizationResponse { scope: None },
)
.unwrap(),
});
}
self.0.execute(request)
}
}
#[test]
fn legacy_non_subject_authorization_fixture_preserves_other_transport_failures() {
use server_api::authorization::PermissionResource;
let client = LegacyNonSubjectAuthorizationClient(
WorkerWorkspaceContext::local_filesystem(Some(WorkspaceId::new("ws-test").unwrap()))
.client_handle(),
);
assert_eq!(
client
.authorize_operation_scope("workdir_catalog", &PermissionResource::Feature)
.unwrap(),
None,
);
assert_eq!(
client
.authorize_operation_scope(
"ticket.create",
&PermissionResource::Workspace {
workspace_id: "ws-test".into(),
}
)
.unwrap(),
None,
);
assert!(
client
.authorize_operation("ticket.*", &PermissionResource::Feature)
.is_err()
);
assert!(matches!(
client.execute(worker::WorkspaceRequest::get("/api/w/ws-test/tickets")),
Err(worker::WorkspaceClientError::Unavailable(_)),
));
}
struct MockFactory {
client: MockClient,
runtime_base: PathBuf,
@@ -7538,6 +7701,22 @@ mod tests {
None,
None,
);
let workspace_context = if workspace_context.client().is_available()
&& workspace_context.workspace_id().is_some()
{
assert!(
!request.request.subjektiv_attached,
"generic legacy fixture must never authorize a Subject Worker"
);
WorkerWorkspaceContext::with_client(
workspace_context.workspace_id().cloned(),
Arc::new(LegacyNonSubjectAuthorizationClient(
workspace_context.client_handle(),
)),
)
} else {
workspace_context
};
let workspace_client = workspace_context.client_handle();
self.observed_workspace_clients.lock().unwrap().push((
workspace_client.kind().to_string(),
+2 -16
View File
@@ -417,11 +417,6 @@ impl WorkdirSession for AuthorizedWorkdirSession {
async fn start_command(&self, request: CommandRequest) -> Result<CommandHandle, WorkdirError> {
// Validate coordinates without treating cwd as the command's access scope.
WorkdirPath::new(request.cwd.as_str()).map_err(WorkdirError::from)?;
if request.spill_dir.is_some() {
return Err(WorkdirError::Denied(
"provider-local command spill paths have no attachment resource mapping".into(),
));
}
self.authorize_command()?;
self.session.start_command(request).await
}
@@ -609,9 +604,8 @@ mod tests {
CommandRequest {
command: command.into(),
timeout_secs: 10,
output_limit: 4096,
transport_byte_budget: 4096,
cwd: WorkdirPath::root(),
spill_dir: None,
tool_call_id: None,
}
}
@@ -786,7 +780,7 @@ mod tests {
}
#[tokio::test]
async fn unmappable_absolute_or_spill_paths_are_denied_before_local_effects() {
async fn unmappable_absolute_paths_are_denied_before_local_effects() {
let root = tempfile::tempdir().unwrap();
let client = AuthorizationClient::new(vec![
permission(WRITE, ".", true),
@@ -804,15 +798,7 @@ mod tests {
.await,
Err(WorkdirError::Denied(_))
));
let mut request = command("printf bad > escaped");
request.spill_dir = Some(root.path().join("spill"));
assert!(matches!(
session.start_command(request).await,
Err(WorkdirError::Denied(_))
));
assert!(!root.path().join("absolute").exists());
assert!(!root.path().join("escaped").exists());
assert!(!root.path().join("spill").exists());
assert!(client.requests.lock().unwrap().is_empty());
session.close().await.unwrap();
}
@@ -134,13 +134,6 @@ fn authorize_permissions(
// Validating cwd is not a shell confinement mechanism. The command can cd elsewhere,
// so a prefix grant (even one covering cwd recursively) is never enough.
resource(working_directory_id, COMMAND, &request.cwd, false)?;
if request.spill_dir.is_some() {
// This is a provider-local PathBuf, not an attachment-relative WorkdirPath.
// Do not authorize an unrepresentable write destination as attachment scope.
return Err(denied(
"caller-selected provider-local command spill paths are unsupported",
));
}
check(COMMAND, &WorkdirPath::root(), true)
}
WorkdirSessionOperation::CommandOutput(_) => {
@@ -233,9 +226,8 @@ mod tests {
WorkdirSessionOperation::CommandStart(CommandRequest {
command: "cd ..; cat secret".into(),
timeout_secs: 1,
output_limit: 1024,
transport_byte_budget: 1024,
cwd: path(cwd),
spill_dir: None,
tool_call_id: None,
})
}
@@ -427,14 +419,6 @@ mod tests {
assert!(!allowed(&[grant(COMMAND, ".", false)], &command("src")));
assert!(!allowed(&[grant(WRITE, ".", true)], &command("src")));
assert!(allowed(&[grant(COMMAND, ".", true)], &command("src")));
let WorkdirSessionOperation::CommandStart(mut request) = command("src") else {
unreachable!()
};
request.spill_dir = Some("/provider/private".into());
assert!(!allowed(
&[grant(COMMAND, ".", true)],
&WorkdirSessionOperation::CommandStart(request)
));
}
#[test]