Skip to content

Commit ff7edbb

Browse files
author
Jonathan Ginsburg
committedFeb 10, 2022
fix(security): mitigate the "Open Redirect Vulnerability"
1 parent c1befa0 commit ff7edbb

File tree

5 files changed

+58
-9
lines changed

5 files changed

+58
-9
lines changed
 

‎client/karma.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,15 @@ function Karma (updater, socket, iframe, opener, navigator, location, document)
239239
self.updater.updateTestStatus('complete')
240240
}
241241
if (returnUrl) {
242-
if (!/^https?:\/\//.test(returnUrl)) {
242+
var isReturnUrlAllowed = false
243+
for (var i = 0; i < this.config.allowedReturnUrlPatterns.length; i++) {
244+
var allowedReturnUrlPattern = new RegExp(this.config.allowedReturnUrlPatterns[i])
245+
if (allowedReturnUrlPattern.test(returnUrl)) {
246+
isReturnUrlAllowed = true
247+
break
248+
}
249+
}
250+
if (!isReturnUrlAllowed) {
243251
throw new Error(
244252
'Security: Navigation to '.concat(
245253
returnUrl,

‎docs/config/01-configuration-file.md

+9
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,15 @@ upon the completion of running the tests. Setting this to false is useful when e
277277

278278
If true, Karma does not display the banner and browser list. Useful when using karma on component tests with screenshots.
279279

280+
## client.allowedReturnUrlPatterns
281+
**Type:** Array
282+
283+
**Default:** `['^https?://']`
284+
285+
**Description:** Define the string representations of the regular expressions that will be allowed for the `return_url` query parameter.
286+
287+
If the value of the `return_url` query parameter does not match any regular expression derived from the string representation of each of the elements of this array, navigation to it will be blocked.
288+
280289
## colors
281290
**Type:** Boolean
282291

‎lib/config.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,19 @@ let TYPE_SCRIPT_AVAILABLE = false
1717
try {
1818
require('coffeescript').register()
1919
COFFEE_SCRIPT_AVAILABLE = true
20-
} catch (e) {}
20+
} catch {}
2121

2222
// LiveScript is required here to enable config files written in LiveScript.
2323
// It's not directly used in this file.
2424
try {
2525
require('LiveScript')
2626
LIVE_SCRIPT_AVAILABLE = true
27-
} catch (e) {}
27+
} catch {}
2828

2929
try {
3030
require('ts-node')
3131
TYPE_SCRIPT_AVAILABLE = true
32-
} catch (e) {}
32+
} catch {}
3333

3434
class Pattern {
3535
constructor (pattern, served, included, watched, nocache, type, isBinary) {
@@ -324,7 +324,8 @@ class Config {
324324
useIframe: true,
325325
runInParent: false,
326326
captureConsole: true,
327-
clearContext: true
327+
clearContext: true,
328+
allowedReturnUrlPatterns: ['^https?://']
328329
}
329330
this.browserDisconnectTimeout = 2000
330331
this.browserDisconnectTolerance = 0

‎static/karma.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,15 @@ function Karma (updater, socket, iframe, opener, navigator, location, document)
249249
self.updater.updateTestStatus('complete')
250250
}
251251
if (returnUrl) {
252-
if (!/^https?:\/\//.test(returnUrl)) {
252+
var isReturnUrlAllowed = false
253+
for (var i = 0; i < this.config.allowedReturnUrlPatterns.length; i++) {
254+
var allowedReturnUrlPattern = new RegExp(this.config.allowedReturnUrlPatterns[i])
255+
if (allowedReturnUrlPattern.test(returnUrl)) {
256+
isReturnUrlAllowed = true
257+
break
258+
}
259+
}
260+
if (!isReturnUrlAllowed) {
253261
throw new Error(
254262
'Security: Navigation to '.concat(
255263
returnUrl,

‎test/client/karma.spec.js

+26-3
Original file line numberDiff line numberDiff line change
@@ -442,15 +442,18 @@ describe('Karma', function () {
442442
assert(spyResult.called)
443443
})
444444

445-
it('should navigate the client to return_url if specified', function (done) {
445+
it('should navigate the client to return_url if specified and allowed', function (done) {
446+
var config = {
447+
// The default value.
448+
allowedReturnUrlPatterns: ['^https?://']
449+
}
446450
windowLocation.search = '?id=567&return_url=http://return.com'
447451
socket = new MockSocket()
448452
k = new ClientKarma(updater, socket, iframe, windowStub, windowNavigator, windowLocation)
449453
clientWindow = { karma: k }
450454
ck = new ContextKarma(ContextKarma.getDirectCallParentKarmaMethod(clientWindow))
451-
ck.config = {}
455+
socket.emit('execute', config)
452456

453-
sinon.spy(socket, 'disconnect')
454457
clock.tick(500)
455458

456459
ck.complete()
@@ -462,6 +465,26 @@ describe('Karma', function () {
462465
clock.tick(10)
463466
})
464467

468+
it('should not navigate the client to return_url if not allowed', function () {
469+
var config = {
470+
allowedReturnUrlPatterns: []
471+
}
472+
473+
windowLocation.search = '?id=567&return_url=javascript:alert(document.domain)'
474+
socket = new MockSocket()
475+
k = new ClientKarma(updater, socket, iframe, windowStub, windowNavigator, windowLocation)
476+
clientWindow = { karma: k }
477+
ck = new ContextKarma(ContextKarma.getDirectCallParentKarmaMethod(clientWindow))
478+
socket.emit('execute', config)
479+
480+
try {
481+
ck.complete()
482+
throw new Error('An error should have been caught.')
483+
} catch (error) {
484+
assert(/Error: Security: Navigation to .* was blocked to prevent malicious exploits./.test(error))
485+
}
486+
})
487+
465488
it('should clear context window upon complete when clearContext config is true', function () {
466489
var config = ck.config = {
467490
clearContext: true

1 commit comments

Comments
 (1)

Lauznis89 commented on Apr 9, 2022

@Lauznis89

Es esmu cilvēks

Please sign in to comment.