1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script src="/tests/SimpleTest/SimpleTest.js"></script>
</head>
<body>
<script class="testbody" type="application/javascript">
"use strict";
var { IdpSandbox } = SpecialPowers.ChromeUtils.import(
"resource://gre/modules/media/IdpSandbox.jsm"
);
var dummyPayload = JSON.stringify({
this: 'is',
a: ['stu', 6],
obj: null
});
function test_domain_sandbox() {
var diabolical = {
toString() {
return 'example.com/path';
}
};
var domains = [ 'ex/foo', 'user@ex', 'user:pass@ex', 'ex#foo', 'ex?foo',
'', 12, null, diabolical, true ];
domains.forEach(function(domain) {
try {
var idp = new IdpSandbox(domain, undefined, window);
ok(false, 'IdpSandbox allowed a bad domain: ' + domain);
} catch (e) {
var str = (typeof domain === 'string') ? domain : typeof domain;
ok(true, 'Evil domain "' + str + '" raises exception');
}
});
}
function test_protocol_sandbox() {
var protos = [ '../evil/proto', '..%2Fevil%2Fproto',
'\\evil', '%5cevil', 12, true, {} ];
protos.forEach(function(proto) {
try {
var idp = new IdpSandbox('example.com', proto, window);
ok(false, 'IdpSandbox allowed a bad protocol: ' + proto);
} catch (e) {
var str = (typeof proto === 'string') ? proto : typeof proto;
ok(true, 'Evil protocol "' + proto + '" raises exception');
}
});
}
function idpName(hash) {
return 'idp.js' + (hash ? ('#' + hash) : '');
}
function makeSandbox(js) {
var name = js || idpName();
info('Creating a sandbox for the protocol: ' + name);
var sandbox = new IdpSandbox('example.com', name, window);
return sandbox.start().then(idp => SpecialPowers.wrap(idp));
}
function test_generate_assertion() {
return makeSandbox()
.then(idp => idp.generateAssertion(dummyPayload,
'https://example.net',
{}))
.then(response => {
response = SpecialPowers.wrap(response);
is(response.idp.domain, 'example.com', 'domain is correct');
is(response.idp.protocol, 'idp.js', 'protocol is correct');
ok(typeof response.assertion === 'string', 'assertion is present');
});
}
// test that the test IdP can eat its own dogfood; which is the only way to test
// validateAssertion, since that consumes the output of generateAssertion (in
// theory, generateAssertion could identify a different IdP domain).
function test_validate_assertion() {
return makeSandbox()
.then(idp => idp.generateAssertion(dummyPayload,
'https://example.net',
{ usernameHint: 'user' }))
.then(assertion => {
var wrapped = SpecialPowers.wrap(assertion);
return makeSandbox()
.then(idp => idp.validateAssertion(wrapped.assertion,
'https://example.net'));
}).then(response => {
response = SpecialPowers.wrap(response);
is(response.identity, 'user@example.com');
is(response.contents, dummyPayload);
});
}
// We don't want to test the #bad or the #hang instructions,
// errors of the sort those generate aren't handled by the sandbox code.
function test_assertion_failure(reason) {
return () => {
return makeSandbox(idpName(reason))
.then(idp => idp.generateAssertion('hello', 'example.net', {}))
.then(r => ok(false, 'should not succeed on ' + reason),
e => ok(true, 'failed correctly on ' + reason));
};
}
function test_load_failure() {
return makeSandbox('non-existent-file')
.then(() => ok(false, 'Should fail to load non-existent file'),
e => ok(e, 'Should fail to load non-existent file'));
}
function test_redirect_ok(from) {
return () => {
return makeSandbox(from)
.then(idp => idp.generateAssertion('hello', 'example.net'))
.then(r => ok(SpecialPowers.wrap(r).assertion,
'Redirect to https should be OK'));
};
}
function test_redirect_fail(from) {
return () => {
return makeSandbox(from)
.then(() => ok(false, 'Redirect to https should fail'),
e => ok(e, 'Redirect to https should fail'));
};
}
function test_bad_js() {
return makeSandbox('idp-bad.js')
.then(() => ok(false, 'Bad JS should not load'),
e => ok(e, 'Bad JS should not load'));
}
function run_all_tests() {
[
test_domain_sandbox,
test_protocol_sandbox,
test_generate_assertion,
test_validate_assertion,
// fail of the IdP fails
test_assertion_failure('fail'),
// fail if the IdP throws
test_assertion_failure('throw'),
// fail if the IdP is not ready
test_assertion_failure('not_ready'),
test_load_failure(),
// Test a redirect to an HTTPS origin, which should be OK
test_redirect_ok('idp-redirect-https.js'),
// Two redirects is fine too
test_redirect_ok('idp-redirect-https-double.js'),
// A secure redirect to a path other than /.well-known/idp-proxy/* should
// also work fine.
test_redirect_ok('idp-redirect-https-odd-path.js'),
// A redirect to HTTP is not-cool
test_redirect_fail('idp-redirect-http.js'),
// Also catch tricks like https->http->https
test_redirect_fail('idp-redirect-http-trick.js'),
test_bad_js
].reduce((p, test) => {
return p.then(test)
.catch(e => ok(false, test.name + ' failed: ' +
SpecialPowers.wrap(e).message + '\n' +
SpecialPowers.wrap(e).stack));
}, Promise.resolve())
.then(() => SimpleTest.finish());
}
SimpleTest.waitForExplicitFinish();
run_all_tests();
</script>
</body>
</html>
|