diff --git a/codex-rs/code-mode-runtime/src/service_tests.rs b/codex-rs/code-mode-runtime/src/service_tests.rs index a40c2eb998..96c692da7d 100644 --- a/codex-rs/code-mode-runtime/src/service_tests.rs +++ b/codex-rs/code-mode-runtime/src/service_tests.rs @@ -785,6 +785,124 @@ text("done"); ); } +#[tokio::test] +async fn global_scope_contains_only_allowed_items() { + let service = InProcessCodeModeSession::new(); + + let response = execute( + &service, + ExecuteRequest { + enabled_tools: vec![echo_tool()], + source: "text(JSON.stringify(Object.getOwnPropertyNames(globalThis).sort()));" + .to_string(), + yield_time_ms: None, + ..execute_request("") + }, + ) + .await; + + let RuntimeResponse::Result { + content_items, + error_text: None, + .. + } = response + else { + panic!("global scope inspection failed unexpectedly: {response:?}"); + }; + let [FunctionCallOutputContentItem::InputText { text }] = content_items.as_slice() else { + panic!("global scope inspection returned unexpected output: {content_items:?}"); + }; + let globals = serde_json::from_str::>(text) + .expect("global scope inspection should return a JSON array"); + let expected = [ + "AggregateError", + "ALL_TOOLS", + "Array", + "ArrayBuffer", + "AsyncDisposableStack", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "Boolean", + "clearTimeout", + "DataView", + "Date", + "DisposableStack", + "Error", + "EvalError", + "FinalizationRegistry", + "Float16Array", + "Float32Array", + "Float64Array", + "Function", + "Infinity", + "Int16Array", + "Int32Array", + "Int8Array", + "Intl", + "Iterator", + "JSON", + "Map", + "Math", + "NaN", + "Number", + "Object", + "Promise", + "Proxy", + "RangeError", + "ReferenceError", + "Reflect", + "RegExp", + "Set", + "String", + "SuppressedError", + "Symbol", + "SyntaxError", + "Temporal", + "TypeError", + "URIError", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakRef", + "WeakSet", + "__codexContentItems", + "add_content", + "audio", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + "escape", + "exit", + "eval", + "generatedImage", + "globalThis", + "image", + "isFinite", + "isNaN", + "load", + "notify", + "parseFloat", + "parseInt", + "setTimeout", + "store", + "text", + "tools", + "undefined", + "unescape", + "yield_control", + ]; + for global in &globals { + assert!( + expected.contains(&global.as_str()), + "unexpected global {global} in {globals:?}" + ); + } +} + #[tokio::test] async fn v8_console_is_not_exposed_on_global_this() { let service = InProcessCodeModeSession::new(); diff --git a/codex-rs/core/tests/suite/code_mode.rs b/codex-rs/core/tests/suite/code_mode.rs index 129368082d..9e09e77ce9 100644 --- a/codex-rs/core/tests/suite/code_mode.rs +++ b/codex-rs/core/tests/suite/code_mode.rs @@ -97,7 +97,6 @@ use image::metadata::Orientation; use pretty_assertions::assert_eq; use serde_json::Value; use std::collections::HashMap; -use std::collections::HashSet; use std::fs; use std::io::Cursor; use std::path::Path; @@ -5121,118 +5120,6 @@ text(`echo=${result.structuredContent.echo}`); Ok(()) } -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn code_mode_lists_global_scope_items() -> Result<()> { - skip_if_no_network!(Ok(())); - - let server = responses::start_mock_server().await; - let code = r#" -text(JSON.stringify(Object.getOwnPropertyNames(globalThis).sort())); -"#; - - let (_test, second_mock) = - run_code_mode_turn_with_rmcp(&server, "use exec to inspect global scope", code).await?; - - let req = second_mock.single_request(); - let (output, success) = custom_tool_output_body_and_success(&req, "call-1"); - assert_ne!( - success, - Some(false), - "exec global scope inspection failed unexpectedly: {output}" - ); - let globals = serde_json::from_str::>(&output)?; - let globals = globals.into_iter().collect::>(); - let expected = [ - "AggregateError", - "ALL_TOOLS", - "Array", - "ArrayBuffer", - "AsyncDisposableStack", - "BigInt", - "BigInt64Array", - "BigUint64Array", - "Boolean", - "clearTimeout", - "DataView", - "Date", - "DisposableStack", - "Error", - "EvalError", - "FinalizationRegistry", - "Float16Array", - "Float32Array", - "Float64Array", - "Function", - "Infinity", - "Int16Array", - "Int32Array", - "Int8Array", - "Intl", - "Iterator", - "JSON", - "Map", - "Math", - "NaN", - "Number", - "Object", - "Promise", - "Proxy", - "RangeError", - "ReferenceError", - "Reflect", - "RegExp", - "Set", - "String", - "SuppressedError", - "Symbol", - "SyntaxError", - "Temporal", - "TypeError", - "URIError", - "Uint16Array", - "Uint32Array", - "Uint8Array", - "Uint8ClampedArray", - "WeakMap", - "WeakRef", - "WeakSet", - "__codexContentItems", - "add_content", - "audio", - "decodeURI", - "decodeURIComponent", - "encodeURI", - "encodeURIComponent", - "escape", - "exit", - "eval", - "generatedImage", - "globalThis", - "image", - "isFinite", - "isNaN", - "load", - "notify", - "parseFloat", - "parseInt", - "setTimeout", - "store", - "text", - "tools", - "undefined", - "unescape", - "yield_control", - ]; - for g in &globals { - assert!( - expected.contains(&g.as_str()), - "unexpected global {g} in {globals:?}" - ); - } - - Ok(()) -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn code_mode_exports_all_tools_metadata_for_builtin_tools() -> Result<()> { skip_if_no_network!(Ok(()));