Skip to content

fix: disable workspace skills in group sessions#9082

Open
LIghtJUNction wants to merge 2 commits into
masterfrom
codex/fix-vulnerability-in-workspace-skills
Open

fix: disable workspace skills in group sessions#9082
LIghtJUNction wants to merge 2 commits into
masterfrom
codex/fix-vulnerability-in-workspace-skills

Conversation

@LIghtJUNction

@LIghtJUNction LIghtJUNction commented Jun 30, 2026

Copy link
Copy Markdown
Member

Motivation

  • Prevent a privilege-boundary vulnerability where non-admin users can plant skills/<name>/SKILL.md in a shared group workspace and have those workspace skills injected into later admin requests when computer_use_runtime is local.

Description

  • Restrict request-scoped workspace skill discovery in astrbot/core/astr_main_agent.py to only run when runtime == "local" and the event is not a group session by checking not event.get_group_id().
  • Add a regression test in tests/unit/test_astr_main_agent.py that creates a workspace skill in a group-session workspace and asserts it is not injected into the system prompt.
  • Files changed: astrbot/core/astr_main_agent.py, tests/unit/test_astr_main_agent.py.

Testing

  • Ran ruff format and ruff check against the modified files and the checks passed.
  • Added a unit test that verifies workspace skills are skipped for group sessions; running it via pytest -k 'workspace_skills' was attempted but blocked by dependency fetch/network tunnel errors in the environment.
  • Attempts to run uv run ruff format and uv run pytest were also blocked by the same dependency fetch/network tunnel issue, so full CI-style execution could not complete in this environment.

Codex Task

Summary by Sourcery

Guard workspace skill discovery against group sessions to avoid injecting group-scoped skills into admin/local requests.

Bug Fixes:

  • Prevent workspace skills defined in shared group workspaces from being injected into system prompts when the runtime is local.

Tests:

  • Add a unit test ensuring workspace skills are skipped for group sessions and not injected into the system prompt.

@dosubot dosubot Bot added size:XS This PR changes 0-9 lines, ignoring generated files. area:core The bug / feature is about astrbot's core, backend labels Jun 30, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • Instead of directly checking not event.get_group_id(), consider centralizing group-session detection behind a helper (e.g., event.is_group_session() or similar) so this privilege boundary logic stays aligned with other parts of the codebase that distinguish group vs. non-group contexts.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Instead of directly checking `not event.get_group_id()`, consider centralizing group-session detection behind a helper (e.g., `event.is_group_session()` or similar) so this privilege boundary logic stays aligned with other parts of the codebase that distinguish group vs. non-group contexts.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request disables workspace-scoped skills in group sessions when running on a local runtime to prevent privilege-boundary vulnerabilities, and adds a corresponding unit test. The review feedback suggests extending this security measure to also disable loading EXTRA_PROMPT.md in group sessions to prevent potential prompt injection attacks. Additionally, it is recommended to enhance the new unit test to verify that global-scoped skills are still correctly loaded and not overridden by workspace-scoped skills.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread astrbot/core/astr_main_agent.py Outdated
workspace_skills = (
skill_manager.list_workspace_skills(
workspace_skills = []
if runtime == "local" and not event.get_group_id():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

security-high high

While disabling workspace-scoped skills in group sessions is an excellent security improvement to prevent privilege-boundary vulnerabilities, a similar vulnerability exists with EXTRA_PROMPT.md.

In _apply_workspace_extra_prompt (called during _decorate_llm_request), the file EXTRA_PROMPT.md is loaded from the workspace path and appended to the system prompt. Since group sessions share the same workspace directory (derived from event.unified_msg_origin), any non-admin user in the group could plant or modify EXTRA_PROMPT.md to perform a prompt injection attack against other users (including admins) in the same group.

To fully mitigate this privilege-boundary vulnerability, please consider also disabling EXTRA_PROMPT.md loading for group sessions. For example, you can update _apply_workspace_extra_prompt as follows:

def _apply_workspace_extra_prompt(
    event: AstrMessageEvent,
    req: ProviderRequest,
) -> None:
    if event.get_group_id():
        return

    extra_prompt_path = _get_workspace_path_for_umo(event.unified_msg_origin) / (
        "EXTRA_PROMPT.md"
    )
    # ... rest of the function

Comment thread tests/unit/test_astr_main_agent.py Outdated
Comment on lines +898 to +931
workspace_skill_dir = workspace_root / "skills" / "workspace-skill"
workspace_skill_dir.mkdir(parents=True)
workspace_skill_dir.joinpath("SKILL.md").write_text(
"---\ndescription: Workspace scoped skill.\n---\n",
encoding="utf-8",
)

monkeypatch.setattr(
module,
"get_astrbot_workspaces_path",
lambda: str(workspaces_dir),
)
monkeypatch.setattr(
"astrbot.core.skills.skill_manager.get_astrbot_data_path",
lambda: str(data_dir),
)
monkeypatch.setattr(
"astrbot.core.skills.skill_manager.get_astrbot_skills_path",
lambda: str(global_skills_dir),
)
monkeypatch.setattr(
"astrbot.core.skills.skill_manager.get_astrbot_plugin_path",
lambda: str(plugins_dir),
)

req = ProviderRequest()
req.conversation = MagicMock(persona_id=None)

await module._ensure_persona_and_skills(
req, {"computer_use_runtime": "local"}, mock_context, mock_event
)

assert "Workspace scoped skill." not in req.system_prompt
assert "## Skills" not in req.system_prompt

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The current test asserts that "Workspace scoped skill." and "## Skills" are not in the system prompt when workspace skills are skipped. However, this doesn't verify that global/plugin skills are still correctly loaded and not accidentally broken or overridden in group sessions.

To make this regression test more robust, we should also set up a global skill (similar to test_ensure_skills_includes_workspace_skills) and assert that:

  1. The global skill is loaded and present in the system prompt.
  2. The workspace skill (even if it has the same name as the global skill) is not loaded and does not override the global skill.
        global_skill_dir = global_skills_dir / "workspace-skill"
        global_skill_dir.mkdir(parents=True)
        global_skill_dir.joinpath("SKILL.md").write_text(
            "---\ndescription: Global scoped skill.\n---\n",
            encoding="utf-8",
        )

        workspace_skill_dir = workspace_root / "skills" / "workspace-skill"
        workspace_skill_dir.mkdir(parents=True)
        workspace_skill_dir.joinpath("SKILL.md").write_text(
            "---\ndescription: Workspace scoped skill.\n---\n",
            encoding="utf-8",
        )

        monkeypatch.setattr(
            module,
            "get_astrbot_workspaces_path",
            lambda: str(workspaces_dir),
        )
        monkeypatch.setattr(
            "astrbot.core.skills.skill_manager.get_astrbot_data_path",
            lambda: str(data_dir),
        )
        monkeypatch.setattr(
            "astrbot.core.skills.skill_manager.get_astrbot_skills_path",
            lambda: str(global_skills_dir),
        )
        monkeypatch.setattr(
            "astrbot.core.skills.skill_manager.get_astrbot_plugin_path",
            lambda: str(plugins_dir),
        )

        req = ProviderRequest()
        req.conversation = MagicMock(persona_id=None)

        await module._ensure_persona_and_skills(
            req, {"computer_use_runtime": "local"}, mock_context, mock_event
        )

        assert "Global scoped skill." in req.system_prompt
        assert "Workspace scoped skill." not in req.system_prompt

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 30, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
astrbot-docs 17505df Commit Preview URL

Branch Preview URL
Jun 30 2026, 06:20 AM

@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels Jun 30, 2026
@Soulter

Soulter commented Jun 30, 2026

Copy link
Copy Markdown
Member

Would a more reasonable approach be to prohibit non-admiins from creating skills in the workspace?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

aardvark area:core The bug / feature is about astrbot's core, backend codex size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants