Repository navigation
refactor(slm-frontend): derive the security, credential and auth-token contracts from the generated schema (#13138) - #13146
Merged
Merged
Conversation
…ntracts from the generated schema (#13138)
Contributor
✅ SSOT Configuration Compliance: Passing🎉 No hardcoded values detected that have SSOT config equivalents! |
5 tasks
This was referenced Jul 31, 2026
Merged
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.
Closes #13147 — the discrete, fully-delivered security/auth slice of #13138.
#13138 itself stays OPEN: ~77 shapes remain, enumerated below, and the credential write paths are blocked on #13145. Partial delivery never closes the parent.
Thinking Path
The count in the issue is wrong, and so was my first attempt at it. The issue says 114 collisions remain. Reproducing the scan on pristine
Dev_new_guiwith a name-level regex reproduced ~109 — but both figures are inflated. A pattern like^\s*(?:export\s+)?(interface|type)\s+(\w+)also matches the members of a type-only import:Requiring a real declaration body (
interface X … {ortype X … =) gives the true figure:Dev_new_gui(5a4c3a4)35, not 39, because four of #13137's shapes have no
response_modeland stayed hand-declared by design.Not every collision is drift — three are worse than drift.
usePrometheusMetrics.tsdeclaresDashboardOverviewandSystemMetrics, and two components declareLogEntry, whose names match a wire schema but whose contents are deliberate client-side view-models built by remapping that same endpoint's response —usePrometheusMetrics.ts:224-241maps/monitoring/dashboard(whose response model really isDashboardOverview,autobot-slm-backend/api/monitoring.py:704) into a different shape, andLogViewer.vue:113-127mapsseverity→level. Deriving those would be actively wrong; they need renaming. They are called out here so the remaining batches do not derive them blindly.Prioritised by risk, not alphabetically. The groups, highest first:
NodeCreate,NodeUpdate,ConnectionTestRequest. Genuine disagreement, and it turned out to be a live defect. Filed as bug(slm): Edit Node silently discards all SSH/PKI/enrollment fields — NodeUpdate accepts 5 of 14; ssh_key dropped on register and connection-test too #13145, deferred here (below).TokenResponse, the eight/security/*shapes, VNC/TLS credential shapes. Delivered here.useCodeSync,useOrchestration,useRoles. Deferred.types/api-responses.ts— the long tail. Deferred.What Changed
18 shapes derived from
components['schemas'][…](94 → 77 by the same scan;SecurityView.vue:43's local re-alias of the now-derivedThreatSummarystill counts against the total but satisfies the issue's "collapse to one exported alias").types/slm.tsSecurityEventResponse,SecurityEventListResponse,SecurityOverviewResponse,SecurityPolicyResponse,SecurityPolicyListResponse,AuditLogResponse,AuditLogListResponse,ThreatSummarytypes/api-responses.tsVNCCredentialCreate,VNCCredentialResponse,VNCEndpointResponse,VNCEndpointsResponse,VNCConnectionInfo,TLSCredentialCreate,TLSCredentialResponse,TLSEndpointResponse,TLSEndpointsResponsestores/auth.tsTokenResponse,MfaChallengeResponseEach alias carries the
file:lineof the backend endpoint whoseresponse_modelit is, so the mapping is checkable rather than assumed.Double declarations collapsed. All eight
/security/*shapes were hand-declared identically in bothtypes/slm.ts:706-821andtypes/api-responses.ts:538-653— one backend change had two places to drift from, and only thetypes/slm.tscopy had any importer.types/slm.tsnow owns the derivation andtypes/api-responses.tsre-exports it, so both import paths keep working from one definition.Drift the derivation exposed and corrected:
TokenResponseomittedtoken. The SLM mirrors the JWT under bothaccess_tokenandtokenso a client written against the core backend reads it (autobot-slm-backend/models/schemas.py:31-48); the hand-written copy documented only half the contract.TLSCredentialResponseomittedca_certandserver_cert— public certificate data the endpoint returns and the UI could never reach (autobot-slm-backend/models/schemas.py:1352-1368).VNCCredentialCreateandTLSCredentialCreateboth omittedextra_data.SecurityOverviewResponse.recent_eventswas declared non-null but isdefault_factory=listserver-side (autobot-slm-backend/models/schemas.py:1601), so the contract makes it optional.SecurityView.vue:643read.lengthoff it unguarded — a crash on any response that omits it. Now?? []/ optional-chained.TLSEndpointResponse.days_until_expiryis nullable and optional;formatExpiryStatusaccepted onlynumber | nulland tested=== null, so an absent value fell through to the "expired" branch. Now== null./api/auth/loginis a union, and was modelled as a flattened blob. Itsresponse_modelisTokenResponse | MfaChallengeResponse(autobot-slm-backend/api/auth.py:81), butstores/auth.tsdeclared one all-optionalMFALoginResponsemerging both. That madeaccess_tokenoptional on the success branch and forced two!assertions on the live login path (stores/auth.ts:99-100). Now the real union plus anisMfaChallengetype guard; the assertions are gone. Both contract members carry anadditionalPropertiescatch-all, so the guard checks the discriminator structurally rather than within.Inline envelopes replaced by their schemas.
getNodeVncCredentials/getNodeTlsCredentialsre-typed their{ credentials, total }return inline; both are real schemas (VNCCredentialListResponse,TLSCredentialListResponse) — the exact shape of the #13137 scope-picker defect, avoided here.Deferred, with reasons
NodeCreate/NodeUpdate/ConnectionTestRequest→ bug(slm): Edit Node silently discards all SSH/PKI/enrollment fields — NodeUpdate accepts 5 of 14; ssh_key dropped on register and connection-test too #13145. The derivation surfaced that the Edit-Node modal PATCHes nine fields the backend'sNodeUpdatedoes not declare (autobot-slm-backend/models/schemas.py:172-179vsAddNodeModal.vue:1123-1141). Pydantic's defaultextra="ignore"drops them, the request 200s and the UI reports success — so "Deploy PKI certificates", "Re-run enrollment tasks" and every SSH credential edit are no-ops, and validation forces an SSH password to enable them.ssh_keyis likewise dropped on register and on connection-test. Deriving these types would turn the surplus fields into compile errors whose only silent fix is deleting UI controls — a product decision, so it is filed rather than taken. Per the task constraint the server contract is left untouched.DashboardOverview,SystemMetrics,LogEntry×2 — must be renamed, not derived (above).formatExpiryStatusreturns hardcoded'Unknown'/'Expired'(SecurityView.vue:416-417). Already in scope of i18n inconsistency: partial coverage — 85/442 .vue files use no t(), ~109 hardcoded English UI strings alongside 7442 t() calls; non-English locales render a patchwork #12732; not re-filed and not fixed here, to keep this diff to contract derivation.Verification
Run in
autobot-slm-frontend/.Before — pristine
origin/Dev_new_guisrc/swapped in withcpfromgit archive(nevergit stash):After:
+4 tests: a new
describeinstores/auth.test.tspinning the login-union discrimination — token branch authenticates, challenge branch stores no token and makes no follow-up/api/auth/mecall,access_tokenwins over the mirroredtoken, andrequires_mfa: falseis not mistaken for a challenge.Lint findings were diffed line-for-line between the
cp-swapped baseline tree and the final tree — identical 18-line finding set, so no pre-existing warning was masked and none was added.verify-generated-types-slmstill holds:git diff --exit-code src/types/generated/api.tsis clean. The generated file was read but never edited, and no regeneration was needed because no backend schema changed.Branch rebased onto
163933b75afterorigin/Dev_new_guimoved mid-work;type-checkre-run green on the rebased tip.Per the #13090 execution constraint no AutoBot application code was run — no backend, no SLM backend, no schema dump, no ad-hoc Postgres/Redis. Every contract claim above was read from the committed generated artefact and cross-checked against backend source at the cited
file:line.Model Used
claude-opus-5