diff --git a/www/src/utils/jotform-completion.ts b/www/src/utils/jotform-completion.ts index 035b006..5601d54 100644 --- a/www/src/utils/jotform-completion.ts +++ b/www/src/utils/jotform-completion.ts @@ -6,7 +6,17 @@ export function isFormACompletion(event: { origin: string; source: unknown; data // never from the message, current child location, query string or referrer. let expected: URL; try { expected = new URL(approvedUrl); } catch { return false; } - if (expected.protocol !== 'https:' || event.origin !== expected.origin) return false; + if (expected.protocol !== 'https:') return false; + + // HIPAA-enabled Jotform submissions can send completion messages + // from a different origin than the original embedded form. + const approvedOrigins = new Set([expected.origin]); + + if (expected.origin === 'https://form.jotform.com') { + approvedOrigins.add('https://hipaa-submit.jotform.com'); + } + + if (!approvedOrigins.has(event.origin)) return false; let data = event.data; if (typeof data === 'string') { if (data.length > 4096) return false; diff --git a/www/tools/service-portal.test.mjs b/www/tools/service-portal.test.mjs index 4a67607..65d9487 100644 --- a/www/tools/service-portal.test.mjs +++ b/www/tools/service-portal.test.mjs @@ -11,16 +11,18 @@ import { collectResource, resourceReferences, rewriteResources } from './portal- import { isFormACompletion } from '../src/utils/jotform-completion.ts'; import { portalLinkPresentation } from '../src/utils/portal-presentation.ts'; -test('completion envelope requires exact origin, active frame window and string Form A ID', () => { +test('completion envelope requires an exact approved origin, active frame window and string Form A ID', () => { const source = {}; const check = (event, frame, active) => isFormACompletion(event, frame, active, 'https://form.jotform.com/262946078314059?formContext=consultation&language=en'); const data = { action: 'submission-completed', formID: '262946078314059' }; const event = { origin: 'https://form.jotform.com', source, data }; assert.equal(check(event, source, true), true); + assert.equal(check({ ...event, origin: 'https://hipaa-submit.jotform.com' }, source, true), true); assert.equal(check({ ...event, data: JSON.stringify(data) }, source, true), true); for (const change of [ - { origin: 'https://form.jotform.com.evil.invalid' }, { origin: 'null' }, { origin: 'http://form.jotform.com' }, + { origin: 'https://form.jotform.com.evil.invalid' }, { origin: 'https://hipaa-submit.jotform.com.evil.invalid' }, + { origin: 'https://other.jotform.com' }, { origin: 'null' }, { origin: 'http://form.jotform.com' }, { source: {} }, { source: null }, { data: { ...data, formID: 'other' } }, { data: { ...data, formID: 262946078314059 } }, { data: { ...data, action: 'load' } }, { data: '{broken' }, { data: [] }, { data: null }, { data: 'x'.repeat(4097) },