mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
* feat: Initial work on security analyzer * feat: Add remote invariant client * chore: improve fault tolerance of client * feat: Add button to enable Invariant Security Analyzer * [feat] confirmation mode for bash actions * feat: Add Invariant Tab with security risk outputs * feat: Add modal setting for Confirmation Mode * fix: frontend tests for confirmation mode switch * fix: add missing CONFIRMATION_MODE value in SettingsModal.test.tsx * fix: update test to integrate new setting * feat: Initial work on security analyzer * feat: Add remote invariant client * chore: improve fault tolerance of client * feat: Add button to enable Invariant Security Analyzer * feat: Add Invariant Tab with security risk outputs * feat: integrate security analyzer with confirmation mode * feat: improve invariant analyzer tab * feat: Implement user confirmation for running bash/python code * fix: don't display rejected actions * fix: make confirmation show only on assistant messages * feat: download traces, update policy, implement settings, auto-approve based on defined risk * Fix: low risk not being shown because it's 0 * fix: duplicate logs in tab * fix: log duplication * chore: prepare for merge, remove logging * Merge confirmation_mode from OpenDevin main * test: update tests to pass * chore: finish merging changes, security analyzer now operational again * feat: document Security Analyzers * refactor: api, monitor * chore: lint, fix risk None, revert policy * fix: check security_risk for None * refactor: rename instances of invariant to security analyzer * feat: add /api/options/security-analyzers endpoint * Move security analyzer from tab to modal * Temporary fix lock when security analyzer is not chosen * feat: don't show lock at all when security analyzer is not enabled * refactor: - Frontend: * change type of SECURITY_ANALYZER from bool to string * add combobox to select SECURITY_ANALYZER, current options are "invariant and "" (no security analyzer) * Security is now a modal, lock in bottom right is visible only if there's a security analyzer selected - Backend: * add close to SecurityAnalyzer * instantiate SecurityAnalyzer based on provided string from frontend * fix: update close to be async, to be consistent with other close on resources * fix: max height of modal (prevent overflow) * feat: add logo * small fixes * update docs for creating a security analyzer module * fix linting * update timeout for http client * fix: move security_analyzer config from agent to session * feat: add security_risk to browser actions * add optional remark on combobox * fix: asdict not called on dataclass, remove invariant dependency * fix: exclude None values when serializing * feat: take default policy from invariant-server instead of being hardcoded * fix: check if policy is None * update image name * test: fix some failing runs * fix: security analyzer tests * refactor: merge confirmation_mode and security_analyzer into SecurityConfig. Change invariant error message for docker * test: add tests for invariant parsing actions / observations * fix: python linting for test_security.py * Apply suggestions from code review Co-authored-by: Engel Nyst <enyst@users.noreply.github.com> * use ActionSecurityRisk | None intead of Optional * refactor action parsing * add extra check * lint parser.py * test: add field keep_prompt to test_security * docs: add information about how to enable the analyzer * test: Remove trailing whitespace in README.md text --------- Co-authored-by: Mislav Balunovic <mislav.balunovic@gmail.com> Co-authored-by: Engel Nyst <enyst@users.noreply.github.com> Co-authored-by: Xingyao Wang <xingyao6@illinois.edu>
61 lines
2.2 KiB
Python
61 lines
2.2 KiB
Python
from typing import Any
|
|
|
|
from fastapi import Request
|
|
|
|
from opendevin.core.logger import opendevin_logger as logger
|
|
from opendevin.events.action.action import Action, ActionSecurityRisk
|
|
from opendevin.events.event import Event
|
|
from opendevin.events.stream import EventStream, EventStreamSubscriber
|
|
|
|
|
|
class SecurityAnalyzer:
|
|
"""Security analyzer that receives all events and analyzes agent actions for security risks."""
|
|
|
|
def __init__(self, event_stream: EventStream):
|
|
"""Initializes a new instance of the SecurityAnalyzer class.
|
|
|
|
Args:
|
|
event_stream: The event stream to listen for events.
|
|
"""
|
|
self.event_stream = event_stream
|
|
self.event_stream.subscribe(
|
|
EventStreamSubscriber.SECURITY_ANALYZER, self.on_event
|
|
)
|
|
|
|
async def on_event(self, event: Event) -> None:
|
|
"""Handles the incoming event, and when Action is received, analyzes it for security risks."""
|
|
logger.info(f'SecurityAnalyzer received event: {event}')
|
|
await self.log_event(event)
|
|
if not isinstance(event, Action):
|
|
return
|
|
|
|
try:
|
|
event.security_risk = await self.security_risk(event) # type: ignore [attr-defined]
|
|
await self.act(event)
|
|
except Exception as e:
|
|
logger.error(f'Error occurred while analyzing the event: {e}')
|
|
|
|
async def handle_api_request(self, request: Request) -> Any:
|
|
"""Handles the incoming API request."""
|
|
raise NotImplementedError(
|
|
'Need to implement handle_api_request method in SecurityAnalyzer subclass'
|
|
)
|
|
|
|
async def log_event(self, event: Event) -> None:
|
|
"""Logs the incoming event."""
|
|
pass
|
|
|
|
async def act(self, event: Event) -> None:
|
|
"""Performs an action based on the analyzed event."""
|
|
pass
|
|
|
|
async def security_risk(self, event: Action) -> ActionSecurityRisk:
|
|
"""Evaluates the Action for security risks and returns the risk level."""
|
|
raise NotImplementedError(
|
|
'Need to implement security_risk method in SecurityAnalyzer subclass'
|
|
)
|
|
|
|
async def close(self) -> None:
|
|
"""Cleanup resources allocated by the SecurityAnalyzer."""
|
|
pass
|