fix(runner): carry Git credentials through the Rust ACP sidecar
This commit is contained in:
parent
56daf4664d
commit
45eabf6563
|
|
@ -51,8 +51,8 @@ in the sandbox home. For API-key Codex ACP runs, the adapter writes the explicit
|
|||
key to an owner-only login file in the staging copy; host credentials stay unchanged.
|
||||
Per-run GitHub launchers declare their own CommonJS package scope so warm runs
|
||||
inside ES-module repositories can still execute Git and GitHub CLI commands. Native runner launches carry the validated scoped paths
|
||||
through runnerd to ACPX, including the explicitly controller-projected GitHub
|
||||
broker environment. ACPX does not inherit ambient host GitHub credentials or
|
||||
through both runnerd’s Rust sidecar filter and the ACPX JavaScript launch filter,
|
||||
including the explicitly controller-projected GitHub broker environment. ACPX does not inherit ambient host GitHub credentials or
|
||||
shell startup hooks. CLI configuration remains in its private runtime directories.
|
||||
Warm sandbox task bindings persist independently of the experimental isolated
|
||||
workspace setting. Only the active host run can establish that binding; the
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use crate::generated_acpx_sidecar_contract::{
|
|||
use crate::local_runner::LocalRunnerError;
|
||||
use crate::process_supervisor::{
|
||||
BoundedLogBuffer, ProcessOutput, SupervisedProcess, VerifiedProcessLaunch,
|
||||
WORK_FOLDER_ENVIRONMENT_KEYS,
|
||||
GITHUB_CREDENTIAL_ENVIRONMENT_KEYS, WORK_FOLDER_ENVIRONMENT_KEYS,
|
||||
};
|
||||
use crate::stable_identity::{is_stable_id, DURABLE_STABLE_ID_CHARS, SHORT_STABLE_ID_CHARS};
|
||||
|
||||
|
|
@ -126,6 +126,7 @@ impl AcpxSidecarTransport {
|
|||
];
|
||||
keys.extend_from_slice(credential_keys);
|
||||
keys.extend_from_slice(WORK_FOLDER_ENVIRONMENT_KEYS);
|
||||
keys.extend_from_slice(GITHUB_CREDENTIAL_ENVIRONMENT_KEYS);
|
||||
Self::start_with_environment_keys(config, &keys)
|
||||
}
|
||||
|
||||
|
|
@ -674,6 +675,58 @@ fn response_error_classification(error: &ResponseError) -> &'static str {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn forwards_projected_github_environment_through_actual_sidecar_spawn() {
|
||||
const CHILD: &str = "PAPERCLIP_ACPX_ENV_TEST_CHILD";
|
||||
if std::env::var(CHILD).as_deref() != Ok("1") {
|
||||
// Set synthetic credentials on a separate test process. Do not
|
||||
// mutate the shared environment of parallel Rust tests.
|
||||
let result = std::process::Command::new(std::env::current_exe().unwrap())
|
||||
.args([
|
||||
"--exact",
|
||||
"acpx_sidecar_transport::tests::forwards_projected_github_environment_through_actual_sidecar_spawn",
|
||||
"--nocapture",
|
||||
])
|
||||
.env(CHILD, "1")
|
||||
.env("PAPERCLIP_GITHUB_BROKER_TOKEN", "test-run-capability")
|
||||
.env("BASH_ENV", "/dev/null")
|
||||
.env("GIT_CONFIG_COUNT", "1")
|
||||
.env("GIT_CONFIG_KEY_0", "credential.helper")
|
||||
.env("GIT_CONFIG_VALUE_0", "test-helper")
|
||||
.env("GIT_CONFIG_KEY_32", "outside-ceiling")
|
||||
.env("UNRELATED_TEST_SECRET", "must-not-cross")
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{}\n{}",
|
||||
String::from_utf8_lossy(&result.stdout),
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
return;
|
||||
}
|
||||
let config = AcpxSidecarTransportConfig {
|
||||
command: PathBuf::from("/bin/sh"),
|
||||
args: vec!["-c".to_owned(), r#"printf '%s\n' "$PAPERCLIP_GITHUB_BROKER_TOKEN|$BASH_ENV|$GIT_CONFIG_COUNT|$GIT_CONFIG_KEY_0|$GIT_CONFIG_VALUE_0|$GIT_CONFIG_KEY_32|$UNRELATED_TEST_SECRET""#.to_owned()],
|
||||
verified_launch: None,
|
||||
request_timeout: Duration::from_secs(3),
|
||||
shutdown_grace: Duration::from_millis(100),
|
||||
};
|
||||
for agent in ["claude", "codex", "pi"] {
|
||||
let mut transport = AcpxSidecarTransport::start_for_agent(&config, agent).unwrap();
|
||||
let line = transport
|
||||
.receive_stdout_line(Duration::from_secs(3), "environment probe")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
line.as_deref(),
|
||||
Some("test-run-capability|/dev/null|1|credential.helper|test-helper||"),
|
||||
"{agent}"
|
||||
);
|
||||
transport.shutdown().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_frame_does_not_echo_untrusted_deserialization_details() {
|
||||
let cases = [
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ use crate::durable::{redact_text, OpenCodeLaunchProfile};
|
|||
use crate::local_runner::LocalRunnerError;
|
||||
use crate::process_supervisor::{
|
||||
is_node_interpreter, BoundedLogBuffer, ProcessOutput, SupervisedProcess,
|
||||
VerifiedProcessArgument, VerifiedProcessLaunch, WORK_FOLDER_ENVIRONMENT_KEYS,
|
||||
VerifiedProcessArgument, VerifiedProcessLaunch, GITHUB_CREDENTIAL_ENVIRONMENT_KEYS,
|
||||
WORK_FOLDER_ENVIRONMENT_KEYS,
|
||||
};
|
||||
use crate::provider_bridge::{AuthorizedTool, DurableReplayFilter, ToolResult};
|
||||
use crate::provider_events::normalized_codex_terminal_event_type;
|
||||
|
|
@ -545,103 +546,6 @@ pub struct CodexProvider {
|
|||
permission_profile: &'static str,
|
||||
}
|
||||
|
||||
// The controller accepts at most 32 process-scoped Git config entries and
|
||||
// projects only these exact GitHub credential names into runnerd. Keep the
|
||||
// provider child boundary equally explicit: runnerd may inherit a configured
|
||||
// entry from this static ceiling, but cannot introduce another environment
|
||||
// variable by changing GIT_CONFIG_COUNT.
|
||||
const GITHUB_CREDENTIAL_ENVIRONMENT_KEYS: &[&str] = &[
|
||||
"ZDOTDIR",
|
||||
"BASH_ENV",
|
||||
"PAPERCLIP_GITHUB_BROKER_URL",
|
||||
"PAPERCLIP_GITHUB_BROKER_TOKEN",
|
||||
"PAPERCLIP_GITHUB_LAUNCHER_DIR",
|
||||
"GH_CONFIG_DIR",
|
||||
"GH_ENTERPRISE_TOKEN",
|
||||
"GITHUB_ENTERPRISE_TOKEN",
|
||||
"GIT_CONFIG_GLOBAL",
|
||||
"GIT_CONFIG_SYSTEM",
|
||||
"GIT_CONFIG_NOSYSTEM",
|
||||
"GIT_ASKPASS",
|
||||
"SSH_ASKPASS",
|
||||
"SSH_AUTH_SOCK",
|
||||
"GIT_SSH_COMMAND",
|
||||
"PAPERCLIP_GITHUB_BRIDGE_TOKEN",
|
||||
"GH_TOKEN",
|
||||
"GITHUB_TOKEN",
|
||||
"PAPERCLIP_GIT_TOKEN",
|
||||
"GIT_TERMINAL_PROMPT",
|
||||
"GIT_CONFIG_COUNT",
|
||||
"GIT_AUTHOR_NAME",
|
||||
"GIT_AUTHOR_EMAIL",
|
||||
"GIT_COMMITTER_NAME",
|
||||
"GIT_COMMITTER_EMAIL",
|
||||
"GIT_CONFIG_KEY_0",
|
||||
"GIT_CONFIG_VALUE_0",
|
||||
"GIT_CONFIG_KEY_1",
|
||||
"GIT_CONFIG_VALUE_1",
|
||||
"GIT_CONFIG_KEY_2",
|
||||
"GIT_CONFIG_VALUE_2",
|
||||
"GIT_CONFIG_KEY_3",
|
||||
"GIT_CONFIG_VALUE_3",
|
||||
"GIT_CONFIG_KEY_4",
|
||||
"GIT_CONFIG_VALUE_4",
|
||||
"GIT_CONFIG_KEY_5",
|
||||
"GIT_CONFIG_VALUE_5",
|
||||
"GIT_CONFIG_KEY_6",
|
||||
"GIT_CONFIG_VALUE_6",
|
||||
"GIT_CONFIG_KEY_7",
|
||||
"GIT_CONFIG_VALUE_7",
|
||||
"GIT_CONFIG_KEY_8",
|
||||
"GIT_CONFIG_VALUE_8",
|
||||
"GIT_CONFIG_KEY_9",
|
||||
"GIT_CONFIG_VALUE_9",
|
||||
"GIT_CONFIG_KEY_10",
|
||||
"GIT_CONFIG_VALUE_10",
|
||||
"GIT_CONFIG_KEY_11",
|
||||
"GIT_CONFIG_VALUE_11",
|
||||
"GIT_CONFIG_KEY_12",
|
||||
"GIT_CONFIG_VALUE_12",
|
||||
"GIT_CONFIG_KEY_13",
|
||||
"GIT_CONFIG_VALUE_13",
|
||||
"GIT_CONFIG_KEY_14",
|
||||
"GIT_CONFIG_VALUE_14",
|
||||
"GIT_CONFIG_KEY_15",
|
||||
"GIT_CONFIG_VALUE_15",
|
||||
"GIT_CONFIG_KEY_16",
|
||||
"GIT_CONFIG_VALUE_16",
|
||||
"GIT_CONFIG_KEY_17",
|
||||
"GIT_CONFIG_VALUE_17",
|
||||
"GIT_CONFIG_KEY_18",
|
||||
"GIT_CONFIG_VALUE_18",
|
||||
"GIT_CONFIG_KEY_19",
|
||||
"GIT_CONFIG_VALUE_19",
|
||||
"GIT_CONFIG_KEY_20",
|
||||
"GIT_CONFIG_VALUE_20",
|
||||
"GIT_CONFIG_KEY_21",
|
||||
"GIT_CONFIG_VALUE_21",
|
||||
"GIT_CONFIG_KEY_22",
|
||||
"GIT_CONFIG_VALUE_22",
|
||||
"GIT_CONFIG_KEY_23",
|
||||
"GIT_CONFIG_VALUE_23",
|
||||
"GIT_CONFIG_KEY_24",
|
||||
"GIT_CONFIG_VALUE_24",
|
||||
"GIT_CONFIG_KEY_25",
|
||||
"GIT_CONFIG_VALUE_25",
|
||||
"GIT_CONFIG_KEY_26",
|
||||
"GIT_CONFIG_VALUE_26",
|
||||
"GIT_CONFIG_KEY_27",
|
||||
"GIT_CONFIG_VALUE_27",
|
||||
"GIT_CONFIG_KEY_28",
|
||||
"GIT_CONFIG_VALUE_28",
|
||||
"GIT_CONFIG_KEY_29",
|
||||
"GIT_CONFIG_VALUE_29",
|
||||
"GIT_CONFIG_KEY_30",
|
||||
"GIT_CONFIG_VALUE_30",
|
||||
"GIT_CONFIG_KEY_31",
|
||||
"GIT_CONFIG_VALUE_31",
|
||||
];
|
||||
|
||||
const CODEX_PROVIDER_ENVIRONMENT_KEYS: &[&str] = &[
|
||||
"CODEX_HOME",
|
||||
"OPENAI_API_KEY",
|
||||
|
|
|
|||
|
|
@ -26,6 +26,103 @@ use sha2::{Digest, Sha256};
|
|||
use crate::local_runner::LocalRunnerError;
|
||||
|
||||
const PROCESS_OUTPUT_QUEUE_CAPACITY: usize = 256;
|
||||
// The controller accepts at most 32 process-scoped Git config entries and
|
||||
// projects only these exact GitHub credential names into runnerd. Keep the
|
||||
// provider child boundary equally explicit: runnerd may inherit a configured
|
||||
// entry from this static ceiling, but cannot introduce another environment
|
||||
// variable by changing GIT_CONFIG_COUNT.
|
||||
pub(crate) const GITHUB_CREDENTIAL_ENVIRONMENT_KEYS: &[&str] = &[
|
||||
"ZDOTDIR",
|
||||
"BASH_ENV",
|
||||
"PAPERCLIP_GITHUB_BROKER_URL",
|
||||
"PAPERCLIP_GITHUB_BROKER_TOKEN",
|
||||
"PAPERCLIP_GITHUB_LAUNCHER_DIR",
|
||||
"GH_CONFIG_DIR",
|
||||
"GH_ENTERPRISE_TOKEN",
|
||||
"GITHUB_ENTERPRISE_TOKEN",
|
||||
"GIT_CONFIG_GLOBAL",
|
||||
"GIT_CONFIG_SYSTEM",
|
||||
"GIT_CONFIG_NOSYSTEM",
|
||||
"GIT_ASKPASS",
|
||||
"SSH_ASKPASS",
|
||||
"SSH_AUTH_SOCK",
|
||||
"GIT_SSH_COMMAND",
|
||||
"PAPERCLIP_GITHUB_BRIDGE_TOKEN",
|
||||
"GH_TOKEN",
|
||||
"GITHUB_TOKEN",
|
||||
"PAPERCLIP_GIT_TOKEN",
|
||||
"GIT_TERMINAL_PROMPT",
|
||||
"GIT_CONFIG_COUNT",
|
||||
"GIT_AUTHOR_NAME",
|
||||
"GIT_AUTHOR_EMAIL",
|
||||
"GIT_COMMITTER_NAME",
|
||||
"GIT_COMMITTER_EMAIL",
|
||||
"GIT_CONFIG_KEY_0",
|
||||
"GIT_CONFIG_VALUE_0",
|
||||
"GIT_CONFIG_KEY_1",
|
||||
"GIT_CONFIG_VALUE_1",
|
||||
"GIT_CONFIG_KEY_2",
|
||||
"GIT_CONFIG_VALUE_2",
|
||||
"GIT_CONFIG_KEY_3",
|
||||
"GIT_CONFIG_VALUE_3",
|
||||
"GIT_CONFIG_KEY_4",
|
||||
"GIT_CONFIG_VALUE_4",
|
||||
"GIT_CONFIG_KEY_5",
|
||||
"GIT_CONFIG_VALUE_5",
|
||||
"GIT_CONFIG_KEY_6",
|
||||
"GIT_CONFIG_VALUE_6",
|
||||
"GIT_CONFIG_KEY_7",
|
||||
"GIT_CONFIG_VALUE_7",
|
||||
"GIT_CONFIG_KEY_8",
|
||||
"GIT_CONFIG_VALUE_8",
|
||||
"GIT_CONFIG_KEY_9",
|
||||
"GIT_CONFIG_VALUE_9",
|
||||
"GIT_CONFIG_KEY_10",
|
||||
"GIT_CONFIG_VALUE_10",
|
||||
"GIT_CONFIG_KEY_11",
|
||||
"GIT_CONFIG_VALUE_11",
|
||||
"GIT_CONFIG_KEY_12",
|
||||
"GIT_CONFIG_VALUE_12",
|
||||
"GIT_CONFIG_KEY_13",
|
||||
"GIT_CONFIG_VALUE_13",
|
||||
"GIT_CONFIG_KEY_14",
|
||||
"GIT_CONFIG_VALUE_14",
|
||||
"GIT_CONFIG_KEY_15",
|
||||
"GIT_CONFIG_VALUE_15",
|
||||
"GIT_CONFIG_KEY_16",
|
||||
"GIT_CONFIG_VALUE_16",
|
||||
"GIT_CONFIG_KEY_17",
|
||||
"GIT_CONFIG_VALUE_17",
|
||||
"GIT_CONFIG_KEY_18",
|
||||
"GIT_CONFIG_VALUE_18",
|
||||
"GIT_CONFIG_KEY_19",
|
||||
"GIT_CONFIG_VALUE_19",
|
||||
"GIT_CONFIG_KEY_20",
|
||||
"GIT_CONFIG_VALUE_20",
|
||||
"GIT_CONFIG_KEY_21",
|
||||
"GIT_CONFIG_VALUE_21",
|
||||
"GIT_CONFIG_KEY_22",
|
||||
"GIT_CONFIG_VALUE_22",
|
||||
"GIT_CONFIG_KEY_23",
|
||||
"GIT_CONFIG_VALUE_23",
|
||||
"GIT_CONFIG_KEY_24",
|
||||
"GIT_CONFIG_VALUE_24",
|
||||
"GIT_CONFIG_KEY_25",
|
||||
"GIT_CONFIG_VALUE_25",
|
||||
"GIT_CONFIG_KEY_26",
|
||||
"GIT_CONFIG_VALUE_26",
|
||||
"GIT_CONFIG_KEY_27",
|
||||
"GIT_CONFIG_VALUE_27",
|
||||
"GIT_CONFIG_KEY_28",
|
||||
"GIT_CONFIG_VALUE_28",
|
||||
"GIT_CONFIG_KEY_29",
|
||||
"GIT_CONFIG_VALUE_29",
|
||||
"GIT_CONFIG_KEY_30",
|
||||
"GIT_CONFIG_VALUE_30",
|
||||
"GIT_CONFIG_KEY_31",
|
||||
"GIT_CONFIG_VALUE_31",
|
||||
];
|
||||
|
||||
// Host-validated scoped paths, carried only through sandbox provider launches.
|
||||
pub(crate) const WORK_FOLDER_ENVIRONMENT_KEYS: &[&str] = &[
|
||||
"PAPERCLIP_RUNNER_EXTERNAL_SANDBOX",
|
||||
|
|
|
|||
Loading…
Reference in New Issue