Skip to content

fix: address Claude plugin directory review findings - #87

Open
tnj wants to merge 3 commits into
mainfrom
fix/claude-directory-review
Open

tnj wants to merge 3 commits into
mainfrom
fix/claude-directory-review

Conversation

@tnj

@tnj tnj commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Summary

Fix the findings from the Claude plugin directory review of abc472f, so the plugin can be resubmitted with plugin as the plugin path.

That review ran against the repository root, which has .claude-plugin/marketplace.json but no plugin.json ("Couldn't confirm what the plugin runs or connects to"). With plugin as the plugin path, the pre-submission checklist would also block on the inline node -e launcher and the missing README in the plugin folder. This PR fixes those too.

The directory doesn't need a dedicated repository: "If the plugin is one folder in a larger repository, give that folder as the plugin path" (Submit your plugin). So the plugin stays in plugin/.

MCP server launch

  • plugin/.mcp.json now runs node ${CLAUDE_PLUGIN_ROOT}/scripts/bundle.js. The directory blocks inline programs such as node -e when the plugin is a subfolder of the repository.
  • Codex doesn't expand ${CLAUDE_PLUGIN_ROOT} (openai/codex#19582). So plugin/.codex-plugin/plugin.json now points to a new plugin/.codex-mcp.json, which runs node ./scripts/bundle.js with cwd: ".". That is the same resolution the old resolver used on Codex after 8cd00db.

Skill allowed-tools

  • MCP tool names now carry the plugin scope (mcp__plugin_deploygate_deploygate__<tool>). The unscoped mcp__deploygate__* names never matched the tools of the installed plugin, so those pre-approvals had no effect.
  • No MCP wildcard. With working names, setup's wildcard would pre-approve all 58 tools, including delete_project and update_saml_certificate. setup now names its Phase 1 onboarding tools only. Tools that invite people or grant app access (add_member, create_shared_team, assign_shared_team_to_app) keep a per-call approval.
  • Removed broad shell access, which held the version for review: ./gradlew, xcodebuild, fastlane, plus cp, cd, zip, and mkdir in setup. Users now approve these commands when the skill runs them. which, ls, git rev-parse, and security find-identity stay.
  • Scoped file edits. In ci-setup, Write Edit became Edit(.github/workflows/**) Edit(bitrise.yml) Edit(.circleci/config.yml). In sdk-setup, Edit became Edit(build.gradle), Edit(build.gradle.kts), Edit(settings.gradle), and Edit(settings.gradle.kts). Claude Code checks only Edit(path) rules for writes, and ignores path rules on Write (permissions).

Listing assets

  • New plugin/README.md. The directory uses it as the listing description, and it must be in the plugin folder. It covers install, the skills, and what the plugin runs, sends (only https://deploygate.com), stores (the device-flow token), and reads on request (the keystore and passwords given to update_keystore). It also explains how scripts/bundle.js is built and checked.
  • New plugin/.claude-plugin/icon.png (512×512 PNG). The directory takes the listing icon from the first save or submission, so it has to be on main before the new submission.

Tests and docs

  • skills.test.ts checks the allowed-tools constraints for every skill. Pre-approved MCP tools must be in an explicit allowlist (PRE_APPROVABLE_MCP_TOOLS), so wildcards and destructive or access-granting tools fail the test.
  • plugin.test.ts checks both MCP configs, the README word count, and the icon dimensions.
  • CLAUDE.md documents the two MCP configs and the allowed-tools rules.
  • The root README.md now describes the token path accurately (XDG_CONFIG_HOME is honored, and 0600 applies on macOS and Linux only).

Findings left for the reviewer

  • scripts/bundle.js. The bundle is over the 256 KiB per-file limit, and the security scan flagged it as code it can't read. A Node server in a subfolder is also always held ("Scripts the validator couldn't follow"). The README now explains how the bundle is built.
  • "Uses a credential from the user's machine". Each case is a DeployGate token sent only to deploygate.com, which the checklist lets a reviewer confirm. The token in plugin.json is the plugin's own device-flow token. The references/ and templates/ cases are CI secrets on the user's CI provider.
  • Local stdio MCP not available on claude.ai. This is informational.

Test plan

  • npm run build && npm test (489 tests pass)
  • claude plugin validate ./plugin and claude plugin validate . pass
  • scripts/smoke-bundle.mjs passes when the bundle is started by absolute path (Claude) and by ./scripts/bundle.js from plugin/ (Codex)
  • Install from this branch in Claude Code and confirm that the MCP server starts and the skills' MCP calls are pre-approved
  • Install from this branch in Codex and confirm that the MCP server starts
  • After merge, withdraw the current submission, submit again with plugin path plugin, and run Validate

🤖 Generated with Claude Code

Prepare the plugin folder for resubmission to the Claude plugin
directory with `plugin` as the plugin path.

- Start the MCP server with `node ${CLAUDE_PLUGIN_ROOT}/scripts/bundle.js`
  in plugin/.mcp.json. The directory blocks inline programs such as
  `node -e` when the plugin is a subfolder of the repository. Codex does
  not expand ${CLAUDE_PLUGIN_ROOT}, so it gets its own
  plugin/.codex-mcp.json that runs the bundle relative to the plugin root.
- Name MCP tools in skill allowed-tools with the plugin scope
  (mcp__plugin_deploygate_deploygate__*). The unscoped names never
  matched, so the pre-approvals had no effect.
- Stop pre-approving build tools (./gradlew, xcodebuild, fastlane) and
  file-mutating shell commands, which the directory holds as broad shell
  access. Limit file edits to Edit(<path>) rules for CI configs and
  Gradle build scripts.
- Add plugin/README.md for the listing, describing what the plugin runs,
  sends, and stores, and how bundle.js is built.
- Add the listing icon at plugin/.claude-plugin/icon.png.
- Add tests that keep these invariants.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 9, 2026 02:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The setup wildcard pre-approves unrelated destructive MCP operations, and the credential-storage disclosure is inaccurate.

2 open findings
What changed in this PR

Updates the published plugin folder to satisfy Claude directory requirements while preserving Codex compatibility.

Changes:

  • Splits Claude and Codex MCP launch configurations.
  • Narrows shell/file permissions and corrects scoped MCP names.
  • Adds listing assets, documentation, and validation tests.
File Description
CLAUDE.md Documents plugin configuration and permission rules.
plugin/​.claude-plugin/​icon.png Adds the validated 512×512 listing icon.
plugin/​.codex-mcp.json Adds Codex-specific MCP launch configuration.
plugin/​.codex-plugin/​plugin.json Selects the Codex MCP configuration.
plugin/​.mcp.json Uses Claude’s plugin-root variable.
plugin/​README.md Adds installation, security, and runtime disclosures.
plugin/​skills/​setup/​SKILL.md Updates setup permissions and MCP scope.
plugin/​skills/​deploy/​SKILL.md Removes pre-approved build commands.
plugin/​skills/​ci-setup/​SKILL.md Restricts edits to CI configuration files.
plugin/​skills/​sdk-setup/​SKILL.md Restricts edits to Gradle files.
src/​__tests__/​plugin.test.ts Validates MCP configurations and listing assets.
src/​__tests__/​skills.test.ts Validates scoped skill permissions.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread plugin/skills/setup/SKILL.md Outdated
Comment thread plugin/README.md Outdated
Scoping the MCP tool names turned the setup skill's wildcard, which
never matched before, into a pre-approval of every tool the server
exposes, including delete_project, delete_keystore, remove_* and
update_saml_certificate. Name the Phase 1 onboarding tools instead, and
leave tools that invite people or grant app access (add_member,
create_shared_team, assign_shared_team_to_app) to a per-call approval.
A new test rejects MCP wildcards and destructive or access-granting
tools in every skill's allowed-tools.

Also describe the token path accurately in both READMEs: it honors
XDG_CONFIG_HOME, and the 0600 mode applies on macOS and Linux only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 9, 2026 02:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The security disclosure omits keystore credentials, and the policy test permits an access-granting tool it intends to reject.

1 open finding
2 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Low severity Disclose transient keystore credentials read by update_keystore

plugin/​README.md:39

The credential disclosure is incomplete: update_keystore reads a local signing keystore and accepts its keystore/key passwords (src/tools/keystores.ts:33-48), so the statement that no other credentials are read is false. This is material to the directory security review; disclose these transient credentials and clarify that they are sent to DeployGate but not persisted.

🧠 Review effort: Balanced

Comment thread src/__tests__/skills.test.ts Outdated
The policy test matched destructive tool names with a pattern, which
let create_shared_team through although the setup skill leaves it to a
per-call approval. Check every pre-approved MCP tool against an explicit
allowlist instead, so any other tool, including ones added to the
server later, needs a deliberate change to be pre-approved.

The plugin README said the plugin reads no credentials besides its
token, but update_keystore reads the keystore file and passwords the
user gives it and sends them to DeployGate. Disclose that, and that
nothing is stored locally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 9, 2026 02:28
@tnj

tnj commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

@copilot On the previously missed finding about update_keystore (plugin/README.md): fixed in a8dfd3f. The README now says that update_keystore reads the keystore file at the path you give it and takes the keystore and key passwords you provide, sends them to https://deploygate.com, and doesn't store them locally. Keystores are also listed among the data sent to DeployGate.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The shell permission guard remains bypassable, and the PNG validation does not verify a valid PNG structure.

1 open finding
1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Validate the complete PNG signature and IHDR chunk

src/​__tests__/​plugin.test.ts:195

This checks only bytes 1–3, so a non-PNG file with arbitrary byte 0 and PNG next would pass and the offsets below could be interpreted as dimensions without an IHDR chunk. Validate the complete eight-byte PNG signature and the first chunk type so this test actually protects the listing asset.

🧠 Review effort: Balanced

Comment on lines +248 to +250
expect(tool).not.toMatch(
/^Bash\((\.\/|gradlew|fastlane|xcodebuild|cp|cd|mkdir|zip|rm|mv)\b/,
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants