mirror of
https://github.com/openai/codex.git
synced 2026-10-19 16:17:49 +00:00
Add browser extension request headers to config requirements (#51194)
## What changed Support `browser_use.extension.request_headers` as a list of `name`/`value` pairs and expose it through `config/requirements/read` as `browserUse.extension.requestHeaders`. Update the generated JSON schemas, TypeScript types, and Python models. Preserve header values, including template strings, verbatim. Higher-priority requirement layers replace the entire header list, including with an explicit empty list. Redact header values in Rust debug output while retaining them in API serialization. ## Testing Add coverage for parsing and malformed entries, extension scoping, empty-list overrides, API mapping and read responses, and debug redaction without changing serialized values. GitOrigin-RevId: 02299e82a91aa863966d0277091bc5e8cc1ebe07
This commit is contained in:
+39
@@ -8192,6 +8192,20 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"BrowserUseExtensionRequirements": {
|
||||
"properties": {
|
||||
"requestHeaders": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/v2/RequestHeader"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"BrowserUseOriginPolicy": {
|
||||
"properties": {
|
||||
"access": {
|
||||
@@ -8344,6 +8358,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"extension": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/BrowserUseExtensionRequirements"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"origins": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/v2/BrowserUseOriginPolicy"
|
||||
@@ -18521,6 +18545,21 @@
|
||||
"title": "RemoteControlStatusChangedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"RequestHeader": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"RequestId": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
||||
+39
@@ -1471,6 +1471,20 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"BrowserUseExtensionRequirements": {
|
||||
"properties": {
|
||||
"requestHeaders": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/RequestHeader"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"BrowserUseOriginPolicy": {
|
||||
"properties": {
|
||||
"access": {
|
||||
@@ -1623,6 +1637,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"extension": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BrowserUseExtensionRequirements"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"origins": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/BrowserUseOriginPolicy"
|
||||
@@ -14533,6 +14557,21 @@
|
||||
"title": "RemoteControlStatusChangedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"RequestHeader": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"RequestId": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
||||
+39
@@ -130,6 +130,20 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"BrowserUseExtensionRequirements": {
|
||||
"properties": {
|
||||
"requestHeaders": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/RequestHeader"
|
||||
},
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"BrowserUseOriginPolicy": {
|
||||
"properties": {
|
||||
"access": {
|
||||
@@ -237,6 +251,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"extension": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/BrowserUseExtensionRequirements"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"origins": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/BrowserUseOriginPolicy"
|
||||
@@ -1056,6 +1080,21 @@
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"RequestHeader": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ResidencyRequirement": {
|
||||
"enum": [
|
||||
"us"
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
+6
@@ -0,0 +1,6 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { RequestHeader } from "./RequestHeader";
|
||||
|
||||
export type BrowserUseExtensionRequirements = { requestHeaders: Array<RequestHeader> | null, };
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { BrowserUseExtensionRequirements } from "./BrowserUseExtensionRequirements";
|
||||
import type { BrowserUseOriginPolicy } from "./BrowserUseOriginPolicy";
|
||||
|
||||
export type BrowserUseRequirements = { allowWebmcp: boolean | null, allowHistoryAccess: boolean | null, disableAutoReview: boolean | null, allowGlobalPersistentApproval: boolean | null, defaultOriginPolicy: BrowserUseOriginPolicy | null, origins: { [key in string]?: BrowserUseOriginPolicy } | null, };
|
||||
export type BrowserUseRequirements = { extension: BrowserUseExtensionRequirements | null, allowWebmcp: boolean | null, allowHistoryAccess: boolean | null, disableAutoReview: boolean | null, allowGlobalPersistentApproval: boolean | null, defaultOriginPolicy: BrowserUseOriginPolicy | null, origins: { [key in string]?: BrowserUseOriginPolicy } | null, };
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type RequestHeader = { name: string, value: string, };
|
||||
@@ -52,6 +52,7 @@ export type { AutoReviewDecisionSource } from "./AutoReviewDecisionSource";
|
||||
export type { AutoReviewRequirements } from "./AutoReviewRequirements";
|
||||
export type { BrowserUseAccessApprovalLifetime } from "./BrowserUseAccessApprovalLifetime";
|
||||
export type { BrowserUseConfig } from "./BrowserUseConfig";
|
||||
export type { BrowserUseExtensionRequirements } from "./BrowserUseExtensionRequirements";
|
||||
export type { BrowserUseOriginPolicy } from "./BrowserUseOriginPolicy";
|
||||
export type { BrowserUseOriginPolicyConfig } from "./BrowserUseOriginPolicyConfig";
|
||||
export type { BrowserUseRequirements } from "./BrowserUseRequirements";
|
||||
@@ -427,6 +428,7 @@ export type { RemoteControlConnectionStatus } from "./RemoteControlConnectionSta
|
||||
export type { RemoteControlDisableParams } from "./RemoteControlDisableParams";
|
||||
export type { RemoteControlEnableParams } from "./RemoteControlEnableParams";
|
||||
export type { RemoteControlStatusChangedNotification } from "./RemoteControlStatusChangedNotification";
|
||||
export type { RequestHeader } from "./RequestHeader";
|
||||
export type { RequestPermissionProfile } from "./RequestPermissionProfile";
|
||||
export type { ResidencyRequirement } from "./ResidencyRequirement";
|
||||
export type { ResponseUsageMetadata } from "./ResponseUsageMetadata";
|
||||
|
||||
@@ -514,6 +514,7 @@ pub struct ComputerUseRequirements {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct BrowserUseRequirements {
|
||||
pub extension: Option<BrowserUseExtensionRequirements>,
|
||||
pub allow_webmcp: Option<bool>,
|
||||
pub allow_history_access: Option<bool>,
|
||||
pub disable_auto_review: Option<bool>,
|
||||
@@ -522,6 +523,30 @@ pub struct BrowserUseRequirements {
|
||||
pub origins: Option<BTreeMap<String, BrowserUseOriginPolicy>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct BrowserUseExtensionRequirements {
|
||||
pub request_headers: Option<Vec<RequestHeader>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct RequestHeader {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for RequestHeader {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RequestHeader")
|
||||
.field("name", &self.name)
|
||||
.field("value", &"[REDACTED]")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
@@ -1141,3 +1166,7 @@ pub struct ConfigWarningNotification {
|
||||
#[ts(optional)]
|
||||
pub range: Option<TextRange>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "config_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
use super::BrowserUseRequirements;
|
||||
use super::RequestHeader;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn request_header_debug_redacts_values_without_changing_serialization() {
|
||||
let wire = json!({ "name": "x-company-token", "value": "private-header-token" });
|
||||
let header: RequestHeader = serde_json::from_value(wire.clone()).unwrap();
|
||||
assert_eq!(serde_json::to_value(&header).unwrap(), wire);
|
||||
assert_eq!(
|
||||
format!("{header:?}"),
|
||||
r#"RequestHeader { name: "x-company-token", value: "[REDACTED]" }"#
|
||||
);
|
||||
let requirements: BrowserUseRequirements = serde_json::from_value(json!({
|
||||
"extension": { "requestHeaders": [wire] }
|
||||
}))
|
||||
.unwrap();
|
||||
for debug in [format!("{requirements:?}"), format!("{requirements:#?}")] {
|
||||
assert!(!debug.contains("private-header-token"));
|
||||
assert!(debug.contains("x-company-token"));
|
||||
assert!(debug.contains("[REDACTED]"));
|
||||
}
|
||||
assert_eq!(
|
||||
serde_json::to_value(&requirements).unwrap()["extension"]["requestHeaders"],
|
||||
json!([wire])
|
||||
);
|
||||
}
|
||||
@@ -626,6 +626,19 @@ fn map_browser_use_requirements_to_api(
|
||||
browser_use: codex_config::BrowserUseRequirementsToml,
|
||||
) -> BrowserUseRequirements {
|
||||
BrowserUseRequirements {
|
||||
extension: browser_use.extension.map(|extension| {
|
||||
codex_app_server_protocol::BrowserUseExtensionRequirements {
|
||||
request_headers: extension.request_headers.map(|headers| {
|
||||
headers
|
||||
.into_iter()
|
||||
.map(|header| codex_app_server_protocol::RequestHeader {
|
||||
name: header.name,
|
||||
value: header.value,
|
||||
})
|
||||
.collect()
|
||||
}),
|
||||
}
|
||||
}),
|
||||
allow_webmcp: browser_use.allow_webmcp,
|
||||
allow_history_access: browser_use.allow_history_access,
|
||||
disable_auto_review: browser_use.disable_auto_review,
|
||||
@@ -1096,6 +1109,12 @@ client_id = "mcp-client"
|
||||
let mapped = map_test_requirements(ConfigRequirementsToml {
|
||||
allow_browser_and_computer_use: Some(false),
|
||||
browser_use: Some(BrowserUseRequirementsToml {
|
||||
extension: Some(codex_config::BrowserUseExtensionRequirementsToml {
|
||||
request_headers: Some(vec![codex_config::RequestHeaderToml {
|
||||
name: "x-browser-agent".into(),
|
||||
value: "ChatGPT/{{session_id}}".into(),
|
||||
}]),
|
||||
}),
|
||||
allow_webmcp: Some(true),
|
||||
allow_history_access: Some(false),
|
||||
disable_auto_review: Some(true),
|
||||
@@ -1154,6 +1173,12 @@ client_id = "mcp-client"
|
||||
assert_eq!(
|
||||
mapped.browser_use,
|
||||
Some(BrowserUseRequirements {
|
||||
extension: Some(codex_app_server_protocol::BrowserUseExtensionRequirements {
|
||||
request_headers: Some(vec![codex_app_server_protocol::RequestHeader {
|
||||
name: "x-browser-agent".into(),
|
||||
value: "ChatGPT/{{session_id}}".into()
|
||||
}]),
|
||||
}),
|
||||
allow_webmcp: Some(true),
|
||||
allow_history_access: Some(false),
|
||||
disable_auto_review: Some(true),
|
||||
|
||||
@@ -57,6 +57,7 @@ async fn config_requirements_read_preserves_webmcp_policy() -> Result<()> {
|
||||
json!({
|
||||
"allowWebmcp": allow_webmcp,
|
||||
"allowHistoryAccess": allow_history_access,
|
||||
"extension": null,
|
||||
"disableAutoReview": null,
|
||||
"allowGlobalPersistentApproval": null,
|
||||
"defaultOriginPolicy": null,
|
||||
|
||||
@@ -106,6 +106,7 @@ async fn import_policy_is_separate_from_browser_feature_and_agent_policy() -> Re
|
||||
allow_external_browser_settings_import: Some(false),
|
||||
}),
|
||||
Some(BrowserUseRequirements {
|
||||
extension: None,
|
||||
allow_webmcp: None,
|
||||
allow_history_access: None,
|
||||
disable_auto_review: Some(true),
|
||||
|
||||
@@ -245,6 +245,9 @@ allow_history_access = false
|
||||
disable_auto_review = true
|
||||
allow_global_persistent_approval = false
|
||||
|
||||
[browser_use.extension]
|
||||
request_headers = [{ name = "x-browser-agent", value = "ChatGPT/{{session_id}}" }]
|
||||
|
||||
[browser_use.default_origin_policy]
|
||||
access = "deny"
|
||||
downloads = "allow"
|
||||
@@ -298,6 +301,12 @@ access = "deny"
|
||||
assert_eq!(
|
||||
requirements.browser_use,
|
||||
Some(BrowserUseRequirements {
|
||||
extension: Some(codex_app_server_protocol::BrowserUseExtensionRequirements {
|
||||
request_headers: Some(vec![codex_app_server_protocol::RequestHeader {
|
||||
name: "x-browser-agent".to_string(),
|
||||
value: "ChatGPT/{{session_id}}".to_string(),
|
||||
}]),
|
||||
}),
|
||||
allow_webmcp: None,
|
||||
allow_history_access: Some(false),
|
||||
disable_auto_review: Some(true),
|
||||
@@ -824,6 +833,7 @@ access = "deny"
|
||||
assert_eq!(
|
||||
requirements.browser_use,
|
||||
Some(BrowserUseRequirements {
|
||||
extension: None,
|
||||
allow_webmcp: None,
|
||||
allow_history_access: Some(false),
|
||||
disable_auto_review: None,
|
||||
|
||||
@@ -424,7 +424,11 @@ impl ManagedClientStartup {
|
||||
startup_complete.store(true, Ordering::Release);
|
||||
outcome
|
||||
};
|
||||
originator.scope(startup).in_current_span().boxed().shared()
|
||||
originator
|
||||
.scope(Box::pin(startup))
|
||||
.in_current_span()
|
||||
.boxed()
|
||||
.shared()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ pub enum BrowserUseAccessApprovalLifetimeToml {
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct BrowserUseRequirementsToml {
|
||||
pub extension: Option<BrowserUseExtensionRequirementsToml>,
|
||||
pub allow_webmcp: Option<bool>,
|
||||
pub allow_history_access: Option<bool>,
|
||||
pub disable_auto_review: Option<bool>,
|
||||
@@ -52,7 +53,10 @@ pub struct BrowserUseRequirementsToml {
|
||||
|
||||
impl BrowserUseRequirementsToml {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.allow_webmcp.is_none()
|
||||
self.extension
|
||||
.as_ref()
|
||||
.is_none_or(|extension| extension.request_headers.is_none())
|
||||
&& self.allow_webmcp.is_none()
|
||||
&& self.allow_history_access.is_none()
|
||||
&& self.disable_auto_review.is_none()
|
||||
&& self.allow_global_persistent_approval.is_none()
|
||||
@@ -67,6 +71,27 @@ impl BrowserUseRequirementsToml {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct BrowserUseExtensionRequirementsToml {
|
||||
pub request_headers: Option<Vec<RequestHeaderToml>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RequestHeaderToml {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for RequestHeaderToml {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RequestHeaderToml")
|
||||
.field("name", &self.name)
|
||||
.field("value", &"[REDACTED]")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct ComputerUseMacosRequirementsToml {
|
||||
pub bundle_ids: Option<BTreeMap<String, AllowDenyRequirementToml>>,
|
||||
|
||||
@@ -43,3 +43,94 @@ fn webmcp_requirements_reject_non_booleans() {
|
||||
assert!(error.to_string().contains("allow_webmcp"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_headers_preserve_templates_and_explicit_disablement() {
|
||||
for (value, expected) in [
|
||||
("[]", vec![]),
|
||||
(
|
||||
r#"[{name = "x-browser-agent", value = "ChatGPT/{{session_id}}"}]"#,
|
||||
vec![crate::RequestHeaderToml {
|
||||
name: "x-browser-agent".into(),
|
||||
value: "ChatGPT/{{session_id}}".into(),
|
||||
}],
|
||||
),
|
||||
] {
|
||||
let requirements: ConfigRequirementsToml = toml::from_str(&format!(
|
||||
"[browser_use.extension]\nrequest_headers = {value}"
|
||||
))
|
||||
.unwrap();
|
||||
assert!(!requirements.is_empty());
|
||||
assert_eq!(
|
||||
requirements
|
||||
.browser_use
|
||||
.unwrap()
|
||||
.extension
|
||||
.unwrap()
|
||||
.request_headers,
|
||||
Some(expected)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_headers_reject_malformed_entries() {
|
||||
for value in [
|
||||
"true",
|
||||
r#"[{name = "x-test"}]"#,
|
||||
r#"[{name = "x-test", value = 1}]"#,
|
||||
r#"[{name = "x-test", value = "ok", typo = true}]"#,
|
||||
] {
|
||||
assert!(
|
||||
toml::from_str::<ConfigRequirementsToml>(&format!(
|
||||
"[browser_use.extension]\nrequest_headers = {value}"
|
||||
))
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_headers_remain_scoped_to_extension() {
|
||||
let extension = r#"[browser_use.extension]
|
||||
request_headers = [{ name = "x-extension", value = "{{session_id}}" }]"#;
|
||||
let other = r#"[browser_use.iab]
|
||||
request_headers = [{ name = "x-iab", value = "future" }]"#;
|
||||
let expected: ConfigRequirementsToml = toml::from_str(extension).unwrap();
|
||||
let combined: ConfigRequirementsToml =
|
||||
toml::from_str(&format!("{extension}\n{other}")).unwrap();
|
||||
assert_eq!(combined, expected);
|
||||
for contents in [other, "[browser_use.extension]"] {
|
||||
let requirements: ConfigRequirementsToml = toml::from_str(contents).unwrap();
|
||||
assert!(requirements.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_header_debug_redacts_values_in_parent_requirements() {
|
||||
let requirements: ConfigRequirementsToml = toml::from_str(
|
||||
r#"[browser_use.extension]
|
||||
request_headers = [{ name = "x-company-token", value = "private-header-token" }]"#,
|
||||
)
|
||||
.unwrap();
|
||||
let header = &requirements
|
||||
.browser_use
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.extension
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.request_headers
|
||||
.as_ref()
|
||||
.unwrap()[0];
|
||||
assert_eq!(header.value, "private-header-token");
|
||||
assert_eq!(
|
||||
format!("{header:?}"),
|
||||
r#"RequestHeaderToml { name: "x-company-token", value: "[REDACTED]" }"#
|
||||
);
|
||||
for debug in [format!("{requirements:?}"), format!("{requirements:#?}")] {
|
||||
assert!(!debug.contains("private-header-token"));
|
||||
assert!(debug.contains("x-company-token"));
|
||||
assert!(debug.contains("[REDACTED]"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2577,6 +2577,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
requirements.browser_use,
|
||||
Some(BrowserUseRequirementsToml {
|
||||
extension: None,
|
||||
allow_webmcp: None,
|
||||
allow_history_access: Some(false),
|
||||
disable_auto_review: Some(true),
|
||||
@@ -2810,6 +2811,7 @@ mod tests {
|
||||
entries: BTreeMap::from([("personality".to_string(), true)]),
|
||||
};
|
||||
let browser_use = BrowserUseRequirementsToml {
|
||||
extension: None,
|
||||
allow_webmcp: None,
|
||||
allow_history_access: Some(false),
|
||||
disable_auto_review: Some(true),
|
||||
|
||||
@@ -56,12 +56,14 @@ pub use application_requirements::ApplicationRequirementsToml;
|
||||
pub use auth_policy::ManagedAuthPolicy;
|
||||
pub use browser_computer_use_requirements::AllowDenyRequirementToml;
|
||||
pub use browser_computer_use_requirements::BrowserUseAccessApprovalLifetimeToml;
|
||||
pub use browser_computer_use_requirements::BrowserUseExtensionRequirementsToml;
|
||||
pub use browser_computer_use_requirements::BrowserUseOriginPolicyToml;
|
||||
pub use browser_computer_use_requirements::BrowserUseRequirementsToml;
|
||||
pub use browser_computer_use_requirements::ComputerUseMacosRequirementsToml;
|
||||
pub use browser_computer_use_requirements::ComputerUseRequirementsToml;
|
||||
pub use browser_computer_use_requirements::ComputerUseWindowsExeRequirementToml;
|
||||
pub use browser_computer_use_requirements::ComputerUseWindowsRequirementsToml;
|
||||
pub use browser_computer_use_requirements::RequestHeaderToml;
|
||||
pub use browser_use::BrowserUseConfigToml;
|
||||
pub use browser_use::BrowserUseOriginPolicyConfigToml;
|
||||
pub use cloud_config_bundle::CloudConfigBundle;
|
||||
|
||||
@@ -1588,3 +1588,26 @@ path = "../plugins"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_headers_replace_lower_priority_lists_including_explicit_empty() {
|
||||
for higher in [
|
||||
r#"[browser_use.extension]
|
||||
request_headers = []"#,
|
||||
r#"[browser_use.extension]
|
||||
request_headers = [{ name = "x-managed", value = "{{session_id}}" }]"#,
|
||||
] {
|
||||
let composed = compose(vec![
|
||||
layer(
|
||||
"low",
|
||||
"Lower priority",
|
||||
r#"[browser_use.extension]
|
||||
request_headers = [{ name = "x-old", value = "old" }]"#,
|
||||
),
|
||||
layer("high", "Higher priority", higher),
|
||||
])
|
||||
.expect("compose requirements")
|
||||
.expect("requirements present");
|
||||
assert_eq!(composed, expected_requirements(higher));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,22 +465,6 @@ class BrowserUseOriginPolicyConfig(BaseModel):
|
||||
uploads: AllowDenyRequirement | None = None
|
||||
|
||||
|
||||
class BrowserUseRequirements(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
)
|
||||
allow_global_persistent_approval: Annotated[
|
||||
bool | None, Field(alias="allowGlobalPersistentApproval")
|
||||
] = None
|
||||
allow_history_access: Annotated[bool | None, Field(alias="allowHistoryAccess")] = None
|
||||
allow_webmcp: Annotated[bool | None, Field(alias="allowWebmcp")] = None
|
||||
default_origin_policy: Annotated[
|
||||
BrowserUseOriginPolicy | None, Field(alias="defaultOriginPolicy")
|
||||
] = None
|
||||
disable_auto_review: Annotated[bool | None, Field(alias="disableAutoReview")] = None
|
||||
origins: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class ByteRange(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
@@ -3872,6 +3856,14 @@ class RemoteControlStatusChangedNotification(BaseModel):
|
||||
status: RemoteControlConnectionStatus
|
||||
|
||||
|
||||
class RequestHeader(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
)
|
||||
name: str
|
||||
value: str
|
||||
|
||||
|
||||
class RequestId(RootModel[str | int]):
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
@@ -6860,6 +6852,30 @@ class BrowserUseConfig(BaseModel):
|
||||
origins: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class BrowserUseExtensionRequirements(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
)
|
||||
request_headers: Annotated[list[RequestHeader] | None, Field(alias="requestHeaders")] = None
|
||||
|
||||
|
||||
class BrowserUseRequirements(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
)
|
||||
allow_global_persistent_approval: Annotated[
|
||||
bool | None, Field(alias="allowGlobalPersistentApproval")
|
||||
] = None
|
||||
allow_history_access: Annotated[bool | None, Field(alias="allowHistoryAccess")] = None
|
||||
allow_webmcp: Annotated[bool | None, Field(alias="allowWebmcp")] = None
|
||||
default_origin_policy: Annotated[
|
||||
BrowserUseOriginPolicy | None, Field(alias="defaultOriginPolicy")
|
||||
] = None
|
||||
disable_auto_review: Annotated[bool | None, Field(alias="disableAutoReview")] = None
|
||||
extension: BrowserUseExtensionRequirements | None = None
|
||||
origins: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class CancelLoginAccountResponse(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
|
||||
Reference in New Issue
Block a user