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
4 changes: 2 additions & 2 deletions astrbot/api/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
CommandResult,
EventResultType,
)
from astrbot.core.platform import AstrMessageEvent

# star register
from astrbot.core.star.register import (
Expand All @@ -31,8 +30,9 @@
from astrbot.core.star.register import (
register_star as register, # 注册插件(Star)
)
from astrbot.core.star import Context, Star
from astrbot.core.star.base import Star
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import error: astrbot.core.star.base module does not exist. The Star class is defined in astrbot.core.star.__init__.py, not in a separate base.py file. This import should be changed to from astrbot.core.star import Star.

Suggested change
from astrbot.core.star.base import Star
from astrbot.core.star import Star

Copilot uses AI. Check for mistakes.
from astrbot.core.star.config import *
from astrbot.core.star.context import Context


# provider
Expand Down
4 changes: 3 additions & 1 deletion astrbot/api/star/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from astrbot.core.star import Context, Star, StarTools
from astrbot.core.star.base import Star
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import error: astrbot.core.star.base module does not exist. The Star class is defined in astrbot.core.star.__init__.py, not in a separate base.py file. This import should be changed to from astrbot.core.star import Star.

Suggested change
from astrbot.core.star.base import Star
from astrbot.core.star import Star

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

看起来需要同步?

from astrbot.core.star.config import *
from astrbot.core.star.context import Context
from astrbot.core.star.register import (
register_star as register, # 注册插件(Star)
)
from astrbot.core.star.star_tools import StarTools

__all__ = ["Context", "Star", "StarTools", "register"]
30 changes: 18 additions & 12 deletions astrbot/core/astr_main_agent.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original file line number Diff line number Diff line change
Expand Up @@ -760,17 +760,23 @@ async def _handle_webchat(
if not user_prompt or not chatui_session_id or not session or session.display_name:
return

llm_resp = await prov.text_chat(
system_prompt=(
"You are a conversation title generator. "
"Generate a concise title in the same language as the user’s input, "
"no more than 10 words, capturing only the core topic."
"If the input is a greeting, small talk, or has no clear topic, "
"(e.g., “hi”, “hello”, “haha”), return <None>. "
"Output only the title itself or <None>, with no explanations."
),
prompt=f"Generate a concise title for the following user query:\n{user_prompt}",
)
try:
llm_resp = await prov.text_chat(
system_prompt=(
"You are a conversation title generator. "
"Generate a concise title in the same language as the user’s input, "
"no more than 10 words, capturing only the core topic."
"If the input is a greeting, small talk, or has no clear topic, "
"(e.g., “hi”, “hello”, “haha”), return <None>. "
"Output only the title itself or <None>, with no explanations."
),
prompt=f"Generate a concise title for the following user query:\n{user_prompt}",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The conversation title generation logic in _handle_webchat is vulnerable to prompt injection because it directly concatenates untrusted user input (user_prompt) into the LLM prompt. An attacker could craft a message that overrides the instructions in the system prompt to control the generated title. Since this title is stored in the database and displayed in the web dashboard, this could lead to Stored Cross-Site Scripting (XSS) if the dashboard does not properly escape the session titles, or at least allow for defacement of the management interface.

To remediate this, use delimiters to wrap the user input and update the system prompt to only consider the content within those delimiters. Additionally, ensure the output is sanitized before storage and rendering.

Suggested change
prompt=f"Generate a concise title for the following user query:\n{user_prompt}",
prompt=f"Generate a concise title for the following user query:\n<user_query>\n{user_prompt}\n</user_query>",

)
except Exception:
logger.exception(
"Failed to generate webchat title for session %s", chatui_session_id
)
return
if llm_resp and llm_resp.completion_text:
title = llm_resp.completion_text.strip()
if not title or "<None>" in title:
Expand Down Expand Up @@ -813,7 +819,7 @@ def _apply_sandbox_tools(
req.func_tool.add_tool(PYTHON_TOOL)
req.func_tool.add_tool(FILE_UPLOAD_TOOL)
req.func_tool.add_tool(FILE_DOWNLOAD_TOOL)
req.system_prompt += f"\n{SANDBOX_MODE_PROMPT}\n"
req.system_prompt = f"{req.system_prompt or ''}\n{SANDBOX_MODE_PROMPT}\n"


def _proactive_cron_job_tools(req: ProviderRequest) -> None:
Expand Down
21 changes: 20 additions & 1 deletion astrbot/core/cron/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
from .manager import CronJobManager
"""Cron package exports.
Keep `CronJobManager` import-compatible while avoiding hard import failure when
`apscheduler` is partially mocked in test environments.
"""

try:
from .manager import CronJobManager
except ModuleNotFoundError as exc:
if not (exc.name and exc.name.startswith("apscheduler")):
raise

_IMPORT_ERROR = exc

class CronJobManager:
def __init__(self, *args, **kwargs) -> None:
raise ModuleNotFoundError(
"CronJobManager requires a complete `apscheduler` installation."
) from _IMPORT_ERROR


__all__ = ["CronJobManager"]
14 changes: 8 additions & 6 deletions tests/test_api_key_open_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from astrbot.dashboard.server import AstrBotDashboard


@pytest_asyncio.fixture(scope="module")
@pytest_asyncio.fixture(scope="module", loop_scope="module")
async def core_lifecycle_td(tmp_path_factory):
tmp_db_path = tmp_path_factory.mktemp("data") / "test_data_api_key.db"
db = SQLiteDatabase(str(tmp_db_path))
Expand All @@ -37,7 +37,7 @@ def app(core_lifecycle_td: AstrBotCoreLifecycle):
return server.app


@pytest_asyncio.fixture(scope="module")
@pytest_asyncio.fixture(scope="module", loop_scope="module")
async def authenticated_header(app: Quart, core_lifecycle_td: AstrBotCoreLifecycle):
test_client = app.test_client()
response = await test_client.post(
Expand Down Expand Up @@ -258,7 +258,7 @@ async def test_open_chat_sessions_pagination(
assert create_data["status"] == "ok"
raw_key = create_data["data"]["api_key"]

creator = "alice"
creator = f"alice_{uuid.uuid4().hex[:8]}"
for idx in range(3):
await core_lifecycle_td.db.create_platform_session(
creator=creator,
Expand All @@ -276,7 +276,8 @@ async def test_open_chat_sessions_pagination(
)

page_1_res = await test_client.get(
"/api/v1/chat/sessions?page=1&page_size=2&username=alice",
"/api/v1/chat/sessions?page=1&page_size=2&username="
f"{creator}",
headers={"X-API-Key": raw_key},
)
assert page_1_res.status_code == 200
Expand All @@ -286,10 +287,11 @@ async def test_open_chat_sessions_pagination(
assert page_1_data["data"]["page_size"] == 2
assert page_1_data["data"]["total"] == 3
assert len(page_1_data["data"]["sessions"]) == 2
assert all(item["creator"] == "alice" for item in page_1_data["data"]["sessions"])
assert all(item["creator"] == creator for item in page_1_data["data"]["sessions"])

page_2_res = await test_client.get(
"/api/v1/chat/sessions?page=2&page_size=2&username=alice",
"/api/v1/chat/sessions?page=2&page_size=2&username="
f"{creator}",
headers={"X-API-Key": raw_key},
)
assert page_2_res.status_code == 200
Expand Down
Loading