Skip to content

Commit

Permalink
add message origin checking for SMART Web Messaging (#126)
Browse files Browse the repository at this point in the history
* add message origin checking for SMART Web Messaging

* fix: unit test change needed to support origin checking

* wip: refactored event checking logic to be easier to folloow.

* fix: broken origin checking in the hook reducer

* fix: separate behavior changes for a future PR

* fix: accidentally commited, removed

* Update message-panel.jsx

Referencing the issue created to change the message ordering.
  • Loading branch information
Carl Anderson authored Apr 1, 2020
1 parent a2136d1 commit 2ff8644
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 22 deletions.
44 changes: 25 additions & 19 deletions src/components/MessagePanel/message-panel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -74,32 +75,37 @@ class MessagePanel extends Component {
event.source.postMessage(msgStructure, event.origin);
}

addMessage(event) {
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;
}

console.log(`Received message from ${event.origin}.`);
isActionableMessage(event) {
// Ignore the event, if it doesn't meet our expectations.
if (!event.data) return false;
if ((event.data.source || "").startsWith('react-devtools-')) return false;

if (!event.data.messageId) {
console.log('Message did not have a messageId and will be ignored.');
return;
console.warn('Message has no messageId.');
return false;
}

if (!event.data.messageType) {
console.log('Message did not have a messageType and will be ignored.');
return;
console.warn('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.warn(`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)) {
const message = JSON.stringify(event.data, null, 2);
// TODO(issue#129): convert this to a stack so newer messages are on top.
this.setState({ messages: [...this.state.messages, message] });
this.replyMessage(event);
}
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/middleware/cds-execution.js
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
4 changes: 3 additions & 1 deletion src/reducers/hook-reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -61,6 +62,7 @@ const hookReducers = (state = initialState, action) => {
{
triggerPoint: action.triggerPoint,
link: action.link,
linkOrigin: url.origin,
windowId,
},
],
Expand Down
2 changes: 1 addition & 1 deletion tests/middleware/cds-execution.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`
);

Expand Down
23 changes: 23 additions & 0 deletions tests/reducers/hook-reducers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ describe('Hook Reducer', () => {

beforeEach(() => {
state = {
apps: [],
currentHook: 'patient-view',
isLoadingData: false,
isContextVisible: true,
Expand All @@ -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-ijkl-mnop-qrst-uvwxyz012345',
};
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 = {
Expand Down

0 comments on commit 2ff8644

Please sign in to comment.