Repository navigation
fix(cli): neutralise plugin manifest text in boss plugin validate and link - #1001
Antriksh1984 wants to merge 2 commits into
Conversation
bdcf620 to
bc9aa3b
Compare
… link boss plugin validate and boss plugin link printed a plugin's own manifest fields (id, display name, version, entrypoint) raw, ahead of the host ever loading the plugin. A malicious or malformed plugin.json could therefore put a terminal escape or a forged line in the CLI's own output before any of the host's existing manifest checks run. Routed those fields through TerminalText.safe, reusing the same helper the status/doctor/mcp CLI hardening added.
630c731 to
e5ab2b7
Compare
|
@claude review this PR - security-sensitive changes, part of the backlog sweep. Focus on auth, signing, path handling, secrets, and injection surfaces. |
|
Claude finished @fluck-boss's task in 3m 41s —— View job Security review - PR #1001 (
|
The one raw line left in the link command: the filename comes from listing build/libs inside the cloned plugin repo, so ESC/CR/LF in it are attacker-chosen - on precisely the branch a hostile plugin lands. It now goes through formatErrorLine like every other error path.
|
"Verified finding 1 against the real code - CONFIRMED and fixed (57ea2b3). BossPluginCommand.kt:220 printed the invalid-plugin header raw, and the filename is discovered inside the cloned plugin repo, so ESC/CR/LF in it are attacker-chosen - on exactly the branch a hostile plugin lands. It now goes through formatErrorLine (TerminalText.safe) like every other error path, with a test pinning the neutralisation. #2 (the validate commands own-argument line) is a fair cheap wrap but weaker - left for the contributor. #3 (astral Cf in TerminalText) is a real text-hiding gap, no terminal control - follow-up sized." |
|
@claude please review this exact head (57ea2b3) for merge into dev. Check that the change is correct and complete for its stated goal, that tests cover the risk being fixed (a test that passes without the fix is a finding), and flag any security issues (secrets, injection, auth or approval bypass, path handling). Keep the review focused on this diff. |
|
Claude finished @fluck-boss's task in 5m 19s —— View job Review of
|
fluck-boss
left a comment
There was a problem hiding this comment.
Thanks @Antriksh1984 - neutralising the plugin manifest text is the right fix. Two points from Claude's review of 57ea2b3 that I checked:
- The escaped
echois undercut by a rawlogger.errorjust above it that still logs the attacker-chosen text. Log the neutralised string (or run it throughLogSanitizer) - the log is read in terminals too. - The head commit's test passes without the head commit's fix:
formatErrorLinealready behaved this way in the parent commit, and the test never goes throughBossPluginCommand. Drive the test through the command path atBossPluginCommand.kt:223so it fails if that call reverts to raw interpolation.
Changes requested + draft until then. Push, get the ubuntu and windows legs green, mark it ready for review, and it goes straight back into the merge queue.
924c565 to
7e62348
Compare
072a2bd to
2ae2565
Compare
Stacked on #996 (which is stacked on #995; it introduces
TerminalText). Until those land the diff also shows their commits; this PR is the last two.Summary
Third and last of the
TerminalTextsweep (#995 the MCP ledger, #996status/doctor/mcp list). This one isboss plugin, and it is the sharpest of the three, because validating a plugin you did not write is the whole point ofboss plugin validate, and its output quotes that plugin's own files:plugin.jsonis reported asMalformed plugin.json: <exception message>, and kotlinx's exception messages quote the offending JSON.boss plugin linkprints the plugin id from the manifest.reloadDevPluginrefuses an id containing control characters, and its refusal message quotes the id it just refused - so the one input that trips the guard is the one printed raw.Printed raw, a hostile manifest writes escape sequences to the developer's terminal (clear the screen over the
[x]lines, retitle the window, OSC 52 clipboard write) and can start lines of its own that read as passing checks.Fix
Every string that comes from the plugin under inspection goes through
TerminalText: a check's name and message, error messages, the plugin id in the "staged" messages, and the reload result. The check and error lines move into two small internal formatters (formatCheckLine,formatErrorLine) so they are testable; that extraction is its own behavior-neutral commit ahead of the fix. JSON output is unchanged.Tests
Test-first: 3 of the 4 new
PluginCommandTerminalSafetyTestcases fail on the unfixed formatters and pass after; the fourth pins that a null message still readsError: null.[ok] Validation passedline,plugin.jsonis run through the realPluginValidator, and every printed check is exactly one line with no control characters.All of
ai.rever.boss.cli.*passes;ktlintCheckanddetektclean. Control characters are built from code points in the test source, not embedded in it.