-
Notifications
You must be signed in to change notification settings - Fork 0
feat(integrations): 인바운드 인젝션 가드 (LangChain/CrewAI) (#370) #5
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -35,13 +35,25 @@ class _LoftBoxBaseTool(BaseTool): | |||||||||||||||||||||||||||||||||||||
| """LoftBox 클라이언트를 들고 있는 CrewAI 도구 베이스. | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| crewai ``BaseTool`` 은 pydantic 모델이라, 클라이언트는 PrivateAttr 로 저장한다. | ||||||||||||||||||||||||||||||||||||||
| 인바운드 인젝션 가드 설정도 PrivateAttr 로 함께 보관한다. | ||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| _client: "LoftBox" = PrivateAttr() | ||||||||||||||||||||||||||||||||||||||
| _injection_threshold: float = PrivateAttr(default=_common.DEFAULT_INJECTION_THRESHOLD) | ||||||||||||||||||||||||||||||||||||||
| _block_high_injection: bool = PrivateAttr(default=False) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| def __init__(self, client: "LoftBox", **kwargs: object) -> None: | ||||||||||||||||||||||||||||||||||||||
| def __init__( | ||||||||||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||||||||||
| client: "LoftBox", | ||||||||||||||||||||||||||||||||||||||
| *, | ||||||||||||||||||||||||||||||||||||||
| injection_threshold: float = _common.DEFAULT_INJECTION_THRESHOLD, | ||||||||||||||||||||||||||||||||||||||
| block_high_injection: bool = False, | ||||||||||||||||||||||||||||||||||||||
| **kwargs: object, | ||||||||||||||||||||||||||||||||||||||
| ) -> None: | ||||||||||||||||||||||||||||||||||||||
| super().__init__(**kwargs) | ||||||||||||||||||||||||||||||||||||||
| self._client = client | ||||||||||||||||||||||||||||||||||||||
| self._injection_threshold = injection_threshold | ||||||||||||||||||||||||||||||||||||||
| self._block_high_injection = block_high_injection | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+49
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win Validate The public option is documented as Proposed fix ) -> None:
+ if not 0.0 <= injection_threshold <= 1.0:
+ raise ValueError("injection_threshold must be between 0.0 and 1.0")
super().__init__(**kwargs)
self._client = client
self._injection_threshold = injection_threshold📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| class SendEmailTool(_LoftBoxBaseTool): | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -80,7 +92,12 @@ def _run( | |||||||||||||||||||||||||||||||||||||
| self, mailbox_id: str, limit: Optional[int] = None, cursor: Optional[str] = None | ||||||||||||||||||||||||||||||||||||||
| ) -> str: | ||||||||||||||||||||||||||||||||||||||
| return _common.run_check_inbox( | ||||||||||||||||||||||||||||||||||||||
| self._client, mailbox_id=mailbox_id, limit=limit, cursor=cursor | ||||||||||||||||||||||||||||||||||||||
| self._client, | ||||||||||||||||||||||||||||||||||||||
| mailbox_id=mailbox_id, | ||||||||||||||||||||||||||||||||||||||
| limit=limit, | ||||||||||||||||||||||||||||||||||||||
| cursor=cursor, | ||||||||||||||||||||||||||||||||||||||
| injection_threshold=self._injection_threshold, | ||||||||||||||||||||||||||||||||||||||
| block_high_injection=self._block_high_injection, | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
@@ -106,6 +123,8 @@ def _run( | |||||||||||||||||||||||||||||||||||||
| q=q, | ||||||||||||||||||||||||||||||||||||||
| limit=limit, | ||||||||||||||||||||||||||||||||||||||
| cursor=cursor, | ||||||||||||||||||||||||||||||||||||||
| injection_threshold=self._injection_threshold, | ||||||||||||||||||||||||||||||||||||||
| block_high_injection=self._block_high_injection, | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
@@ -127,12 +146,29 @@ def _run(self, message_id: str, reason: str) -> str: | |||||||||||||||||||||||||||||||||||||
| return _common.run_reject_message(self._client, message_id=message_id, reason=reason) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| def get_crewai_tools(client: "LoftBox") -> List[BaseTool]: | ||||||||||||||||||||||||||||||||||||||
| """CrewAI Agent 에 넘길 LoftBox 도구 목록.""" | ||||||||||||||||||||||||||||||||||||||
| def get_crewai_tools( | ||||||||||||||||||||||||||||||||||||||
| client: "LoftBox", | ||||||||||||||||||||||||||||||||||||||
| *, | ||||||||||||||||||||||||||||||||||||||
| injection_threshold: float = _common.DEFAULT_INJECTION_THRESHOLD, | ||||||||||||||||||||||||||||||||||||||
| block_high_injection: bool = False, | ||||||||||||||||||||||||||||||||||||||
| ) -> List[BaseTool]: | ||||||||||||||||||||||||||||||||||||||
| """CrewAI Agent 에 넘길 LoftBox 도구 목록. | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| injection_threshold/block_high_injection 으로 수신 메일 인젝션 가드를 조정한다 | ||||||||||||||||||||||||||||||||||||||
| (check_inbox/list_messages 에 적용). | ||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||
| return [ | ||||||||||||||||||||||||||||||||||||||
| SendEmailTool(client), | ||||||||||||||||||||||||||||||||||||||
| CheckInboxTool(client), | ||||||||||||||||||||||||||||||||||||||
| ListMessagesTool(client), | ||||||||||||||||||||||||||||||||||||||
| CheckInboxTool( | ||||||||||||||||||||||||||||||||||||||
| client, | ||||||||||||||||||||||||||||||||||||||
| injection_threshold=injection_threshold, | ||||||||||||||||||||||||||||||||||||||
| block_high_injection=block_high_injection, | ||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||
| ListMessagesTool( | ||||||||||||||||||||||||||||||||||||||
| client, | ||||||||||||||||||||||||||||||||||||||
| injection_threshold=injection_threshold, | ||||||||||||||||||||||||||||||||||||||
| block_high_injection=block_high_injection, | ||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||
| ApproveMessageTool(client), | ||||||||||||||||||||||||||||||||||||||
| RejectMessageTool(client), | ||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -35,14 +35,27 @@ class LoftBoxToolkit: | |||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||||||||
| client: 인증된 ``LoftBox`` 클라이언트. | ||||||||||||||||||||||||||||||
| injection_threshold: 이 점수 이상의 수신 메일을 고위험으로 보고 ⚠️ 경고를 | ||||||||||||||||||||||||||||||
| 붙인다(0.0~1.0, 기본 0.7). | ||||||||||||||||||||||||||||||
| block_high_injection: True 면 고위험 메일의 제목을 차단(strict 모드). | ||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def __init__(self, client: "LoftBox") -> None: | ||||||||||||||||||||||||||||||
| def __init__( | ||||||||||||||||||||||||||||||
| self, | ||||||||||||||||||||||||||||||
| client: "LoftBox", | ||||||||||||||||||||||||||||||
| *, | ||||||||||||||||||||||||||||||
| injection_threshold: float = _common.DEFAULT_INJECTION_THRESHOLD, | ||||||||||||||||||||||||||||||
| block_high_injection: bool = False, | ||||||||||||||||||||||||||||||
| ) -> None: | ||||||||||||||||||||||||||||||
| self._client = client | ||||||||||||||||||||||||||||||
| self._injection_threshold = injection_threshold | ||||||||||||||||||||||||||||||
| self._block_high_injection = block_high_injection | ||||||||||||||||||||||||||||||
|
Comment on lines
+47
to
+52
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win Validate The docstring promises Proposed fix ) -> None:
+ if not 0.0 <= injection_threshold <= 1.0:
+ raise ValueError("injection_threshold must be between 0.0 and 1.0")
self._client = client
self._injection_threshold = injection_threshold
self._block_high_injection = block_high_injection📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def get_tools(self) -> List["StructuredTool"]: | ||||||||||||||||||||||||||||||
| """LangChain 에이전트에 넘길 ``StructuredTool`` 목록.""" | ||||||||||||||||||||||||||||||
| c = self._client | ||||||||||||||||||||||||||||||
| threshold = self._injection_threshold | ||||||||||||||||||||||||||||||
| block = self._block_high_injection | ||||||||||||||||||||||||||||||
| return [ | ||||||||||||||||||||||||||||||
| StructuredTool.from_function( | ||||||||||||||||||||||||||||||
| func=partial(_common.run_send_email, c), | ||||||||||||||||||||||||||||||
|
|
@@ -51,13 +64,23 @@ def get_tools(self) -> List["StructuredTool"]: | |||||||||||||||||||||||||||||
| args_schema=_common.SendEmailArgs, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| StructuredTool.from_function( | ||||||||||||||||||||||||||||||
| func=partial(_common.run_check_inbox, c), | ||||||||||||||||||||||||||||||
| func=partial( | ||||||||||||||||||||||||||||||
| _common.run_check_inbox, | ||||||||||||||||||||||||||||||
| c, | ||||||||||||||||||||||||||||||
| injection_threshold=threshold, | ||||||||||||||||||||||||||||||
| block_high_injection=block, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| name="check_inbox", | ||||||||||||||||||||||||||||||
| description=_common.CHECK_INBOX_DESCRIPTION, | ||||||||||||||||||||||||||||||
| args_schema=_common.CheckInboxArgs, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| StructuredTool.from_function( | ||||||||||||||||||||||||||||||
| func=partial(_common.run_list_messages, c), | ||||||||||||||||||||||||||||||
| func=partial( | ||||||||||||||||||||||||||||||
| _common.run_list_messages, | ||||||||||||||||||||||||||||||
| c, | ||||||||||||||||||||||||||||||
| injection_threshold=threshold, | ||||||||||||||||||||||||||||||
| block_high_injection=block, | ||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||
| name="list_messages", | ||||||||||||||||||||||||||||||
| description=_common.LIST_MESSAGES_DESCRIPTION, | ||||||||||||||||||||||||||||||
| args_schema=_common.ListMessagesArgs, | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Validate
thresholdbefore comparing scores.injection_threshold > 1.0silently disables all normal risk warnings, while negative values mark every scored message risky. Since this is a public guard setting and scores are documented as0.0~1.0, reject out-of-range thresholds inassess_injection.Suggested fix
def assess_injection( msg: "Message", threshold: float = DEFAULT_INJECTION_THRESHOLD ) -> InjectionAssessment: """메시지의 인젝션 위험을 평가한다. score 가 없으면(미채점/발신 메시지) risky=False. score 가 threshold 이상이면 고위험으로 판정한다. """ + if not 0.0 <= threshold <= 1.0: + raise ValueError("injection threshold must be between 0.0 and 1.0") score = getattr(msg, "injection_score", None) cats = list(getattr(msg, "injection_categories", None) or []) risky = score is not None and score >= threshold return InjectionAssessment(risky=risky, score=score, categories=cats)📝 Committable suggestion
🤖 Prompt for AI Agents