You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(core)!: retire PluginSecurityScanner — plugin security scanning is not a platform capability (#15930)
* feat(core)!: retire PluginSecurityScanner across all four surfaces (#14919)
ADR-0049 enforce-or-remove; maintainer ruling 2026-09-05 (director summon #14,
decision batch #42). The class was a shell that reported success: four of its
five private scanners returned an empty issue list unconditionally, and the
fifth matched against an in-memory vulnerability database whose only writer had
zero callers -- so every scan() ever performed answered status: 'passed' with a
perfect score, for a malicious plugin as readily as a benign one. A security
control that cannot fail is worse than none, because callers rely on it.
- delete packages/core/examples/phase2-integration.ts (the only constructor)
- delete src/security/security-scanner.ts; drop its export block from
src/security/index.ts, leaving a tombstone naming the retirement
- rewrite PHASE2_IMPLEMENTATION.md section 6 to state plainly that plugin
security scanning is NOT a platform capability, and drop the two capability
claims elsewhere in the same document that outlived their subject
- delete the FOLLOW-UPS.md row, repair the paragraph that existed only to
compound it, and correct the neighbouring row whose evidence the deletion
falsified
- pin the retirement as an export-list assertion on both barrels
Repair was refused by name: a real vulnerability scanner is a feature with a
design surface, not a defect fix. There is no replacement export.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
* feat(spec): register the PluginSecurityScanner retirement in the ADR-0087 ledger (#14919)
check-adr-0087-registration refused the previous disposition, correctly: the
changeset carries a real consumer prescription (delete the import and every
call), so `not-required (no-migration-prescription)` was a self-contradiction.
Every other not-required category is false too -- @objectstack/core publishes,
so `unpublished` is out; `already-registered` has no entry to name;
`type-surface-only` needs an any/unknown-to-concrete narrowing this is not; and
`runtime-interface-only` explicitly inherits the same prescription refusal
(#8299). The only truthful disposition left is `registered`.
That is also the repo's settled convention for this exact shape -- a published TS
symbol with no spec schema, no stored source and no tombstone, where the ledger
is the only channel that reaches an upgrader. contracts.IDataDriver.findStream
and actor-user-roles-to-positions are both registered on those grounds.
D3 semantic, not a D2 conversion: the class has no spec schema, so there is no
authorable key to tombstone and no stored sys_metadata row to rewrite -- a
scanner was constructed per call and every result lived in a per-instance Map
discarded with the object, so applyConversionsToStoredItem has no seam that would
ever see one. This is what the ruling's "no metadata migration" excludes, and it
is excluded.
- add entries/semantic/18.plugin-security-scanner-retired.ts (one file, per the
entries README kit -- no hand edit inside registry.ts's generated markers)
- regenerate registry.ts via gen:migration-registry (157 semantic entries)
- flip the core changeset's marker to `registered
plugin-security-scanner-retired`, keeping the BREAKING banner, the
no-replacement statement and the NOT MEASURED paragraph untouched
- add the @objectstack/spec patch changeset, mirroring the #6138 backfill
Measured and recorded in that changeset: the regeneration lap the entries README
warns about did not materialise. check:generated reports all 15 artifacts up to
date, and running gen:spec-changes and gen:upgrade-guide explicitly moved
neither file -- a major-18 semantic entry is not yet projected into either.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
* fix(changeset): the `registered` marker takes ids only — move its rationale into the body
check-adr-0087-registration parses everything after `registered` as a
comma/space-separated id list, so the trailing `why` prose that the
`not-required (...)` forms accept was read as 131 nonexistent migration ids. The
asymmetry is real and AGENTS.md spells it: `registered SOME-MIGRATION-ID` carries
no `why`, the three `not-required` forms do. The rationale is unchanged, only
relocated into the changeset body where a reader gets it anyway.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
---------
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/qa/platform-checklist/FOLLOW-UPS.md
+13-9Lines changed: 13 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -183,19 +183,23 @@ governance hole.
183
183
184
184
| surface | evidence | the deadness, precisely |
185
185
|---|---|---|
186
-
|`PluginSecurityScanner` (`packages/core/src/security/security-scanner.ts`) | zero constructors outside `packages/core/examples/`; not in plugin-loader, service-package, rest, or any CLI path | Exported dead code on the PUBLIC barrel (`packages/core/src/index.ts` re-exports `./security/index.js`). 3 of 5 scan methods are empty stubs; `scanDependencies` has a real loop whose only data source (`addVulnerability`, ``) has zero callers; `updateVulnerabilityDatabase` (``) is a log-only no-op. |
187
-
|`KernelSecurityScanResult` / `KernelSecurityVulnerability` / `PluginSecurityManifest.scanResults` (`packages/spec/src/kernel/plugin-security-advanced.zod.ts,476,625`) | no `.parse`/`.safeParse` site anywhere; only consumer is the dead scanner (type-only import) | 22 rows published to `packages/spec/authorable-surface/kernel.json` with zero authors and zero parsers. The whole `plugin-security-advanced` module has no runtime consumer. |
186
+
|`KernelSecurityScanResult` / `KernelSecurityVulnerability` / `PluginSecurityManifest.scanResults` (`packages/spec/src/kernel/plugin-security-advanced.zod.ts,476,625`) | no `.parse`/`.safeParse` site anywhere; **zero** consumers of any kind since #14919 retired the dead scanner that was the last type-only importer | 22 rows published to `packages/spec/authorable-surface/kernel.json` with zero authors and zero parsers. The whole `plugin-security-advanced` module has no runtime consumer. |
188
187
|`PluginQualityMetrics.securityScan` (`packages/spec/src/kernel/plugin-registry.zod.ts`) | spec self-test only | Nothing reads or writes it at runtime. |
189
188
| Marketplace/incident scan vocab (`marketplace.zod.ts` 'scanning' status, `marketplace-admin.zod.ts,193`, `incident-response.zod.ts` 'malware') | declared-only enum members, no producer in this repo | Cloud/EE surface. Same shape as the `'failed'`/`'expired'` upload statuses #7667 had to close: declared, published, no writer. |
190
189
| MetadataPlugin FS scan + `metadata-fs` boot scan (`packages/metadata/src/plugin.ts,270` — `watch ?? false`; `packages/runtime/src/standalone-stack.ts` hard-off; `metadata-fs` unwired from any `os dev`/`os serve` lane) | unit-pinned in-package only | No reachable fixture from any shipped boot; if a future lane wires `metadata-fs`, the boot-scan/watcher dot-entry divergence is the risk to test first. |
191
190
192
-
Compounding the first row: `packages/core/PHASE2_IMPLEMENTATION.md` advertises
193
-
the scanner as a working feature, tells readers to import from `@objectstack/core/security`
194
-
(a subpath `packages/core/package.json` does not export), and its sample fields
195
-
(`scanResult.passed`/`.score`/`.summary.critical`) do not exist on the actual schema —
196
-
the example (`examples/phase2-integration.ts`) sits outside every tsconfig and is never
197
-
typechecked. Enforce or remove; if removed, the spec-property-retirement playbook applies
198
-
to the authorable-surface rows.
191
+
The scanner row above was **CLOSED by removal** in #14919 (maintainer ruling,
192
+
director summon #14, decision batch #42): the class, its barrel export, its
193
+
`packages/core/examples/` demonstration and the `PHASE2_IMPLEMENTATION.md` section that
194
+
advertised it are gone, and that section now states plainly that plugin security scanning
195
+
is not a platform capability. Repair was refused by name. Do not re-derive it.
196
+
197
+
**What SURVIVES that removal, in the same document.**`PHASE2_IMPLEMENTATION.md` sections
198
+
4 and 5 still tell readers to `import … from '@objectstack/core/security'` — a subpath
199
+
`packages/core/package.json` declares in no `exports` entry, so it resolves for no
200
+
consumer of the published package. Deliberately left: the two repairs (declare the
201
+
subpath, or repoint both sections at the root barrel) differ in whether they widen the
202
+
published contract, which is not a lane's call. Filed separately.
199
203
200
204
### 7b. Docs drift (PD#10 class — file as docs fixes, not checklist items)
0 commit comments