diff --git a/lib/core/utils/collect-results-from-frames.js b/lib/core/utils/collect-results-from-frames.js index f1d80fe3a1..8ad27569a7 100644 --- a/lib/core/utils/collect-results-from-frames.js +++ b/lib/core/utils/collect-results-from-frames.js @@ -37,12 +37,7 @@ axe.utils.sendCommandToFrame = function(node, parameters, resolve, reject) { }, 500); // send 'axe.ping' to the frame - axe.utils.respondable(win, 'axe.ping', null, undefined, function( - pingResponse - ) { - if (!pingResponse || !pingResponse.axe === true) { - return; - } + axe.utils.respondable(win, 'axe.ping', null, undefined, function() { clearTimeout(timeout); // Give axe 60s (or user-supplied value) to respond to 'axe.start' diff --git a/lib/core/utils/respondable.js b/lib/core/utils/respondable.js index a72a5c2998..a2c2a0c276 100644 --- a/lib/core/utils/respondable.js +++ b/lib/core/utils/respondable.js @@ -86,6 +86,12 @@ _keepalive: keepalive }; + var axeRespondables = axe._cache.get('axeRespondables'); + if (!axeRespondables) { + axeRespondables = {}; + axe._cache.set('axeRespondables', axeRespondables); + } + axeRespondables[uuid] = true; if (typeof callback === 'function') { messages[uuid] = callback; } @@ -213,6 +219,18 @@ var uuid = data.uuid; + /** + * NOTE: messages from other contexts (frames) in response + * to a message should not contain a topic. We ignore these + * messages to prevent rogue postMessage handlers reflecting + * our messages. + * @see https://github.com/dequelabs/axe-core/issues/1754 + */ + var axeRespondables = axe._cache.get('axeRespondables') || {}; + if (axeRespondables[uuid] && data.topic && e.source !== window) { + return; + } + var keepalive = data._keepalive; var callback = messages[uuid]; @@ -230,7 +248,7 @@ try { publish(e.source, data, keepalive); } catch (err) { - post(e.source, data.topic, err, uuid, false); + post(e.source, null, err, uuid, false); } } }, diff --git a/test/core/public/run.js b/test/core/public/run.js index 45024e8565..976c081831 100644 --- a/test/core/public/run.js +++ b/test/core/public/run.js @@ -534,4 +534,46 @@ describe('axe.run iframes', function() { frame.src = '../mock/frames/test.html'; fixture.appendChild(frame); }); + + it('ignores unexpected messages from non-axe iframes', function(done) { + var frame = document.createElement('iframe'); + + frame.addEventListener('load', function() { + var safetyTimeout = window.setTimeout(function() { + done('timeout'); + }, 1000); + + axe.run('#fixture', {}, function(err, result) { + assert.isNull(err); + assert.equal(result.violations.length, 1); + window.clearTimeout(safetyTimeout); + done(); + }); + }); + + frame.src = '../mock/frames/with-echo.html'; + fixture.appendChild(frame); + }); + + it('ignores unexpected messages from axe iframes', function(done) { + var frame = document.createElement('iframe'); + + frame.addEventListener('load', function() { + var safetyTimeout = window.setTimeout(function() { + done('timeout'); + }, 1000); + if (!axe._audit) { + throw new Error('no _audit'); + } + axe.run('#fixture', {}, function(err, result) { + assert.isNull(err); + assert.equal(result.violations.length, 1); + window.clearTimeout(safetyTimeout); + done(); + }); + }); + + frame.src = '../mock/frames/with-echo-axe.html'; + fixture.appendChild(frame); + }); }); diff --git a/test/core/utils/respondable.js b/test/core/utils/respondable.js index 6f1b6a9519..6843b48eaa 100644 --- a/test/core/utils/respondable.js +++ b/test/core/utils/respondable.js @@ -118,7 +118,6 @@ describe('axe.utils.respondable', function() { event.data = JSON.stringify({ _respondable: true, _source: 'axeAPI.2.0.0', - topic: 'Death star', message: 'Help us Obi-Wan', uuid: mockUUID }); @@ -140,7 +139,6 @@ describe('axe.utils.respondable', function() { event.data = JSON.stringify({ _respondable: true, _source: 'axeAPI.x.y.z', - topic: 'Death star', message: 'Help us Obi-Wan', uuid: mockUUID }); @@ -164,7 +162,6 @@ describe('axe.utils.respondable', function() { event.data = JSON.stringify({ _respondable: true, _source: 'axeAPI.2.0.0', - topic: 'Death star', message: 'Help us Obi-Wan', uuid: mockUUID }); @@ -189,7 +186,6 @@ describe('axe.utils.respondable', function() { event.data = JSON.stringify({ _respondable: true, _source: 'axeAPI.2.0.0', - topic: 'Death star', message: 'Help us Obi-Wan', uuid: mockUUID }); @@ -210,7 +206,6 @@ describe('axe.utils.respondable', function() { event.initEvent('message', true, true); event.data = { _respondable: true, - topic: 'batman', uuid: mockUUID }; event.source = window; @@ -230,7 +225,6 @@ describe('axe.utils.respondable', function() { event.data = JSON.stringify({ _respondable: true, - topic: 'batman', uuid: mockUUID }) + 'joker tricks!'; event.source = window; @@ -249,8 +243,7 @@ describe('axe.utils.respondable', function() { event.initEvent('message', true, true); event.data = '{ "_respondable": true, "topic": "batman" }'; event.data = JSON.stringify({ - _respondable: true, - topic: 'batman' + _respondable: true }); event.source = window; @@ -269,7 +262,6 @@ describe('axe.utils.respondable', function() { event.data = '{ "_respondable": true, "topic": "batman", "uuid": "12" }'; event.data = JSON.stringify({ _respondable: true, - topic: 'batman', uuid: 'not-' + mockUUID }); event.source = window; @@ -288,7 +280,6 @@ describe('axe.utils.respondable', function() { event.initEvent('message', true, true); event.data = '{ "uuid": "48", "topic": "batman" }'; event.data = JSON.stringify({ - topic: 'batman', uuid: mockUUID }); event.source = window; @@ -308,7 +299,6 @@ describe('axe.utils.respondable', function() { event.data = JSON.stringify({ _respondable: true, _source: 'axeAPI.2.0.0', - topic: 'Death star', error: { name: 'ReferenceError', message: 'The exhaust port is open!', @@ -337,7 +327,6 @@ describe('axe.utils.respondable', function() { event.data = JSON.stringify({ _respondable: true, _source: 'axeAPI.2.0.0', - topic: 'Death star', error: { name: 'evil', message: 'The exhaust port is open!', diff --git a/test/integration/full/frame-tested/frame-tested-echoes-postmessage.html b/test/integration/full/frame-tested/frame-tested-echoes-postmessage.html deleted file mode 100644 index 7c44b0d5f4..0000000000 --- a/test/integration/full/frame-tested/frame-tested-echoes-postmessage.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - frame-tested echoes postMessage test - - - - - - - - - -
- - - - - diff --git a/test/integration/full/frame-tested/frame-tested-echoes-postmessage.js b/test/integration/full/frame-tested/frame-tested-echoes-postmessage.js deleted file mode 100644 index 8842117b47..0000000000 --- a/test/integration/full/frame-tested/frame-tested-echoes-postmessage.js +++ /dev/null @@ -1,28 +0,0 @@ -describe('frame-tested-echoes-postmessage test', function() { - 'use strict'; - - var results; - before(function(done) { - axe.testUtils.awaitNestedLoad(function() { - axe.run( - { - runOnly: { type: 'rule', values: ['frame-tested'] }, - checks: { - 'frame-tested': { options: { isViolation: true } } - } - }, - function(err, r) { - assert.isNull(err); - results = r; - done(); - } - ); - }); - }); - - describe('result', function() { - it('should not error', function() { - assert.lengthOf(results.violations, 0); - }); - }); -}); diff --git a/test/mock/frames/responder.html b/test/mock/frames/responder.html index eb3baf767c..e382bae67c 100644 --- a/test/mock/frames/responder.html +++ b/test/mock/frames/responder.html @@ -11,6 +11,7 @@ version: '2.0.0' }; + + + + + + +

Frame with axe-core

+
Target in iframe
+ + + diff --git a/test/integration/full/frame-tested/frames/with-echo.html b/test/mock/frames/with-echo.html similarity index 57% rename from test/integration/full/frame-tested/frames/with-echo.html rename to test/mock/frames/with-echo.html index 963328b9c8..870114dd2e 100644 --- a/test/integration/full/frame-tested/frames/with-echo.html +++ b/test/mock/frames/with-echo.html @@ -1,16 +1,17 @@ - Frame without axe-core + Echo frame without axe-core

Frame without axe-core

+
Target in iframe