Skip to content

Preserve native semantics in plugin links - #2094

Merged
ymichael merged 4 commits into
mainfrom
bb/post-merge-fix-plugin-links-violate-native-ancho-thr_wq8j2hq2db
Aug 21, 2026
Merged

Preserve native semantics in plugin links#2094
ymichael merged 4 commits into
mainfrom
bb/post-merge-fix-plugin-links-violate-native-ancho-thr_wq8j2hq2db

Conversation

@ymichael

@ymichael ymichael commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

What was wrong

The new host-rendered plugin link components claimed ordinary activation without fully preserving native anchor semantics at their boundary. ExperimentalFileLink exposed the untrusted file path directly as href even though valid relative file names may begin with strings such as vscode: or mailto:, so native or modifier activation could launch an external scheme; a target rejected by runtime validation still kept an active href. ExperimentalUrlLink did not exclude anchors with an explicit target, and its RouteAnchor wrapper also stripped explicit targets from internal routes, allowing the app navigation host or SPA router to override the requested native browsing context.

Review found three related gaps: custom named targets could create a browsing context with live window.opener access, the public SDK test link did not apply production file-target validation, and the validator accepted unpaired UTF-16 surrogates that made encodeURIComponent throw during render.

What changed

  • Derive file-link hrefs only from the normalized target, percent-encode the complete path behind a fixed ./ prefix, and omit href when runtime validation rejects the target. The shared validator now rejects unpaired surrogates before encoding. Ordinary valid activation still goes to the shared file-preview host, while modifier, download, copied-href, keyboard, and accessible-name behavior remains anchor-native and scheme-safe.
  • Treat every explicit URL target as browser-owned. Targeted links render as direct anchors so internal routes retain their target too; untargeted app routes continue using RouteAnchor. _blank and custom named targets preserve supplied rel tokens while adding noopener noreferrer unless opener is explicit; same-context targets are unchanged.
  • Move file-target validation into one internal SDK module used by both production and the public plugin test runtime, so malformed targets are inert and record no navigation in either surface. Click ownership remains explicit in each component; no shared click helper was added.
  • Document the boundary rules in the SDK contract, README, experimental API audit, template plugin guide, and built-in plugin-authoring skill.
  • Update the GitHub Markdown, Connect dashboard, and Tasks pull-request consumer regressions to assert their intentional _blank links remain native instead of expecting the test host to claim them.

This is the smallest complete fix because it changes only href construction, validation, and target ownership at the two link boundaries; it shares the existing validator only where production and the SDK harness must be identical, and reuses the existing file, URL, and route hosts without a compatibility path or click-helper abstraction. There is no server/daemon wire change and no HOST_DAEMON_PROTOCOL_VERSION bump.

How you verified

  • Before the implementation, the focused app regressions failed 6 assertions: the scheme-like href stayed vscode:foo, the malformed target stayed an active link, all tested explicit external targets were claimed by openUrl, and an internal _blank target was stripped.
  • Before the consumer-test corrections, the focused Connect test failed 1/16 and the focused Tasks test failed 1/3 because each expected host navigation from an explicit _blank link; after correction both passed in full.
  • Before the review fixes, named targets had no safe rel, the SDK traversal target remained an active ./..%2Fsecret link, and unpaired surrogates threw URIError: URI malformed in both production and the harness. A separate regression proved an explicit nofollow token also needed the safe tokens merged rather than replacing them.
  • pnpm exec turbo run test --filter=@bb/app --force -- src/components/plugin/ExperimentalFileLink.test.tsx src/components/plugin/ExperimentalUrlLink.test.tsx src/lib/live-file-navigation.test.ts — 3 files / 24 tests passed after the final rebase.
  • pnpm exec turbo run test --filter=@get-bb/plugin-sdk --force -- src/testing/__tests__/app-harness.test.tsx — 1 file / 43 tests passed after the final rebase.
  • pnpm exec turbo run test --filter=bb-plugin-github --force — 6 files / 24 tests passed after reproducing the stale Markdown expectation locally.
  • pnpm exec turbo run test --filter='!@bb/server' --filter='!@bb/app' --filter='!@bb/integration-tests' --force — the exact CI Packages shard passed 66/66 Turbo tasks after the final rebase.
  • pnpm exec turbo run test --filter=@bb/app --force — 413 files / 3,182 passed / 3 skipped.
  • pnpm exec turbo run test --filter=@get-bb/plugin-sdk --force — 14 files / 117 tests passed, including package-export and portable bundled-type checks.
  • pnpm exec turbo run build typecheck --filter=@bb/app --filter=@get-bb/plugin-sdk --filter=@bb/server --filter=@bb/templates --force — 11/11 Turbo tasks passed after the final rebase.
  • pnpm exec turbo run lint --filter=@bb/app --filter=@get-bb/plugin-sdk --filter=@bb/server --filter=@bb/templates --force — passed with 0 errors (155 warning-only findings outside the changed files).
  • git diff --check origin/main...HEAD — passed.

No linked issue.

AGENT GENERATED: by GPT-5.6-Sol

@bb-slop-cop

bb-slop-cop Bot commented Aug 20, 2026

Copy link
Copy Markdown

🚨 SLOP COP 🚨 · review

I am SlopCop. I am reviewing this pull request for security, code quality, performance, architecture, and product behavior.

Comment thread apps/app/src/components/plugin/ExperimentalUrlLink.tsx Outdated
Comment thread packages/plugin-sdk/src/testing/app.tsx
Comment thread packages/plugin-sdk/src/testing/app.tsx Outdated
Comment thread apps/app/src/components/plugin/ExperimentalFileLink.tsx

@bb-slop-cop bb-slop-cop Bot 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.

🚨 SLOP COP 🚨 · review

Plain English summary

This PR makes plugin links act more like normal browser links. It encodes file paths and keeps explicit URL targets under browser control.

The direction is good. I found two merge blockers and two important contract gaps.

Findings

  • Medium security: A custom named target receives window.opener. The opened site can replace the BB page. Default each new top-level target to noopener noreferrer.
  • Merge blocker: The package CI job fails. The GitHub plugin uses target="_blank", but its test still expects BB host routing.
  • Medium: The public SDK test runtime accepts malformed file targets. The app makes the same targets inactive. Plugin tests can now report false success.
  • Medium: encodeURIComponent throws for an unpaired UTF-16 surrogate. The current validator accepts that string, so a plugin can crash its slot.
  • Low: Two agent guides still describe the old rules. Update packages/templates/src/templates/bb-guide-plugins.md and the built-in plugin authoring skill.

Architecture and performance

I found no second link implementation. Primary-click checks repeat in the app and SDK test runtime.

A small helper for button and modifier checks can reduce drift. Keep each target policy explicit because URL, file, and route links differ.

I found no meaningful performance regression. The change adds one path encoding per render and one attribute read per click.

Verification

  • The two app test files passed all 10 tests.
  • The SDK harness passed all 41 tests.
  • The GitHub plugin passed 23 tests and failed one related test.
  • The package CI job failed with the same assertion.
  • I started the pull-request app and tested the real components with Doobie.
  • Ordinary URL and file clicks reached their hosts. Valid file hrefs used safe encoding. Malformed file links had no href.
  • A custom target opened a named window with live access to the BB window.

I left the GitHub review state unchanged.

@ymichael
ymichael force-pushed the bb/post-merge-fix-plugin-links-violate-native-ancho-thr_wq8j2hq2db branch 2 times, most recently from 2223672 to e2cd712 Compare August 20, 2026 23:15
@ymichael
ymichael force-pushed the bb/post-merge-fix-plugin-links-violate-native-ancho-thr_wq8j2hq2db branch from e2cd712 to 0524b30 Compare August 20, 2026 23:48
@ymichael
ymichael merged commit 63162f8 into main Aug 21, 2026
13 checks passed
@ymichael
ymichael deleted the bb/post-merge-fix-plugin-links-violate-native-ancho-thr_wq8j2hq2db branch August 21, 2026 00:02
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.

1 participant