Skip to content

skills: inline @mention injects a denied skill's body without a permission check #49891

Open

Description

@zzacong

Summary

A skill denied via permissions is still injected into the session when referenced with an @<skill-id> mention. The mention path loads the skill body directly and does not consult the skill permission.

Environment

  • opencode version: 2.0.8
  • OS: Darwin 25.6.0 (darwin arm64)
  • Terminal: Ghostty, xterm-256color, truecolor
  • Shell: /bin/zsh
  • Install/channel: release/latest
  • Active plugins: none

Reproduction

  1. Configure:

    { "permissions": [ { "action": "skill", "resource": "create-plan", "effect": "deny" } ] }
  2. Start a session and send @create-plan.

  3. The full SKILL.md body is injected into the conversation despite the deny.

  4. Control: loading the same skill via the skill tool returns {"type":"permission.rejected","message":"Permission denied: skill"}.

Expected Behavior

Per the docs, a deny effect "Hides matching skills from the model and rejects loading." An explicit mention should not bypass it, or the docs should define explicit user activation as a separate source/permission action.

Actual Behavior

The mention injects the skill body with no permission check. This was reproduced after a full client restart.

Additional Context

Activity

  1. holny commented on Sep 19, 2026

    @holny
    Contributor

    Reproduced the same gap — in packages/core/src/session/prompt.ts the mention-sourced skills attachments are resolved straight through skillService.get() + Skill.prepare() (around line 56-71) with no permission consultation, while the skill tool path properly returns permission.rejected. So a denied skill can be pulled into context just by mentioning it.

    Happy to fix this: route mention-sourced skills through the same permission check the tool uses (reject denied ones instead of silently injecting the body), plus a regression test for the deny + @mention combo.

  2. pablokan commented on Oct 9, 2026

    @pablokan

    Independent reproduction on v2.0.26 (release/latest, Linux), with a different config shape than the OP: the deny is set in an agent's frontmatter, not root permissions.

    ~/.config/opencode/agents/klean.md:

    permissions:
      - { action: "*", resource: "*", effect: ask }
      - { action: "skill", resource: "*", effect: deny }

    opencode.json sets "default_agent": "klean".

    Reproduction: new session in an empty directory, send @teach (also reproduced with @opencode). The full body is injected despite deny.

    Evidence from the session DB (~/.local/share/opencode/opencode.db, table session_message, agent klean):

    • user message: "text":"@teach oh my pi" + "skills":[{"id":"teach","name":"teach","text":"<skill_content name=\"teach\">…"}]
    • no skill tool call anywhere in the session.

    So the mention path injects the body into the user message with no permission consult — consistent with the root-cause note above.

    Extra surface worth noting: the /skills selector lists the full discovered catalog, unfiltered by the skill permission — denied skills (and skills with metadata.opencode/autoinvoke: false) are still listed and selectable. So under deny, the model can't initiate a skill, but a user @mention or a selector pick still lands the body in context.

    Still reproduces as of 2026-10-09, so the fix isn't in latest yet.

  3. opencodejr commented on Oct 28, 2026

    @opencodejr
    Contributor

    Thanks for the detailed report!

    In v2 this is currently intentional, not an accidental bypass. A skill permission governs the model's skill tool: what's advertised to the model and what it can load. An explicit @skill-id mention adds the skill's instructions directly to your prompt and never goes through that tool. This was changed in 2.0.0 in response to #43872, which asked for the opposite of what you're asking: that users can still invoke skills that are denied to the model. The docs page says "To load a skill yourself, mention it in your prompt as @skill-id", but you're right that the permissions table's "rejects loading" doesn't say whether that covers user mentions.

    I'll treat this as a feature request: a way for deny (or a separate permission action) to also block a user's explicit @ activation of a skill, and clearer docs on how mentions and the skill permission interact. Maintainers will consider it.

    For now, the only way to make @create-plan unavailable is to remove the skill from your skill sources. A plugin can also do this by dropping it via ctx.skill.transform, which removes it for both you and the model.

    Related:

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions