fix(appstore): compute app trust fields server-side (#580) - #2408
Conversation
Opus 4.8 Review — ✅ no blocking findingsReviewed Review detailsI've verified the full flow. Let me confirm the snapshot survives all All paths in No injected security hole, no crash/data-loss path, no removed guard without replacement — the change is purely additive hardening that moves trust computation server-side. No findings. [OPUS-REVIEWED] d190c77 Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
UX Review (Fable 5) — ✅ PASSAdvisory UX-level review of UX-Verdict: PASS Pure trust-boundary refactor: no new strings, no flow changes, and the before/after screenshots confirm the Discover tab renders pixel-identical. [UX-REVIEWED] d190c77 |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — ✅ PASSAdvisory design-level review of Design-Verdict: PASS Trust computation moves to the layer where Suggestions
[DESIGN-REVIEWED] d190c77 |
/api/apps/registry rows now carry server-computed provenance
('core' | 'external' | 'builtin') and verified fields, stamped by a new
_apply_trust_fields pass at the end of list_registry() — where the
server-attached _registry tag is authoritative — and OVERWRITING anything
an index publishes. featured is stripped from external rows entirely, so
an external index can never self-flag into the Discover spotlight.
The client's three trust derivations (isVerified, sourceLabel,
pickFeatured) collapse to read the server fields, still reject a
_registry-tagged row first (nothing smuggled through an older gateway can
relabel an external row), and keep the legacy origin/author derivation
only as a fallback for rows from older gateways. Client-synthesized
builtin rows in browseApps speak the new contract directly.
Rendering is identical for legitimate data before/after (screenshots
under temp-screenshots/appstore-trust-580/).
Closes #580
4dd0490 to
d190c77
Compare
|
🤖 Kiro Crew Auto-Pipeline [operator: bolichen97] Disposition of the Design Review suggestion (single-writer trust contract for synthesized builtin rows): accepted-and-deferred → #2416. Rationale for deferring: builtin rows never pass through |
…irodotdev#2408) /api/apps/registry rows now carry server-computed provenance ('core' | 'external' | 'builtin') and verified fields, stamped by a new _apply_trust_fields pass at the end of list_registry() — where the server-attached _registry tag is authoritative — and OVERWRITING anything an index publishes. featured is stripped from external rows entirely, so an external index can never self-flag into the Discover spotlight. The client's three trust derivations (isVerified, sourceLabel, pickFeatured) collapse to read the server fields, still reject a _registry-tagged row first (nothing smuggled through an older gateway can relabel an external row), and keep the legacy origin/author derivation only as a fallback for rows from older gateways. Client-synthesized builtin rows in browseApps speak the new contract directly. Rendering is identical for legitimate data before/after (screenshots under temp-screenshots/appstore-trust-580/). Closes kirodotdev#580
Summary
Moves the App Store trust boundary server-side.
/api/apps/registryrows now carry explicit server-computedprovenance("core" | "external" | "builtin") andverifiedfields, andfeaturedis stripped from external rows — so the client no longer derives trust semantics from the absence of the internal_registrytag.Backend (
src/kiro_crew/apps/registry.py)_apply_trust_fields()pass at the tail oflist_registry(), where the server-attached_registrytag is authoritative. It overwrites anything an index publishes, so an external registry can never spoof the fields.verifiedderives from the index-declared author snapshotted before the app.json merge (_index_author), never from the repo-fetched manifest — a third-party core repo publishing"author": "KiroCrew"in itsapp.jsoncannot mint the badge. (Caught by the pre-push Opus review pass; the snapshot key is popped before the payload is served.)featuredis popped from external rows entirely, so an external index can never self-flag into the Discover spotlight regardless of client logic._registrystays in the payload (source-label text, older clients,appManifest.ts::keysFor,pickFeaturedlegacy arm) — this change adds fields only.Frontend (
website/src/components/appstore/types.ts,website/src/pages/AppsPage.tsx)isVerified/sourceLabel/pickFeaturedprefer the server fields; a_registry-tagged row is still rejected FIRST, soverified/provenancevalues smuggled through an older gateway (which computes nothing) can never relabel an external row.origin/authorderivation kept as fallback for rows from older gateways — existing tests pass unmodified as the back-compat proof.browseAppssetprovenance: 'builtin',verified: trueso both code paths speak the new contract.Docs:
docs/system-specs/modules/app-kit-platform.mdupdated in the same commit.Why
Three PR #532 security holes (author spoof, origin spoof, featured self-flagging) were all instances of one misplaced boundary: trust derived client-side from an internal tagging detail. Nothing prevented a future backend change from silently flipping the verified badge next to an Install button that runs third-party setup with gateway privileges. The trust decision now lives where
_registryis authoritative.Testing
TestApplyTrustFields(9 tests) intest/test_apps_registry.py— external spoof neutralization (author/origin/featured), index pre-seed overwrite, manifest-author spoof regression, non-string author,_index_authornon-leak, plus alist_registryend-to-end.website/src/test/appstoreCategories.test.ts— server-field precedence, older-gateway smuggling defenses, either-signal external exclusion inpickFeatured, legacy-fallback parity. Pre-existing suites pass unmodified.npx tsc -bclean; vitest 866/866 files; full pytest run (residual failures verified identical on cleanmain— host-environment)./api/apps/registryemits the new fields (launchdarkly → provenance: core, verified: false).Rendering back-compat
Discover tab is pixel-identical before/after (spotlight, verified badge, source labels):
Pre-push review
Dual model-pinned review fleet ran before push: GPT 5.6 Sol — CLEAN; Opus 5 — 1 BLOCKING (verified mintable from manifest author) fixed via the pre-merge index-author snapshot + regression tests, 1 advisory (docstring
_registrydependants) fixed; focused verifier confirmed the fix on all 5 checked points.Closes #580