From 9a6c0bf1976e7c9b9e0fa7e8cbc56024aa88b852 Mon Sep 17 00:00:00 2001 From: Carl Anderson Date: Fri, 20 Dec 2019 10:51:08 -0600 Subject: [PATCH 1/7] add message origin checking for SMART Web Messaging --- src/middleware/cds-execution.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middleware/cds-execution.js b/src/middleware/cds-execution.js index 4bbf4f9..d9ea17f 100644 --- a/src/middleware/cds-execution.js +++ b/src/middleware/cds-execution.js @@ -151,7 +151,7 @@ const onSystemActions = (action, next, pre, post) => { const webMessageMiddleware = (store) => (next) => { window.addEventListener('message', ({ data, origin, source }) => { Object.entries(windowsRegistered) - .filter(([windowId, w]) => w.sourceWindow === source) + .filter(([windowId, w]) => w.sourceWindow === source && w.origin === origin) .map(([windowId, w]) => w.triggerPoint) .map((triggerPoint) => triggerHandlers[triggerPoint]) .forEach((handler) => handler.onMessage({ From 1c051de9a3252258558f8d7203afd85914a872c1 Mon Sep 17 00:00:00 2001 From: Carl Anderson Date: Fri, 20 Dec 2019 11:29:00 -0600 Subject: [PATCH 2/7] fix: unit test change needed to support origin checking --- tests/middleware/cds-execution.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/middleware/cds-execution.test.js b/tests/middleware/cds-execution.test.js index 9b11188..d8c45f4 100644 --- a/tests/middleware/cds-execution.test.js +++ b/tests/middleware/cds-execution.test.js @@ -45,7 +45,7 @@ describe("CDS Execution Middleware", () => { unregister: unregisterWindow } = cdsExecution.registerWindow( "example-trigger-point", - "fake-origin", + "", // jsdom makes the origin look empty null // jsdom makes the source window look `null` ); From 18f87f61e9e9f31f9f4ee9603324880656a12aa6 Mon Sep 17 00:00:00 2001 From: Carl Anderson Date: Wed, 11 Mar 2020 22:50:26 -0500 Subject: [PATCH 3/7] wip: refactored event checking logic to be easier to folloow. --- src/components/MessagePanel/message-panel.jsx | 45 ++++++++++++------- src/middleware/cds-execution.js | 1 + 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/components/MessagePanel/message-panel.jsx b/src/components/MessagePanel/message-panel.jsx index 2df78df..da20efc 100644 --- a/src/components/MessagePanel/message-panel.jsx +++ b/src/components/MessagePanel/message-panel.jsx @@ -52,13 +52,14 @@ class MessagePanel extends Component { replyMessage(event) { let payloadStructure; - if (event.data.messageType.includes('scratchpad.')) { + const messageTarget = event.data.messageType.split('.'); + if (messageTarget[0] === 'scratchpad') { payloadStructure = { status: 200, location: 'https://resource-location/', outcome: 'Success', }; - } else if (event.data.messageType.includes('ui.')) { + } else if (messageTarget[0] === 'ui') { payloadStructure = { success: true, details: 'Success', @@ -74,32 +75,44 @@ class MessagePanel extends Component { event.source.postMessage(msgStructure, event.origin); } - addMessage(event) { + isActionableMessage(event) { + // Ignore the event, if it doesn't meet our expectations. + if (!event.data) return false; + if (event.data.source === "react-devtools-bridge") return false; + + // TODO: why do the following check? if (event.origin.includes(document.domain)) { console.log(`Received message from ${event.origin} but it is the same as the current document's domain: ${document.domain}.`); - return; + return false; } - console.log(`Received message from ${event.origin}.`); - if (!event.data.messageId) { - console.log('Message did not have a messageId and will be ignored.'); - return; + console.log('Message has no messageId.'); + return false; } if (!event.data.messageType) { - console.log('Message did not have a messageType and will be ignored.'); - return; + console.log('Message has no messageType.'); + return false; } - if (!(event.data.messageType.includes('scratchpad.') || event.data.messageType.includes('ui.'))) { - console.log('Message did not have a supported messageType and will be ignored.'); - return; + const messageTarget = event.data.messageType.split('.'); + if (!['scratchpad', 'ui'].includes(messageTarget[0])) { + console.log(`Unknown message type '${messageTarget[0]}.`); + return false; } - const message = JSON.stringify(event.data, null, 2); - this.setState({ messages: [...this.state.messages, message] }); - this.replyMessage(event); + return true; + } + + addMessage(event) { + if (this.isActionableMessage(event)) { + console.log('Adding a message to Message panel.', event); // XXX + const message = JSON.stringify(event.data, null, 2); + // TODO: convert this to a stack so newer messages are on top. + this.setState({ messages: [...this.state.messages, message] }); + this.replyMessage(event); + } } /** diff --git a/src/middleware/cds-execution.js b/src/middleware/cds-execution.js index d9ea17f..0c4e9d9 100644 --- a/src/middleware/cds-execution.js +++ b/src/middleware/cds-execution.js @@ -150,6 +150,7 @@ const onSystemActions = (action, next, pre, post) => { /* eslint-disable no-unused-vars */ const webMessageMiddleware = (store) => (next) => { window.addEventListener('message', ({ data, origin, source }) => { +// console.log('CDS IN ACTION', data, origin, source); Object.entries(windowsRegistered) .filter(([windowId, w]) => w.sourceWindow === source && w.origin === origin) .map(([windowId, w]) => w.triggerPoint) From a023a94a0dda76704c84e4fbf6bb255c4c1e8e94 Mon Sep 17 00:00:00 2001 From: Carl Anderson Date: Thu, 12 Mar 2020 16:14:05 -0500 Subject: [PATCH 4/7] fix: broken origin checking in the hook reducer --- src/components/MessagePanel/message-panel.jsx | 16 ++++--------- src/middleware/cds-execution.js | 1 - src/reducers/hook-reducers.js | 4 +++- tests/reducers/hook-reducers.test.js | 23 +++++++++++++++++++ 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/components/MessagePanel/message-panel.jsx b/src/components/MessagePanel/message-panel.jsx index da20efc..8f3a5c0 100644 --- a/src/components/MessagePanel/message-panel.jsx +++ b/src/components/MessagePanel/message-panel.jsx @@ -78,27 +78,20 @@ class MessagePanel extends Component { isActionableMessage(event) { // Ignore the event, if it doesn't meet our expectations. if (!event.data) return false; - if (event.data.source === "react-devtools-bridge") return false; - - // TODO: why do the following check? - if (event.origin.includes(document.domain)) { - console.log(`Received message from ${event.origin} but it is the same as the current document's domain: ${document.domain}.`); - return false; - } + if ((event.data.source || "").startsWith('react-devtools-')) return false; if (!event.data.messageId) { - console.log('Message has no messageId.'); - return false; + console.warn('Message has no messageId.'); } if (!event.data.messageType) { - console.log('Message has no messageType.'); + console.warn('Message has no messageType.'); return false; } const messageTarget = event.data.messageType.split('.'); if (!['scratchpad', 'ui'].includes(messageTarget[0])) { - console.log(`Unknown message type '${messageTarget[0]}.`); + console.warn(`Unknown message type '${messageTarget[0]}.`); return false; } @@ -107,7 +100,6 @@ class MessagePanel extends Component { addMessage(event) { if (this.isActionableMessage(event)) { - console.log('Adding a message to Message panel.', event); // XXX const message = JSON.stringify(event.data, null, 2); // TODO: convert this to a stack so newer messages are on top. this.setState({ messages: [...this.state.messages, message] }); diff --git a/src/middleware/cds-execution.js b/src/middleware/cds-execution.js index 0c4e9d9..d9ea17f 100644 --- a/src/middleware/cds-execution.js +++ b/src/middleware/cds-execution.js @@ -150,7 +150,6 @@ const onSystemActions = (action, next, pre, post) => { /* eslint-disable no-unused-vars */ const webMessageMiddleware = (store) => (next) => { window.addEventListener('message', ({ data, origin, source }) => { -// console.log('CDS IN ACTION', data, origin, source); Object.entries(windowsRegistered) .filter(([windowId, w]) => w.sourceWindow === source && w.origin === origin) .map(([windowId, w]) => w.triggerPoint) diff --git a/src/reducers/hook-reducers.js b/src/reducers/hook-reducers.js index 75a0ac6..5800425 100644 --- a/src/reducers/hook-reducers.js +++ b/src/reducers/hook-reducers.js @@ -49,9 +49,10 @@ const hookReducers = (state = initialState, action) => { return { ...state, isLoadingData: action.isLoaderOn }; } case types.LAUNCH_SMART_APP: { + const url = new URL(action.link.url); const { windowId } = cdsExecution.registerWindow( action.triggerPoint, - action.link, + url.origin, action.sourceWindow, ); return { @@ -61,6 +62,7 @@ const hookReducers = (state = initialState, action) => { { triggerPoint: action.triggerPoint, link: action.link, + linkOrigin: url.origin, windowId, }, ], diff --git a/tests/reducers/hook-reducers.test.js b/tests/reducers/hook-reducers.test.js index d07dd99..c75a0b8 100644 --- a/tests/reducers/hook-reducers.test.js +++ b/tests/reducers/hook-reducers.test.js @@ -6,6 +6,7 @@ describe('Hook Reducer', () => { beforeEach(() => { state = { + apps: [], currentHook: 'patient-view', isLoadingData: false, isContextVisible: true, @@ -28,6 +29,28 @@ describe('Hook Reducer', () => { }); }); + describe('LAUNCH_SMART_APP', () => { + it('should extract the origin from a link url', () => { + const action = { + type: types.LAUNCH_SMART_APP, + triggerPoint: 'patient-view', + link: { + url: 'http://localhost:8080/foo/bar/ignored', + }, + sourceWindow: 'abcdefgh-ijklm-nopqrstuvwxyz', + }; + const expectedApp = { + triggerPoint: action.triggerPoint, + link: action.link, + linkOrigin: 'http://localhost:8080', + windowId: 0, + }; + const expected = Object.assign({}, state, { apps: [ expectedApp ] }); + const actual = reducer(state, action); + expect(actual).toEqual(expected); + }) + }); + describe('SET_CONTEXT_VISIBILITY', () => { it('should handle the SET_CONTEXT_VISIBILITY action accordingly', () => { const action = { From 8cc48c8308a9d86a56591fb40042a280852b2348 Mon Sep 17 00:00:00 2001 From: Carl Anderson Date: Fri, 13 Mar 2020 14:32:06 -0500 Subject: [PATCH 5/7] fix: separate behavior changes for a future PR --- .vscode/launch.json | 40 +++++++++++++++++++ jest_config_js | 3 ++ src/components/MessagePanel/message-panel.jsx | 1 + tests/reducers/hook-reducers.test.js | 2 +- 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 .vscode/launch.json create mode 100644 jest_config_js diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..a12d537 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,40 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Jest Current File", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": [ + "${fileBasenameNoExtension}", + "--config", + "jest.config.js" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "disableOptimisticBPs": true, + "windows": { + "program": "${workspaceFolder}/node_modules/jest/bin/jest", + } + }, + { + "type": "node", + "request": "launch", + "name": "Jest Tests", + "program": "${workspaceRoot}\\node_modules\\jest\\bin\\jest.js", + "args": [ + "-i" + ], + "preLaunchTask": "build", + "internalConsoleOptions": "openOnSessionStart", + "outFiles": [ + "${workspaceRoot}/dist/**/*" + ], + "envFile": "${workspaceRoot}/.env" + } + ] +} \ No newline at end of file diff --git a/jest_config_js b/jest_config_js new file mode 100644 index 0000000..61958f9 --- /dev/null +++ b/jest_config_js @@ -0,0 +1,3 @@ +module.exports = { + "testEnvironment": "node" +} \ No newline at end of file diff --git a/src/components/MessagePanel/message-panel.jsx b/src/components/MessagePanel/message-panel.jsx index 8f3a5c0..1e97778 100644 --- a/src/components/MessagePanel/message-panel.jsx +++ b/src/components/MessagePanel/message-panel.jsx @@ -82,6 +82,7 @@ class MessagePanel extends Component { if (!event.data.messageId) { console.warn('Message has no messageId.'); + return false; } if (!event.data.messageType) { diff --git a/tests/reducers/hook-reducers.test.js b/tests/reducers/hook-reducers.test.js index c75a0b8..bf1c573 100644 --- a/tests/reducers/hook-reducers.test.js +++ b/tests/reducers/hook-reducers.test.js @@ -37,7 +37,7 @@ describe('Hook Reducer', () => { link: { url: 'http://localhost:8080/foo/bar/ignored', }, - sourceWindow: 'abcdefgh-ijklm-nopqrstuvwxyz', + sourceWindow: 'abcdefgh-ijkl-mnop-qrst-uvwxyz012345', }; const expectedApp = { triggerPoint: action.triggerPoint, From 58f66e0f618d6a5e235d2b69c20e3593b64e1036 Mon Sep 17 00:00:00 2001 From: Carl Anderson Date: Fri, 13 Mar 2020 14:38:23 -0500 Subject: [PATCH 6/7] fix: accidentally commited, removed --- .vscode/launch.json | 40 ---------------------------------------- jest_config_js | 3 --- 2 files changed, 43 deletions(-) delete mode 100644 .vscode/launch.json delete mode 100644 jest_config_js diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index a12d537..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Jest Current File", - "program": "${workspaceFolder}/node_modules/.bin/jest", - "args": [ - "${fileBasenameNoExtension}", - "--config", - "jest.config.js" - ], - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "disableOptimisticBPs": true, - "windows": { - "program": "${workspaceFolder}/node_modules/jest/bin/jest", - } - }, - { - "type": "node", - "request": "launch", - "name": "Jest Tests", - "program": "${workspaceRoot}\\node_modules\\jest\\bin\\jest.js", - "args": [ - "-i" - ], - "preLaunchTask": "build", - "internalConsoleOptions": "openOnSessionStart", - "outFiles": [ - "${workspaceRoot}/dist/**/*" - ], - "envFile": "${workspaceRoot}/.env" - } - ] -} \ No newline at end of file diff --git a/jest_config_js b/jest_config_js deleted file mode 100644 index 61958f9..0000000 --- a/jest_config_js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - "testEnvironment": "node" -} \ No newline at end of file From a8afe2be626530c17f2027be3c6978c41fe33104 Mon Sep 17 00:00:00 2001 From: Carl Anderson Date: Tue, 17 Mar 2020 11:16:25 -0500 Subject: [PATCH 7/7] Update message-panel.jsx Referencing the issue created to change the message ordering. --- src/components/MessagePanel/message-panel.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MessagePanel/message-panel.jsx b/src/components/MessagePanel/message-panel.jsx index 1e97778..4fc40fb 100644 --- a/src/components/MessagePanel/message-panel.jsx +++ b/src/components/MessagePanel/message-panel.jsx @@ -102,7 +102,7 @@ class MessagePanel extends Component { addMessage(event) { if (this.isActionableMessage(event)) { const message = JSON.stringify(event.data, null, 2); - // TODO: convert this to a stack so newer messages are on top. + // TODO(issue#129): convert this to a stack so newer messages are on top. this.setState({ messages: [...this.state.messages, message] }); this.replyMessage(event); }