Skip to content

Commit 9f479bb

Browse files
committed
feat: add support for policies and certificates
1 parent 2c4911d commit 9f479bb

5 files changed

Lines changed: 489 additions & 0 deletions

File tree

src/bedrock_agentcore/tools/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,20 @@
77
BrowserConfiguration,
88
BrowserExtension,
99
BrowserSigningConfiguration,
10+
Certificate,
11+
CertificateLocation,
1012
CodeInterpreterConfiguration,
13+
EnterprisePolicy,
14+
EnterprisePolicyS3Location,
1115
ExtensionS3Location,
1216
ExternalProxy,
1317
NetworkConfiguration,
1418
ProfileConfiguration,
1519
ProxyConfiguration,
1620
ProxyCredentials,
1721
RecordingConfiguration,
22+
ResourceLocation,
23+
SecretsManagerLocation,
1824
SessionConfiguration,
1925
ViewportConfiguration,
2026
VpcConfig,
@@ -25,19 +31,25 @@
2531
"BasicAuth",
2632
"BrowserClient",
2733
"browser_session",
34+
"Certificate",
35+
"CertificateLocation",
2836
"CodeInterpreter",
2937
"code_session",
3038
"BrowserConfiguration",
3139
"BrowserExtension",
3240
"BrowserSigningConfiguration",
3341
"CodeInterpreterConfiguration",
42+
"EnterprisePolicy",
43+
"EnterprisePolicyS3Location",
3444
"ExtensionS3Location",
3545
"ExternalProxy",
3646
"NetworkConfiguration",
3747
"ProfileConfiguration",
3848
"ProxyConfiguration",
3949
"ProxyCredentials",
4050
"RecordingConfiguration",
51+
"ResourceLocation",
52+
"SecretsManagerLocation",
4153
"SessionConfiguration",
4254
"ViewportConfiguration",
4355
"VpcConfig",

src/bedrock_agentcore/tools/browser_client.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
from .._utils.endpoints import get_control_plane_endpoint, get_data_plane_endpoint
2525
from .config import BrowserExtension, ProfileConfiguration, ProxyConfiguration, ViewportConfiguration
26+
from .config import Certificate, EnterprisePolicy, EnterprisePolicyS3Location, ResourceLocation
2627

2728

2829
def _to_dict(value):
@@ -116,6 +117,8 @@ def create_browser(
116117
description: Optional[str] = None,
117118
recording: Optional[Dict] = None,
118119
browser_signing: Optional[Dict] = None,
120+
enterprise_policies: Optional[List[Union[EnterprisePolicy, Dict[str, Any]]]] = None,
121+
certificates: Optional[List[Union[Certificate, Dict[str, Any]]]] = None,
119122
tags: Optional[Dict[str, str]] = None,
120123
client_token: Optional[str] = None,
121124
) -> Dict:
@@ -148,6 +151,11 @@ def create_browser(
148151
{
149152
"enabled": True
150153
}
154+
enterprise_policies (Optional[List[Union[EnterprisePolicy, Dict]]]): Chromium
155+
enterprise policies at managed enforcement level. Up to 10 policy files,
156+
each .json and max 5MB, from a same-region S3 bucket.
157+
certificates (Optional[List[Union[Certificate, Dict]]]): Root CA certificates
158+
from Secrets Manager for the browser to trust.
151159
tags (Optional[Dict[str, str]]): Tags for the browser
152160
client_token (Optional[str]): Idempotency token
153161
@@ -194,6 +202,12 @@ def create_browser(
194202
request_params["browserSigning"] = browser_signing
195203
self.logger.info("🔐 Web Bot Auth (browserSigning) enabled")
196204

205+
if enterprise_policies:
206+
request_params["enterprisePolicies"] = [_to_dict(p) for p in enterprise_policies]
207+
208+
if certificates:
209+
request_params["certificates"] = [_to_dict(c) for c in certificates]
210+
197211
if tags:
198212
request_params["tags"] = tags
199213

@@ -299,6 +313,8 @@ def start(
299313
proxy_configuration: Optional[Union[ProxyConfiguration, Dict[str, Any]]] = None,
300314
extensions: Optional[List[Union[BrowserExtension, Dict[str, Any]]]] = None,
301315
profile_configuration: Optional[Union[ProfileConfiguration, Dict[str, Any]]] = None,
316+
enterprise_policies: Optional[List[Union[EnterprisePolicy, Dict[str, Any]]]] = None,
317+
certificates: Optional[List[Union[Certificate, Dict[str, Any]]]] = None,
302318
) -> str:
303319
"""Start a browser sandbox session.
304320
@@ -324,6 +340,11 @@ def start(
324340
configuration for persisting browser state across sessions. Can be a
325341
ProfileConfiguration dataclass or a plain dict:
326342
{"profileIdentifier": "my-profile-id"}
343+
enterprise_policies (Optional[List[Union[EnterprisePolicy, Dict]]]): Chromium
344+
enterprise policies at recommended enforcement level. Up to 10 policy files,
345+
each .json and max 5MB, from a same-region S3 bucket.
346+
certificates (Optional[List[Union[Certificate, Dict]]]): Root CA certificates
347+
from Secrets Manager for the browser session to trust.
327348
328349
Returns:
329350
str: The session ID of the newly created session.
@@ -373,6 +394,12 @@ def start(
373394
if profile_configuration is not None:
374395
request_params["profileConfiguration"] = _to_dict(profile_configuration)
375396

397+
if enterprise_policies is not None:
398+
request_params["enterprisePolicies"] = [_to_dict(p) for p in enterprise_policies]
399+
400+
if certificates is not None:
401+
request_params["certificates"] = [_to_dict(c) for c in certificates]
402+
376403
response = self.data_plane_client.start_browser_session(**request_params)
377404

378405
self.identifier = response["browserIdentifier"]
@@ -633,6 +660,8 @@ def browser_session(
633660
proxy_configuration: Optional[Union[ProxyConfiguration, Dict[str, Any]]] = None,
634661
extensions: Optional[List[Union[BrowserExtension, Dict[str, Any]]]] = None,
635662
profile_configuration: Optional[Union[ProfileConfiguration, Dict[str, Any]]] = None,
663+
enterprise_policies: Optional[List[Union[EnterprisePolicy, Dict[str, Any]]]] = None,
664+
certificates: Optional[List[Union[Certificate, Dict[str, Any]]]] = None,
636665
) -> Generator[BrowserClient, None, None]:
637666
"""Context manager for creating and managing a browser sandbox session.
638667
@@ -687,6 +716,10 @@ def browser_session(
687716
start_kwargs["extensions"] = extensions
688717
if profile_configuration is not None:
689718
start_kwargs["profile_configuration"] = profile_configuration
719+
if enterprise_policies is not None:
720+
start_kwargs["enterprise_policies"] = enterprise_policies
721+
if certificates is not None:
722+
start_kwargs["certificates"] = certificates
690723

691724
client.start(**start_kwargs)
692725

src/bedrock_agentcore/tools/config.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,127 @@ def to_dict(self) -> Dict:
452452
return config
453453

454454

455+
@dataclass
456+
class EnterprisePolicyS3Location:
457+
"""S3 location of a browser enterprise policy JSON file.
458+
459+
Attributes:
460+
bucket: S3 bucket name (must be in the same region as the API call)
461+
prefix: S3 object key for the policy JSON file
462+
version_id: Optional S3 object version ID
463+
"""
464+
465+
bucket: str
466+
prefix: str
467+
version_id: Optional[str] = None
468+
469+
def to_dict(self) -> Dict:
470+
"""Convert to API-compatible dictionary."""
471+
location = {"bucket": self.bucket, "prefix": self.prefix}
472+
if self.version_id:
473+
location["versionId"] = self.version_id
474+
return location
475+
476+
477+
@dataclass
478+
class ResourceLocation:
479+
"""Location of a resource. Currently supports S3.
480+
481+
Attributes:
482+
s3: S3 location of the resource
483+
"""
484+
485+
s3: Optional[EnterprisePolicyS3Location] = None
486+
487+
def to_dict(self) -> Dict:
488+
"""Convert to API-compatible dictionary."""
489+
if self.s3:
490+
return {"s3": self.s3.to_dict()}
491+
raise ValueError("ResourceLocation must have one location type set")
492+
493+
494+
@dataclass
495+
class EnterprisePolicy:
496+
"""Browser enterprise policy.
497+
498+
Attributes:
499+
location: Location of the enterprise policy file
500+
type: "MANAGED" for CreateBrowser or "RECOMMENDED" for StartBrowserSession
501+
"""
502+
503+
location: ResourceLocation
504+
type: str
505+
506+
def __post_init__(self):
507+
if self.type not in ["MANAGED", "RECOMMENDED"]:
508+
raise ValueError(f"type must be 'MANAGED' or 'RECOMMENDED', got '{self.type}'")
509+
510+
def to_dict(self) -> Dict:
511+
"""Convert to API-compatible dictionary."""
512+
return {
513+
"location": self.location.to_dict(),
514+
"type": self.type,
515+
}
516+
517+
518+
@dataclass
519+
class SecretsManagerLocation:
520+
"""Secrets Manager location for a certificate.
521+
522+
Attributes:
523+
secret_arn: ARN of the Secrets Manager secret containing the certificate
524+
"""
525+
526+
secret_arn: str
527+
528+
def to_dict(self) -> Dict:
529+
"""Convert to API-compatible dictionary."""
530+
return {"secretArn": self.secret_arn}
531+
532+
533+
@dataclass
534+
class CertificateLocation:
535+
"""Location from which to retrieve a certificate.
536+
537+
Attributes:
538+
secrets_manager: Secrets Manager location containing the certificate
539+
"""
540+
541+
secrets_manager: SecretsManagerLocation
542+
543+
def to_dict(self) -> Dict:
544+
"""Convert to API-compatible dictionary."""
545+
return {"secretsManager": self.secrets_manager.to_dict()}
546+
547+
548+
@dataclass
549+
class Certificate:
550+
"""Root CA certificate for browser or code interpreter.
551+
552+
Attributes:
553+
location: Location of the certificate
554+
"""
555+
556+
location: CertificateLocation
557+
558+
def to_dict(self) -> Dict:
559+
"""Convert to API-compatible dictionary."""
560+
return {"location": self.location.to_dict()}
561+
562+
@classmethod
563+
def from_secret_arn(cls, secret_arn: str) -> "Certificate":
564+
"""Create a Certificate from a Secrets Manager ARN.
565+
566+
Args:
567+
secret_arn: ARN of the secret containing the certificate
568+
"""
569+
return cls(
570+
location=CertificateLocation(
571+
secrets_manager=SecretsManagerLocation(secret_arn=secret_arn)
572+
)
573+
)
574+
575+
455576
def create_browser_config(
456577
name: str,
457578
execution_role_arn: str,

0 commit comments

Comments
 (0)