Conversation
…ry namespace
Lightning Message Service is a browser-side bus with no server mediation. A
channel with isExposed=true is published to components in any namespace,
including every installed managed package, and any component sharing a page with
a publisher or subscriber can subscribe to read every payload or publish one of
its own. Sharing rules, org-wide defaults and field-level security govern records
and do not reach it, so whatever the channel carries is readable regardless of
what the reader is entitled to see.
The matching risk is the inverse: a subscriber that treats the payload as trusted
input, taking a record id from a message and passing it to Apex, or rendering it
into the page. The bus is shaped like a trust boundary and is not one.
Three things from the platform docs shape the finding, and none of them are
obvious from the setting alone:
- Salesforce's own AppExchange Security Review requires isExposed=false, so
this is a defect by the platform owner's definition, not only ours.
- isExposed cannot be set back to false once true. Remediation is therefore a
new channel plus migrating every publisher and subscriber, not a setting
change, and the remediation text says so rather than implying a toggle.
- Visualforce supports only exposed channels. An org using LMS from a
Visualforce page has an exposed channel because the platform requires one,
not because anyone chose it, so "just turn it off" would be wrong advice.
Installed channels are reported separately at LOW. The reach is identical to a
local exposed channel, but since the exposure cannot be reversed and the metadata
is not editable here, there is no fix available in this org — only a question for
the vendor. Reporting them together would hand a reader a remediation they cannot
apply.
Queryability was verified against a developer edition before the check was
written rather than assumed: LightningMessageChannel is Tooling-queryable,
IsExposed is a selectable and filterable boolean, and ManageableState is what
separates local from installed. No MetadataClient fallback is needed, and the
check costs one Tooling query.
Deliberately grants no capability in CapabilityRegistry. An exposed channel is a
surface, not reach: nothing in the metadata says what the channel carries, so
granting data-read would assert an exposure that has not been established.
Wiring it into a chain as a step is a separate judgment and is not made here.
Scope note: the check does not yet cross-reference which Visualforce pages or
components reference each channel. That needs a second markup scan, and
VisualforceXssCheck already pays that cost with a 500-page cap, so sharing it
through the cache is the right way to add it rather than querying ApexPage twice.
Six touches per the contract: impl class, registry entry, mapping.ts control rows
plus the devSecurity DOMAIN group, CheckMeta effort/impact, unit test with a
mocked Tooling client, and the README and docs/CHECKS.md rows with the count
moved 92 -> 93.
Gates green: npm run build, npm test (1250 passing across 126 suites, up from
1242).
Signed-off-by: cclabsnz <cloudcounselnz@gmail.com>
cclabsnz
marked this pull request as draft
September 15, 2026 06:55
Owner
Author
|
Holding this as a draft. The finding text asserts that any component sharing the page can subscribe to read payloads or publish its own — that is inferred from how LMS works, not demonstrated. Establishing whether an exposed channel can actually leak data, and to whom, before this ships. |
…not a wider one The finding said any component sharing a page with a publisher or subscriber could read every payload or publish its own. That was inferred from how a client-side bus works and was not established, and it is wrong in the way that matters: subscribing requires importing the channel into a deployed component, so the actor is an installed managed package or something else deployed into the org, never arbitrary script on the page. Written the original way it implied an XSS-reachable bus, which the metadata does not support. The narrower claim still stands, and on Salesforce's own account rather than ours. Their guidance is that Lightning Message Service is secure by default, that components in other namespaces cannot read a channel unless isExposed is true, and that exposing it lets outside packages publish to and subscribe on it. That is the platform owner describing the exposure, which is a better footing than a deduction from first principles. Two limits are now stated in the finding instead of left for a reader to infer. The check reads channel metadata, so it establishes that the door is open and not that anything sensitive passes through it — what a channel carries is visible only in the publishing component. And an exposed channel in an org with no third-party components on those pages has no audience today, though it acquires one the moment a package is installed. Remediation now leads with reading the publishing component, because that is what separates urgent from untidy, and nothing in the metadata distinguishes a channel passing a record id from one passing field values. Both narrowed claims are pinned by tests. An overstatement is the easy drift here: the wording that reads best is the wording that claims most. Gates green: npm run build, npm test (1252 passing across 126 suites). Signed-off-by: cclabsnz <cloudcounselnz@gmail.com>
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.
Adds check 93. Came out of asking whether Lightning Message Channels — server-defined metadata, consumed entirely client-side — can be used for injection. They can, and the platform documents enough to make it auditable.
What the exposure actually is
Lightning Message Service is a browser-side bus with no server mediation. A channel with
isExposed=trueis published to components in any namespace, including every installed managed package. Any component sharing a page with a publisher or subscriber can:The second is the injection you would not expect: a subscriber that takes a record id from a message and passes it to Apex, or renders it into the page, is trusting a publisher it cannot authenticate. The bus is shaped like a trust boundary and is not one.
Three facts from the docs that shape the finding
None are obvious from the setting itself, and each changes the advice:
isExposed=false. A defect by the platform owner's definition, not only ours.isExposedcannot be set back to false once true. Remediation is a new channel plus migrating every publisher and subscriber — not a toggle. The remediation text says so, because implying otherwise understates the cost by a wide margin.Installed channels are reported separately at LOW: identical reach, but not editable here and not reversible anyway, so there is no fix available in this org — only a question for the vendor. Reporting them together would hand a reader a remediation they cannot apply.
Verified before writing, not assumed
Queryability was the open question, and it decided the design. Confirmed against a developer edition with a read-only Tooling query:
LightningMessageChannelis Tooling-queryableIsExposedis a selectable and filterable boolean (WHERE IsExposed = trueexecutes)ManageableStateis what separates local from installedSo no
MetadataClientfallback and no advisory-degradation path — the whole check is one Tooling query.Deliberately grants no capability
An exposed channel is a surface, not reach. Nothing in the metadata says what the channel carries, so granting
data-readwould assert an exposure that has not been established. Wiring it into a chain as a step is a separate judgment and is not made here.Scope left open
The check does not cross-reference which Visualforce pages or components reference each channel, which would let it escalate severity where the exposure is load-bearing. That needs a second markup scan, and
VisualforceXssCheckalready pays that cost under a 500-page cap — so sharing it through the cache is the right way to add it, rather than queryingApexPagetwice.Verification
Six touches per the contract: impl class, registry entry,
mapping.tscontrol rows plus thedevSecurityDOMAIN group,CheckMetaeffort/impact, unit test with a mocked Tooling client, README anddocs/CHECKS.mdrows with the count moved 92 → 93.npm run buildclean;npm test1250 passing across 126 suites (from 1242). Registry confirms 93 registered checks, and the exact query string the check issues was re-run against the org after the check was written.