Skip to content

Commit 323334d

Browse files
fix: clear CodeQL incomplete URL substring sanitization alerts (#6804)
* fix: clear CodeQL incomplete URL substring sanitization alerts Replace hostname substring checks with urlparse hostname matching in RAG DataType classification, and assert the full mocked Stagehand navigate result instead of searching for a URL substring. Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com> * test: call DataTypes.from_content in GitHub hostname tests from_content lives on DataTypes, not the DataType enum. Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com> * test: harden tool-call streaming emit mock against instance shadowing CI failed when class-level CrewAIEventsBus.emit patches were shadowed by the singleton instance. Patch both the class and crewai_event_bus.emit, and read events from kwargs/args explicitly. Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Rip&Tear <theCyberTech@users.noreply.github.com>
1 parent 7accafb commit 323334d

4 files changed

Lines changed: 63 additions & 9 deletions

File tree

lib/crewai-tools/src/crewai_tools/rag/data_types.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ def get_file_type(path: str) -> DataType | None:
135135

136136
if "docs" in url.netloc or ("docs" in url.path and url.scheme != "file"):
137137
return DataType.DOCS_SITE
138-
if "github.com" in url.netloc:
138+
hostname = (url.hostname or "").lower()
139+
if hostname == "github.com" or hostname.endswith(".github.com"):
139140
return DataType.GITHUB
140141

141142
return DataType.WEBSITE
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Tests for DataTypes content classification."""
2+
3+
from crewai_tools.rag.data_types import DataType, DataTypes
4+
5+
6+
class TestDataTypesFromContentGitHub:
7+
"""GitHub URL detection must use hostname matching, not substrings."""
8+
9+
def test_github_com_url(self) -> None:
10+
assert (
11+
DataTypes.from_content("https://github.com/crewai/crewai")
12+
== DataType.GITHUB
13+
)
14+
15+
def test_github_subdomain_url(self) -> None:
16+
assert (
17+
DataTypes.from_content("https://gist.github.com/user/abc")
18+
== DataType.GITHUB
19+
)
20+
21+
def test_spoofed_github_hostname_is_website(self) -> None:
22+
# Substring checks like `"github.com" in netloc` would misclassify this.
23+
assert (
24+
DataTypes.from_content("https://github.com.evil.example/crewai")
25+
== DataType.WEBSITE
26+
)
27+
28+
def test_github_in_path_is_not_github(self) -> None:
29+
assert (
30+
DataTypes.from_content("https://example.com/github.com/repo")
31+
== DataType.WEBSITE
32+
)

lib/crewai-tools/tests/tools/stagehand_tool_test.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,14 @@ def test_navigate_command(mock_run, stagehand_tool):
163163
command_type="navigate",
164164
)
165165

166-
# Assertions
167-
assert "https://example.com" in result
166+
# Assertions — compare the full mocked result (avoid URL substring checks)
167+
assert result == "Successfully navigated to https://example.com"
168+
mock_run.assert_called_once_with(
169+
stagehand_tool,
170+
instruction="Go to example.com",
171+
url="https://example.com",
172+
command_type="navigate",
173+
)
168174

169175

170176
@patch(

lib/crewai/tests/llms/test_tool_call_streaming.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,33 @@ def get_temperature_tool_schema() -> dict[str, Any]:
3838

3939
@pytest.fixture
4040
def mock_emit() -> MagicMock:
41-
"""Mock the event bus emit function."""
42-
from crewai.events.event_bus import CrewAIEventsBus
41+
"""Mock the singleton event bus emit used by LLM providers.
4342
44-
with patch.object(CrewAIEventsBus, "emit") as mock:
45-
yield mock
43+
Patch the singleton instance (not only the class) so a leftover
44+
instance-level ``emit`` from other tests cannot shadow the mock.
45+
"""
46+
from crewai.events.event_bus import CrewAIEventsBus, crewai_event_bus
47+
48+
with (
49+
patch.object(CrewAIEventsBus, "emit") as class_mock,
50+
patch.object(crewai_event_bus, "emit", new=class_mock),
51+
):
52+
yield class_mock
53+
54+
55+
def _event_from_emit_call(call: Any) -> Any:
56+
"""Return the event argument from an emit mock call."""
57+
event = call.kwargs.get("event")
58+
if event is None and len(call.args) >= 2:
59+
event = call.args[1]
60+
return event
4661

4762

4863
def get_tool_call_events(mock_emit: MagicMock) -> list[LLMStreamChunkEvent]:
4964
"""Extract tool call streaming events from mock emit calls."""
5065
tool_call_events = []
5166
for call in mock_emit.call_args_list:
52-
event = call[1].get("event") if len(call) > 1 else None
67+
event = _event_from_emit_call(call)
5368
if isinstance(event, LLMStreamChunkEvent) and event.call_type == LLMCallType.TOOL_CALL:
5469
tool_call_events.append(event)
5570
return tool_call_events
@@ -59,7 +74,7 @@ def get_all_stream_events(mock_emit: MagicMock) -> list[LLMStreamChunkEvent]:
5974
"""Extract all streaming events from mock emit calls."""
6075
stream_events = []
6176
for call in mock_emit.call_args_list:
62-
event = call[1].get("event") if len(call) > 1 else None
77+
event = _event_from_emit_call(call)
6378
if isinstance(event, LLMStreamChunkEvent):
6479
stream_events.append(event)
6580
return stream_events

0 commit comments

Comments
 (0)