feat(workspace): scope-trust list for aspect loading#10347
Merged
davidfirst merged 13 commits intomasterfrom May 7, 2026
Merged
feat(workspace): scope-trust list for aspect loading#10347davidfirst merged 13 commits intomasterfrom
davidfirst merged 13 commits intomasterfrom
Conversation
The workspace can now declare which scopes it trusts when loading aspects (envs, generators, etc.). Effective trust = builtin (`teambit.*`, `bitdev.*`) + the owner of `defaultScope` + entries listed under `trustedScopes` in workspace.jsonc. When an aspect comes from a scope outside that list, an interactive shell prompts to extend the list; in non-interactive contexts it surfaces a clear error pointing to `bit scope trust <scope>` or workspace.jsonc. Adds: - `trustedScopes?: string[]` on the workspace config - `bit scope trust [pattern]` (lists effective trust when called bare) - `bit scope untrust <pattern>` - aspect-load hook on ScopeMain so scope-aspects-loader and workspace-aspects-loader honor the same gate
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a workspace-level “scope trust” policy that gates aspect loading (envs/generators/etc.) based on a computed allowlist of trusted scope patterns, with interactive prompting in TTY contexts and an actionable error in non-interactive runs. It integrates this guard into both the scope and workspace aspect-loader paths and adds CLI commands to manage the trust list in workspace.jsonc.
Changes:
- Adds a
ScopeMainpre-require()hook (aspectLoadGuard) and invokes it from bothscope-aspects-loaderandworkspace-aspects-loader. - Introduces workspace
trustedScopes?: string[]configuration plusbit scope trust/bit scope untrustcommands to manage it. - Adds an e2e test to verify that an env from an untrusted scope is not required (marker file not written).
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| scopes/workspace/workspace/workspace.main.runtime.ts | Wires ScopeTrust into ScopeMain and registers scope trust/untrust subcommands. |
| scopes/workspace/workspace/workspace-aspects-loader.ts | Calls the new aspectLoadGuard before requiring workspace-loaded aspects. |
| scopes/workspace/workspace/types.ts | Adds trustedScopes?: string[] to the workspace extension config interface/docs. |
| scopes/workspace/workspace/scope-trust/scope-trust.ts | Implements trust resolution, pattern matching, interactive TOFU prompt, and config persistence. |
| scopes/workspace/workspace/scope-trust/scope-trust.cmd.ts | Implements bit scope trust (add/list) and bit scope untrust (remove). |
| scopes/workspace/workspace/scope-trust/index.ts | Exposes the scope-trust module exports for runtime wiring. |
| scopes/scope/scope/scope.main.runtime.ts | Adds setAspectLoadGuard() / getAspectLoadGuard() to ScopeMain. |
| scopes/scope/scope/scope-aspects-loader.ts | Invokes the guard before requiring an aspect from a scope-aspects capsule. |
| e2e/harmony/scope-trust.e2e.ts | Adds an end-to-end test for refusing to load an env aspect from an untrusted scope. |
The aspect-load gate now runs only when `trustedScopes` is present in workspace.jsonc. Without that key, no gate and no prompts (default behavior). CLI consolidated to one command that takes an action positional: - bit scope trust # same as list - bit scope trust list # status; if on, the effective list - bit scope trust enable # opt-in (writes trustedScopes: []) - bit scope trust disable # opt-out (removes the key) - bit scope trust add <p> # add pattern (auto-enables) - bit scope trust remove <p> # remove pattern (does NOT auto-disable) `bit scope untrust` is gone (replaced by `bit scope trust remove`).
- support dotless scopes (legacy `defaultScope: my-scope`): both the auto-trust derivation and `bit scope trust add my-scope` now accept exact dotless names - preserve workspace.jsonc comments on add/remove/enable by using mergeIntoExisting (matching how setDefaultScope updates the same aspect) - bit scope trust: loadAspects=false, so the command stays usable as a recovery path when the workspace already references an aspect outside the trust list - e2e: assert the import error message contains the refusal text instead of swallowing all errors (avoids false positive on unrelated import failures)
- types.ts comment: point users at the actual command surface (`bit scope trust [enable|disable|add|remove]`) - prompt: say "Aspect ..." instead of "uses an env" since the gate fires for any aspect (envs, generators, etc.) - isValidPattern: disallow `/` in scope patterns (scope names don't contain slashes — that's the scope/component separator)
Code reuse: - isValidPattern delegates to isValidScopeName from @teambit/legacy-bit-id instead of a hand-rolled regex (also picks up the canonical character set) - list output uses formatSection / formatTitle / formatHint from @teambit/cli, matching the project's CLI style guide Code quality: - consolidate workspace.jsonc reads through one private readExt helper (was duplicated across 5 methods) - collapse addTrustedScope/removeTrustedScope into a single mutateConfiguredList helper that takes a list mutator - extract a deny() closure in createGuard so the same throw isn't repeated - drop unreachable default arm in the action switch (validated upfront) - extract requirePattern() helper for add/remove arg validation - narrow `as any` cast on enquirer prompt to Parameters<typeof prompt>[0] - drop unused TrustedScopesGroups public re-export - rename getOwnerWildcardFromDefaultScope -> getInferredOwnerPattern (the dotless branch returns an exact match, not a wildcard) UX polish: - listing's "configured" section now disappears cleanly when empty, with a separate hint instead of a "(1)"-counted placeholder e2e: - pull the duplicated env source string into a top-level constant
- action arg: replace "|" with "," so the CLI docs Markdown table doesn't break on pipe characters - extendedDescription: replace "<p>" with "PATTERN" so MDX doesn't parse it as an unclosed JSX/HTML tag
add learn-bit-react.*, bitdesign.*, and frontend.* to the auto-trusted builtin scopes so users importing common community/design components don't have to opt-in those scopes manually.
scope-trust adds 3 new files (workspace/dist/scope-trust/*) loaded during bit-bootstrap. snapshot refresh is left for a follow-up PR.
pattern is typed string|undefined; passing it directly to chalk.bold relied on a runtime guarantee. assigning the requirePattern() result to a local makes both the mutation and the success-message use the narrowed string type.
GiladShoham
approved these changes
May 7, 2026
isValidPattern now rejects multi-dot wildcards like `acme.frontend.*`. The matcher only consults scope owners, so anything beyond a single owner segment can't match a real Bit scope; reject up front rather than persist a pattern that will never apply.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Opt-in workspace policy. When the workspace declares
trustedScopesin workspace.jsonc, Bit checks the scope of every aspect (env, generator, etc.) before loading it. Without that key, behavior is unchanged — anything loads.Once enabled, the effective trust set is:
teambit.*,bitdev.*,learn-bit-react.*,bitdesign.*,frontend.*(the bit.cloud orgs that ship official + community-blessed components most users already pull from)defaultScope(e.g.acme.frontend→acme.*)trustedScopesin workspace.jsoncIf an aspect's scope isn't on that list, an interactive shell prompts the user to add it; non-interactive contexts (CI) get an error pointing at
bit scope trust add <scope>or workspace.jsonc.Surface
trustedScopes?: string[]onteambit.workspace/workspacebit scope trust [action] [pattern]— single command with action positionallist(default) — status; if on, the effective listenable— opt-in (writestrustedScopes: [])disable— opt-out (removes the key)add <pattern>— add pattern (auto-enables)remove <pattern>— remove pattern (does NOT auto-disable on empty)ScopeMain; bothscope-aspects-loaderandworkspace-aspects-loaderhonor it beforerequire()-ing aspects