-
Notifications
You must be signed in to change notification settings - Fork 93
feat: Add configurable security policy support to Agent #427
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
040e162
Add configurable security policy filename support
openhands-agent 0a9eaa9
Merge main branch and resolve conflicts
openhands-agent 817e221
Fix test failures by copying security_risk_assessment.j2 to temp dire…
openhands-agent 6ce4bc4
Simplify security policy tests to focus on core functionality
openhands-agent 05f679b
feat: Add support for absolute paths in template loading
openhands-agent be5ecbb
docs: Add absolute path examples and documentation
openhands-agent 4da8f4a
test: Clean up security policy integration tests
openhands-agent a680ee0
test: Remove redundant absolute path tests and example
openhands-agent 2f52b83
examples: Rewrite security policy example to match established style
openhands-agent 8feadfa
docs: Clean up security policy README after removing absolute_path_ex…
openhands-agent b29bbdd
Merge main branch and resolve conflicts
openhands-agent ea3219c
fix: Add security analyzer to test_security_policy_in_system_message …
openhands-agent 3a4aef2
Merge main into configurable-security-policy
openhands-agent 7719e68
fix: prevent stdlib glob shadowing and fix import order
openhands-agent 5ad3b8f
Merge branch 'main' into configurable-security-policy
neubig 924af92
Merge main into configurable-security-policy
openhands-agent d7333f0
Refactor security policy example to follow SDK examples format
openhands-agent 7424a2a
Fix test path for new repository structure
openhands-agent 05c100c
Fix test_render_template_with_relative_path to provide security_polic…
openhands-agent 681b993
Rename example 28 to 31 to match latest numbering
openhands-agent 397543c
Trigger CI re-run
openhands-agent 5c28a19
Merge main into configurable-security-policy
openhands-agent c8d9663
Rename example to 32 to avoid conflict with 31_iterative_refinement.py
openhands-agent d0a2306
Fix test to copy self_documentation.j2 template
openhands-agent 4eb442f
Revert unrelated glob module changes
openhands-agent File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
156 changes: 156 additions & 0 deletions
156
examples/01_standalone_sdk/32_configurable_security_policy.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| """OpenHands Agent SDK — Configurable Security Policy Example | ||
|
|
||
| This example demonstrates how to use a custom security policy template | ||
| with an agent. Security policies define risk assessment guidelines that | ||
| help agents evaluate the safety of their actions. | ||
|
|
||
| By default, agents use the built-in security_policy.j2 template. This | ||
| example shows how to: | ||
| 1. Use the default security policy | ||
| 2. Provide a custom security policy template embedded in the script | ||
| 3. Apply the custom policy to guide agent behavior | ||
| """ | ||
|
|
||
| import os | ||
| import tempfile | ||
| from pathlib import Path | ||
|
|
||
| from pydantic import SecretStr | ||
|
|
||
| from openhands.sdk import ( | ||
| LLM, | ||
| Agent, | ||
| Conversation, | ||
| Event, | ||
| LLMConvertibleEvent, | ||
| get_logger, | ||
| ) | ||
| from openhands.sdk.tool import Tool | ||
| from openhands.tools.file_editor import FileEditorTool | ||
| from openhands.tools.terminal import TerminalTool | ||
|
|
||
|
|
||
| logger = get_logger(__name__) | ||
|
|
||
| # Define a custom security policy template inline | ||
| CUSTOM_SECURITY_POLICY = ( | ||
| "# 🔐 Custom Security Risk Policy\n" | ||
| "When using tools that support the security_risk parameter, assess the " | ||
| "safety risk of your actions:\n" | ||
| "\n" | ||
| "- **LOW**: Safe read-only actions.\n" | ||
| " - Viewing files, calculations, documentation.\n" | ||
| "- **MEDIUM**: Moderate container-scoped actions.\n" | ||
| " - File modifications, package installations.\n" | ||
| "- **HIGH**: Potentially dangerous actions.\n" | ||
| " - Network access, system modifications, data exfiltration.\n" | ||
| "\n" | ||
| "**Custom Rules**\n" | ||
| "- Always prioritize user data safety.\n" | ||
| "- Escalate to **HIGH** for any external data transmission.\n" | ||
| ) | ||
|
|
||
| # Configure LLM | ||
| api_key = os.getenv("LLM_API_KEY") | ||
| assert api_key is not None, "LLM_API_KEY environment variable is not set." | ||
| model = os.getenv("LLM_MODEL", "anthropic/claude-sonnet-4-5-20250929") | ||
| base_url = os.getenv("LLM_BASE_URL") | ||
| llm = LLM( | ||
| usage_id="agent", | ||
| model=model, | ||
| base_url=base_url, | ||
| api_key=SecretStr(api_key), | ||
| ) | ||
|
|
||
| # Tools | ||
| cwd = os.getcwd() | ||
| tools = [ | ||
| Tool(name=TerminalTool.name), | ||
| Tool(name=FileEditorTool.name), | ||
| ] | ||
|
|
||
| # Example 1: Agent with default security policy | ||
| print("=" * 100) | ||
| print("Example 1: Agent with default security policy") | ||
| print("=" * 100) | ||
| default_agent = Agent(llm=llm, tools=tools) | ||
| print(f"Security policy filename: {default_agent.security_policy_filename}") | ||
| print("\nDefault security policy is embedded in the agent's system message.") | ||
|
|
||
| # Example 2: Agent with custom security policy | ||
| print("\n" + "=" * 100) | ||
| print("Example 2: Agent with custom security policy") | ||
| print("=" * 100) | ||
|
|
||
| # Create a temporary file for the custom security policy | ||
| with tempfile.NamedTemporaryFile( | ||
| mode="w", suffix=".j2", delete=False, encoding="utf-8" | ||
| ) as temp_file: | ||
| temp_file.write(CUSTOM_SECURITY_POLICY) | ||
| custom_policy_path = temp_file.name | ||
|
|
||
| try: | ||
| # Create agent with custom security policy (using absolute path) | ||
| custom_agent = Agent( | ||
| llm=llm, | ||
| tools=tools, | ||
| security_policy_filename=custom_policy_path, | ||
| ) | ||
| print(f"Security policy filename: {custom_agent.security_policy_filename}") | ||
| print("\nCustom security policy loaded from temporary file.") | ||
|
|
||
| # Verify the custom policy is in the system message | ||
| system_message = custom_agent.system_message | ||
| if "Custom Security Risk Policy" in system_message: | ||
| print("✓ Custom security policy successfully embedded in system message.") | ||
| else: | ||
| print("✗ Custom security policy not found in system message.") | ||
|
|
||
| # Run a conversation with the custom agent | ||
| print("\n" + "=" * 100) | ||
| print("Running conversation with custom security policy") | ||
| print("=" * 100) | ||
|
|
||
| llm_messages = [] # collect raw LLM messages | ||
|
|
||
| def conversation_callback(event: Event): | ||
| if isinstance(event, LLMConvertibleEvent): | ||
| llm_messages.append(event.to_llm_message()) | ||
|
|
||
| conversation = Conversation( | ||
| agent=custom_agent, | ||
| callbacks=[conversation_callback], | ||
| workspace=".", | ||
| ) | ||
|
|
||
| conversation.send_message( | ||
| "Please create a simple Python script named hello.py that prints " | ||
| "'Hello, World!'. Make sure to follow security best practices." | ||
| ) | ||
| conversation.run() | ||
|
|
||
| print("\n" + "=" * 100) | ||
| print("Conversation finished.") | ||
| print(f"Total LLM messages: {len(llm_messages)}") | ||
| print("=" * 100) | ||
|
|
||
| # Report cost | ||
| cost = conversation.conversation_stats.get_combined_metrics().accumulated_cost | ||
| print(f"EXAMPLE_COST: {cost}") | ||
|
|
||
| finally: | ||
| # Clean up temporary file | ||
| Path(custom_policy_path).unlink(missing_ok=True) | ||
|
|
||
| print("\n" + "=" * 100) | ||
| print("Example Summary") | ||
| print("=" * 100) | ||
| print("This example demonstrated:") | ||
| print("1. Using the default security policy (security_policy.j2)") | ||
| print("2. Creating a custom security policy template") | ||
| print("3. Applying the custom policy via security_policy_filename parameter") | ||
| print("4. Running a conversation with the custom security policy") | ||
| print( | ||
| "\nYou can customize security policies to match your organization's " | ||
| "specific requirements." | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.