Repository navigation
feat(endpoints): store, encrypt, embed and export forms (#2177-#2180) - #2338
Merged
Merged
Conversation
Forms built on endpoints now store, protect, render and report their submissions: 1. #2178: binding a form makes a webhook source of input form (connection chosen on endpoint_set); an accepted submission is stored as one event in webhook_form_<name>, signed-in submitter included. 2. #2179: sensitive fields are sealed with fieldcrypt before the spool; fieldcrypt gains key rotation (ENCRYPTION_KEY_PREVIOUS); platform.decrypt opens them for a persona listed in the source's decrypt_personas, audited as form_decrypt. 3. #2177: /portal/forms/<endpoint> renders a form; HTML and JSX assets embed it by mcp:form: reference, and a signed-in form inside an asset takes a short-lived viewer token as its caller. 4. #2180: the owner's Submissions card lists stored submissions with CSV and Excel export; forms are authored through manage_script alone. Also fixes the E2E Nightly failure in TestIssue2065, which read the call record once before it was written; it now polls. Closes #2177 Closes #2178 Closes #2179 Closes #2180
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2338 +/- ##
========================================
Coverage 94.80% 94.80%
========================================
Files 1422 1435 +13
Lines 115639 116477 +838
========================================
+ Hits 109627 110430 +803
- Misses 5992 6027 +35
Partials 20 20 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.
Forms built on endpoints (#2094) now store their submissions, protect sensitive fields, render inside assets, and report to their owner. This covers four child tickets (#2177, #2178, #2179, #2180) and one E2E Nightly fix. The epic stays open for #2331, #2332 and #2333.
Collection through a webhook source (#2178)
manage_script command=endpoint_setmakes a webhook source namedform-<endpoint>withinput: form(internal/webhook/whsource/form.go,internal/endpoint/epsource). Its events land inwebhook_form_<endpoint>on a scratch catalog connection.endpoint_settakes aconnectionargument. With no argument it uses the only scratch connection; when there are several, it refuses and lists them.endpoint_listreportsis_form,formandform_connections.internal/webhook/receiver/form.go,Deliver) as one event of typeform_submission. The payload holds endpoint, version, values, fields, sensitive and submitter (internal/endpoint/epcollect/epcollect.go).Retry-Afterand nothing is recorded as accepted (internal/endpoint/eprun/form.go).POST /hooks/form-<endpoint>answers 404.whadmin.ErrFormSource).input: form, and refuses aformblock on a source that isn't a form's.Sensitive field encryption (#2179)
sensitiveis sealed withfieldcryptbefore the event reaches the spool. The ciphertext is what lands in the spool, the raw segment, the compacted Parquet and the Trino view.epcollect.ErrCannotSeal).fieldcryptgains key rotation:NewFieldEncryptor(key, previous...)decrypts with the current key, then each previous key.ENCRYPTION_KEY_PREVIOUSis a list of earlier keys (pkg/platform/fieldcrypt/fieldcrypt.go,docs/server/deployment.md).platform.decrypt(payload)is a new script host call (internal/scripttoken/decrypt.go,internal/endpoint/epcollect/decrypt.go). It is allowed when the run author's persona is listed in the source'sdecrypt_personas, whichendpoint_setand the portal binding editor set. Admins are always allowed.platform.queryreturns it (a dict) and as JSON text.form_decryptnaming the fields opened (pkg/audit/event.go).[encrypted].Rendering and embedding in assets (#2177)
GET /portal/forms/<endpoint>renders a form from its spec, with its bot check, and posts to the endpoint (internal/endpoint/epformpage).mcp:form:<endpoint>reference (internal/portal/assetrefs,internal/portal/contentrefs). An asset may embed any number of forms.allow-scripts allow-downloads, opaque origin, no cookie). Four changes follow from that, and the asset sandbox itself is unchanged:internal/endpoint/epform/viewer.go), and the endpoint takes it as the caller only for that form (ephttp.caller).Origin: nullwith CORS, never with credentials. Endpoints that are not forms still refuse it.allow-forms.ui/src/components/renderers/formFrames.ts), becauseframe-src 'self'refusespublic_base_urlwhen it differs.docs/scripts/endpoints.md).Submissions view, export and MCP authoring (#2180)
GET /api/v1/portal/scripts/{id}/submissionslists a form's stored submissions to its owner or an admin, decrypted and audited.GET /api/v1/portal/scripts/{id}/submissions/export?format=csv|xlsxexports them (internal/endpoint/epsubmit,internal/httpserver/scripthttp/submissionshttp). CSV cells that start with a formula character are prefixed.manage_scriptalone; acceptance criterion 2 of feat(forms): submissions view, export and MCP authoring #2180 does exactly that.E2E Nightly
TestIssue2065_TheAuditTheCallRecordAndTheRecordingHoldThePlaceholderread the call record once. The record is written after the audit row, so the read could come too early. It now polls (test/acceptance/issue_2065_test.go).Acceptance
All 13 criteria passed through the real surface on the dev stack, and each run is recorded at
build/<n>/acceptance.jsonl:input: formis accepted only on a form's own source.The Playwright half is
ui/e2e/forms. The #2065 criteria also re-pass.Readers of this state
whsource.Config.inputgainsform, and there is a newformblock (whsource.Form).ui/src/api/admin/types/webhooks.ts,ui/src/pages/webhooks/webhookForm.ts,WebhookEditor.tsxandWebhookDetailPage.tsx, all updated.FormSourceSection.tsx.formas an input to choose.bindinghttp.bindingRequestgainsconnectionanddecrypt_personas, and the endpoint route descriptions changed.ui/src/api/portal/hooks/scriptEndpoint.ts,ScriptEndpointCard.tsx,ScriptEndpointSections.tsxandendpoints/FormStorageFields.tsx, all updated.epcmd(endpoint_set), which takes the same two fields.GET /portal/scripts/{id}/submissionsand its export route are new, along withepsubmit.Page,epsubmit.Submissionandepcollect.Submitter.ui/src/api/portal/hooks/scriptSubmissions.tsandScriptSubmissions.tsx.audit.EventTypeandauditfiltersResponse.event_kindsgainform_decrypt, which also changes the parameter descriptions on the audit event and metric routes.ui/src/api/admin/hooks/system-audit.tssendsevent_kindas a string), so it needs no change.Built-in pages
make builtin-pages-checklisted seven pages whose source paths this diff touches. I read each against the change:asset-references-and-the-refresh-loop.mdnow coversmcp:form:references.content-types-for-stored-files,documents-that-fit-any-screen,presentations,semi-dynamic-dashboards,managed-script-dialectandwriting-managed-scriptssay nothing the change alters. None of them lists the script host calls, whichhelp(scriptcontract) documents and which now includesplatform.decrypt.New package imports
test/structure/testdata/allowed_internal_imports.txtgains 47 edges, all from packages this PR adds or extends:epcollect:whsourceandwhevent: the event it builds.scripttoken: the decrypt host call's interface.pkg/auditandpkg/middleware: theform_decryptrow and the caller identity.epform: the spec's sensitive fields.logsan.epsubmit:whsourceandwhtable: the source and the view it reads.epcollect: decrypting.tablexlsx: the Excel export.pkg/auditandpkg/middleware.logsan.epsource:whsource.epformpage:epbinding,eprunandepform: the binding, the spec and the run.assetrefs: the shared URI prefix.logsan.epcmd,ephttp,endpointwireandpkg/portaltoepform: the viewer token.epcmdtoepsource.epruntoepcollect.epwiretoassetrefs: the form targets an asset resolves.formwire,webhookwireandsubmissionshttpare wiring:formwireconnectspkg/platform, the Trino toolkit,webhookwire,epform,epsubmit,whsourceandwhtable.webhookwireconnectsepcmd,epcollect,epsource,scripttoken,wheventandpkg/middleware.submissionshttpservesepsubmitthroughscripthttp's ownership check, withhttpjsonandlogsan.internal/httpservermountsformwireandsubmissionshttp.Package size budgets were held by moving code out, never by raising a budget:
fieldcrypt.DecodeKey/DecodeKeysmoved out ofpkg/platform.scriptruntoscriptdialect.epsourcesplit out ofepcollectfor cohesion.submissionshttpis its own package underscripthttp.make verifypassed.Closes #2177
Closes #2178
Closes #2179
Closes #2180