Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions openhands-sdk/openhands/sdk/agent/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,14 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
"- An absolute path (e.g., '/path/to/custom_prompt.j2')"
),
)
security_policy_filename: str = Field(
security_policy_filename: str | None = Field(
default="security_policy.j2",
description=(
"Security policy template filename. Can be either:\n"
"Security policy template filename. Can be:\n"
"- A relative filename (e.g., 'security_policy.j2') loaded from the "
"agent's prompts directory\n"
"- An absolute path (e.g., '/path/to/custom_security_policy.j2')"
"- An absolute path (e.g., '/path/to/custom_security_policy.j2')\n"
"- Empty string or None to disable security policy"
),
)
system_prompt_kwargs: dict[str, object] = Field(
Expand Down
2 changes: 2 additions & 0 deletions openhands-sdk/openhands/sdk/agent/prompts/system_prompt.j2
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ You are OpenHands agent, a helpful AI assistant that can interact with a compute
</SELF_DOCUMENTATION>

<SECURITY>
{% if security_policy_filename %}
{% include security_policy_filename %}
{% endif %}
</SECURITY>

{% if llm_security_analyzer %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,11 @@ def set_confirmation_policy(self, policy: ConfirmationPolicyBase) -> None:

def set_security_analyzer(self, analyzer: SecurityAnalyzerBase | None) -> None:
"""Set the security analyzer for the remote conversation."""
payload = {"security_analyzer": analyzer.model_dump() if analyzer else analyzer}
payload = {
"security_analyzer": analyzer.model_dump(mode="json")
if analyzer
else analyzer
}
_send_request(
self._client,
"POST",
Expand Down
35 changes: 13 additions & 22 deletions openhands-sdk/openhands/sdk/security/grayswan/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,34 +91,25 @@ def validate_thresholds(self) -> GraySwanAnalyzer:

def model_post_init(self, __context: Any) -> None:
"""Initialize the analyzer after model creation."""
# Resolve API key from environment if not provided
if self.api_key is None:
env_key = os.getenv("GRAYSWAN_API_KEY")
if env_key:
self.api_key = SecretStr(env_key)
logger.debug(
"API key resolved from GRAYSWAN_API_KEY environment variable"
)

if not self.api_key:
# Design choice: Graceful degradation instead of fail-fast.
# This allows deployment without API key configured, returning UNKNOWN
# risk for all analyses. This is intentional to support environments
# where security analysis is optional or configured later.
# ALWAYS prefer environment variable - this ensures Docker gets the correct key
# even if serialization didn't work properly
env_key = os.getenv("GRAYSWAN_API_KEY")
if env_key:
self.api_key = SecretStr(env_key)
logger.info("Using GraySwan API key from environment")
elif not self.api_key or not self.api_key.get_secret_value():
logger.warning(
"GRAYSWAN_API_KEY not set. GraySwanAnalyzer will return UNKNOWN risk."
)

# Resolve policy ID from environment if not provided
if self.policy_id is None:
self.policy_id = os.getenv("GRAYSWAN_POLICY_ID")

if not self.policy_id:
# Use GraySwan default coding agent policy
# Always prefer environment variable for policy ID too
env_policy = os.getenv("GRAYSWAN_POLICY_ID")
if env_policy:
self.policy_id = env_policy
logger.info(f"Using GraySwan policy ID from environment: {self.policy_id}")
elif not self.policy_id:
self.policy_id = "689ca4885af3538a39b2ba04"
logger.info(f"Using default GraySwan policy ID: {self.policy_id}")
else:
logger.info(f"Using GraySwan policy ID from environment: {self.policy_id}")

logger.info(
f"GraySwanAnalyzer initialized with history_limit={self.history_limit}, "
Expand Down