fix(settings): correct the exposure analysis and satisfy the coverage and lint gates

The Agent Note claimed the plugin inventory page already exposed every
mounted plugin's effective configuration to the same browser, so the
removed allowlist blocked nothing a caller could not already read. That
is false: PluginInventoryEntry carries entryId, moduleName, enabled, and
fiberPhase, and the page's configuration row renders an enabled tag, not
a stored value. The allowlist did keep an unlisted namespace's resolved,
base, and user values off the wire; the note now says so and rests the
decision on the loopback pin, secret redaction, the user's own document,
and the fact that permission and agent-presets were already served.

The post-await disposal check reads through an opaque method, mirroring
the settings seam's isStopped(): control flow narrowed the field to false
across the await, so the lint gate saw the guard as dead.
This commit is contained in:
Yichen Jiang
2026-08-12 21:35:21 +08:00
parent d8035680b9
commit 5d9f026e55
5 changed files with 18 additions and 9 deletions

View File

@@ -60,12 +60,17 @@ export class PluginConfigSectionController {
private readonly entries: () => readonly StoredEntry[],
) {}
/** Opaque read of {@link disposed}: control flow cannot narrow it across awaits. */
private isDisposed(): boolean {
return this.disposed
}
/**
* Re-read the served namespaces from the Host and republish.
* @returns settlement after the read, or immediately once disposed.
*/
async load(): Promise<void> {
if (this.disposed) return
if (this.isDisposed()) return
const generation = ++this.generation
let response: Awaited<ReturnType<IApiClient['settings']['describe']>>
try {
@@ -75,7 +80,7 @@ export class PluginConfigSectionController {
// or reconnect reads again.
return
}
if (this.disposed || generation !== this.generation || !response.result.ok) return
if (this.isDisposed() || generation !== this.generation || !response.result.ok) return
this.served = response.result.value.namespaces.map(view => view.ns)
this.loaded = true
this.publish()

View File

@@ -603,7 +603,7 @@ describe('PluginConfigSectionController', () => {
const settings = settingsApi(['bash'])
const controller = new PluginConfigSectionController(settings.api, () => ledger('bash'))
await controller.load()
settings.describe.mockRejectedValueOnce(new Error('offline') as never)
settings.describe.mockRejectedValueOnce(new Error('offline'))
await controller.load()