alexa-cookie2 5.0.2 → 5.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +6 -0
- package/alexa-cookie.js +86 -20
- package/lib/proxy.js +131 -44
- package/package.json +6 -6
- package/test/base-amazon-page-handle.test.js +123 -0
- package/test/proxy-cookie-rewrite.test.js +148 -0
- package/test/proxy-cookie-sanitizing.test.js +186 -0
- package/test/proxy-host-switch.test.js +166 -0
- package/test/proxy-initial-url.test.js +184 -0
- package/test/proxy-success-requires-auth-code.test.js +170 -0
- package/test/refresh-skips-app-registration.test.js +270 -0
- package/test/registration-hardening.test.js +261 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const vm = require('vm');
|
|
5
|
+
|
|
6
|
+
const testName = 'base-amazon-page-handle';
|
|
7
|
+
const outputDir = process.env.ALEXA_COOKIE_TEST_OUTPUT_DIR || path.join(__dirname, '..', 'test-output');
|
|
8
|
+
const outputFile = path.join(outputDir, `${testName}.txt`);
|
|
9
|
+
const lines = [];
|
|
10
|
+
|
|
11
|
+
function line(value = '') {
|
|
12
|
+
lines.push(value);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function writeOutput() {
|
|
16
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
17
|
+
fs.writeFileSync(outputFile, `${lines.join('\n')}\n`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function recordAssertion(description, fn) {
|
|
21
|
+
try {
|
|
22
|
+
fn();
|
|
23
|
+
line(`${description}: PASS`);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
line(`${description}: FAIL`);
|
|
26
|
+
writeOutput();
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const cookieFile = path.join(__dirname, '..', 'alexa-cookie.js');
|
|
32
|
+
const source = fs.readFileSync(cookieFile, 'utf8');
|
|
33
|
+
|
|
34
|
+
let capturedProxyOptions;
|
|
35
|
+
|
|
36
|
+
function loadCookieModule() {
|
|
37
|
+
capturedProxyOptions = undefined;
|
|
38
|
+
const module = { exports: {} };
|
|
39
|
+
const sandbox = {
|
|
40
|
+
__dirname: path.dirname(cookieFile),
|
|
41
|
+
console,
|
|
42
|
+
module,
|
|
43
|
+
exports: module.exports,
|
|
44
|
+
require(name) {
|
|
45
|
+
if (name === './lib/proxy.js') {
|
|
46
|
+
return {
|
|
47
|
+
initAmazonProxy(options, _callbackCookie, callbackListening) {
|
|
48
|
+
capturedProxyOptions = options;
|
|
49
|
+
callbackListening && callbackListening({
|
|
50
|
+
address: () => ({ port: options.proxyPort || 3456 }),
|
|
51
|
+
close: (callback) => callback && callback()
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (name === 'cookie') {
|
|
57
|
+
return { parse: () => ({}) };
|
|
58
|
+
}
|
|
59
|
+
return require(name);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
vm.runInNewContext(source, sandbox, { filename: cookieFile });
|
|
63
|
+
return module.exports;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function runProxyOnlyConfig(baseAmazonPage) {
|
|
67
|
+
const cookieModule = loadCookieModule();
|
|
68
|
+
const input = {
|
|
69
|
+
proxyOwnIp: '127.0.0.1',
|
|
70
|
+
proxyPort: 3456,
|
|
71
|
+
baseAmazonPage,
|
|
72
|
+
amazonPage: baseAmazonPage,
|
|
73
|
+
logger: () => {}
|
|
74
|
+
};
|
|
75
|
+
cookieModule.generateAlexaCookie('', '', input, () => {});
|
|
76
|
+
return { input, output: capturedProxyOptions };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const de = runProxyOnlyConfig('amazon.de');
|
|
81
|
+
const uk = runProxyOnlyConfig('amazon.co.uk');
|
|
82
|
+
const com = runProxyOnlyConfig('amazon.com');
|
|
83
|
+
|
|
84
|
+
line('TEST: base Amazon page handle');
|
|
85
|
+
line('');
|
|
86
|
+
line('CODE UNDER TEST:');
|
|
87
|
+
line('- alexa-cookie.js: generateAlexaCookie option initialization');
|
|
88
|
+
line('- option passed to lib/proxy.js: baseAmazonPageHandle');
|
|
89
|
+
line('');
|
|
90
|
+
line('INPUT:');
|
|
91
|
+
line(`DE baseAmazonPage: ${de.input.baseAmazonPage}`);
|
|
92
|
+
line(`DE amazonPage: ${de.input.amazonPage}`);
|
|
93
|
+
line(`UK baseAmazonPage: ${uk.input.baseAmazonPage}`);
|
|
94
|
+
line(`UK amazonPage: ${uk.input.amazonPage}`);
|
|
95
|
+
line(`COM baseAmazonPage: ${com.input.baseAmazonPage}`);
|
|
96
|
+
line(`COM amazonPage: ${com.input.amazonPage}`);
|
|
97
|
+
line('');
|
|
98
|
+
line('OBSERVED:');
|
|
99
|
+
line(`DE baseAmazonPageHandle: ${de.output.baseAmazonPageHandle}`);
|
|
100
|
+
line(`UK baseAmazonPageHandle: ${uk.output.baseAmazonPageHandle}`);
|
|
101
|
+
line(`COM baseAmazonPageHandle: ${com.output.baseAmazonPageHandle}`);
|
|
102
|
+
line('');
|
|
103
|
+
line('ASSERTIONS:');
|
|
104
|
+
recordAssertion('DE baseAmazonPageHandle === "_de"', () => {
|
|
105
|
+
assert.strictEqual(de.output.baseAmazonPageHandle, '_de');
|
|
106
|
+
});
|
|
107
|
+
recordAssertion('UK baseAmazonPageHandle === "_uk"', () => {
|
|
108
|
+
assert.strictEqual(uk.output.baseAmazonPageHandle, '_uk');
|
|
109
|
+
});
|
|
110
|
+
recordAssertion('COM baseAmazonPageHandle === ""', () => {
|
|
111
|
+
assert.strictEqual(com.output.baseAmazonPageHandle, '');
|
|
112
|
+
});
|
|
113
|
+
line('');
|
|
114
|
+
line('RESULT: PASS');
|
|
115
|
+
writeOutput();
|
|
116
|
+
} catch (err) {
|
|
117
|
+
if (!lines.includes('RESULT: PASS')) {
|
|
118
|
+
line('');
|
|
119
|
+
line('RESULT: FAIL');
|
|
120
|
+
writeOutput();
|
|
121
|
+
}
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const vm = require('vm');
|
|
6
|
+
|
|
7
|
+
const testName = 'proxy-cookie-rewrite';
|
|
8
|
+
const outputDir = process.env.ALEXA_COOKIE_TEST_OUTPUT_DIR || path.join(__dirname, '..', 'test-output');
|
|
9
|
+
const outputFile = path.join(outputDir, `${testName}.txt`);
|
|
10
|
+
const lines = [];
|
|
11
|
+
|
|
12
|
+
function line(value = '') {
|
|
13
|
+
lines.push(value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function writeOutput() {
|
|
17
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
18
|
+
fs.writeFileSync(outputFile, `${lines.join('\n')}\n`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function recordAssertion(description, fn) {
|
|
22
|
+
try {
|
|
23
|
+
fn();
|
|
24
|
+
line(`${description}: PASS`);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
line(`${description}: FAIL`);
|
|
27
|
+
writeOutput();
|
|
28
|
+
throw err;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const proxyFile = path.join(__dirname, '..', 'lib', 'proxy.js');
|
|
33
|
+
const source = fs.readFileSync(proxyFile, 'utf8');
|
|
34
|
+
|
|
35
|
+
let capturedProxyOptions;
|
|
36
|
+
|
|
37
|
+
function createExpressStub() {
|
|
38
|
+
return {
|
|
39
|
+
use() {},
|
|
40
|
+
get() {},
|
|
41
|
+
listen() {
|
|
42
|
+
const server = {
|
|
43
|
+
address: () => ({ port: 3456 }),
|
|
44
|
+
on: () => server
|
|
45
|
+
};
|
|
46
|
+
return server;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function loadProxyModule() {
|
|
52
|
+
capturedProxyOptions = undefined;
|
|
53
|
+
const module = { exports: {} };
|
|
54
|
+
const sandbox = {
|
|
55
|
+
Buffer,
|
|
56
|
+
URL,
|
|
57
|
+
__dirname: path.dirname(proxyFile),
|
|
58
|
+
console,
|
|
59
|
+
module,
|
|
60
|
+
exports: module.exports,
|
|
61
|
+
require(name) {
|
|
62
|
+
if (name === 'express') return createExpressStub;
|
|
63
|
+
if (name === 'http-proxy-response-rewrite') return () => {};
|
|
64
|
+
if (name === 'http-proxy-middleware') {
|
|
65
|
+
return {
|
|
66
|
+
createProxyMiddleware(_context, options) {
|
|
67
|
+
capturedProxyOptions = options;
|
|
68
|
+
return function proxyMiddleware() {};
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (name === 'cookie') {
|
|
73
|
+
return { parse: () => ({}) };
|
|
74
|
+
}
|
|
75
|
+
return require(name);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
vm.runInNewContext(source, sandbox, { filename: proxyFile });
|
|
79
|
+
return module.exports;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const proxyModule = loadProxyModule();
|
|
83
|
+
const formerDataStorePath = path.join(os.tmpdir(), `alexa-cookie-proxy-cookie-test-${Date.now()}.json`);
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const input = {
|
|
87
|
+
proxyOwnIp: '127.0.0.1',
|
|
88
|
+
proxyPort: 3456,
|
|
89
|
+
proxyListenBind: '0.0.0.0',
|
|
90
|
+
baseAmazonPage: 'amazon.de',
|
|
91
|
+
baseAmazonPageHandle: '_de',
|
|
92
|
+
amazonPageProxyLanguage: 'de_DE',
|
|
93
|
+
acceptLanguage: 'de-DE',
|
|
94
|
+
proxyLogLevel: 'silent',
|
|
95
|
+
formerDataStorePath
|
|
96
|
+
};
|
|
97
|
+
proxyModule.initAmazonProxy(input);
|
|
98
|
+
const cookieDomainRewrite = capturedProxyOptions.cookieDomainRewrite || {};
|
|
99
|
+
const cookiePathRewrite = capturedProxyOptions.cookiePathRewrite || {};
|
|
100
|
+
const cookieDomainRewriteKeys = Object.keys(cookieDomainRewrite);
|
|
101
|
+
const cookiePathRewriteKeys = Object.keys(cookiePathRewrite);
|
|
102
|
+
|
|
103
|
+
line('TEST: proxy cookie rewrite');
|
|
104
|
+
line('');
|
|
105
|
+
line('CODE UNDER TEST:');
|
|
106
|
+
line('- lib/proxy.js: cookieDomainRewrite');
|
|
107
|
+
line('- lib/proxy.js: cookiePathRewrite');
|
|
108
|
+
line('');
|
|
109
|
+
line('INPUT:');
|
|
110
|
+
line(`proxyOwnIp: ${input.proxyOwnIp}`);
|
|
111
|
+
line(`baseAmazonPage: ${input.baseAmazonPage}`);
|
|
112
|
+
line('');
|
|
113
|
+
line('OBSERVED:');
|
|
114
|
+
line(`cookieDomainRewrite keys: ${cookieDomainRewriteKeys.join(', ')}`);
|
|
115
|
+
line(`cookieDomainRewrite["*"]: ${cookieDomainRewrite['*']}`);
|
|
116
|
+
line(`cookiePathRewrite keys: ${cookiePathRewriteKeys.join(', ')}`);
|
|
117
|
+
line(`cookiePathRewrite["*"]: ${cookiePathRewrite['*']}`);
|
|
118
|
+
line(`cookieDomainRewrite contains proxy IP: ${Object.values(cookieDomainRewrite).includes(input.proxyOwnIp)}`);
|
|
119
|
+
line('');
|
|
120
|
+
line('ASSERTIONS:');
|
|
121
|
+
recordAssertion('cookieDomainRewrite only has "*" key', () => {
|
|
122
|
+
assert.deepStrictEqual(cookieDomainRewriteKeys, ['*']);
|
|
123
|
+
});
|
|
124
|
+
recordAssertion('cookieDomainRewrite["*"] === ""', () => {
|
|
125
|
+
assert.strictEqual(cookieDomainRewrite['*'], '');
|
|
126
|
+
});
|
|
127
|
+
recordAssertion('cookieDomainRewrite does not contain proxy IP', () => {
|
|
128
|
+
assert.ok(!Object.values(cookieDomainRewrite).includes(input.proxyOwnIp), 'set-cookie domain must not be rewritten to an IP address');
|
|
129
|
+
});
|
|
130
|
+
recordAssertion('cookiePathRewrite only has "*" key', () => {
|
|
131
|
+
assert.deepStrictEqual(cookiePathRewriteKeys, ['*']);
|
|
132
|
+
});
|
|
133
|
+
recordAssertion('cookiePathRewrite["*"] === "/"', () => {
|
|
134
|
+
assert.strictEqual(cookiePathRewrite['*'], '/');
|
|
135
|
+
});
|
|
136
|
+
line('');
|
|
137
|
+
line('RESULT: PASS');
|
|
138
|
+
writeOutput();
|
|
139
|
+
} catch (err) {
|
|
140
|
+
if (!lines.includes('RESULT: PASS')) {
|
|
141
|
+
line('');
|
|
142
|
+
line('RESULT: FAIL');
|
|
143
|
+
writeOutput();
|
|
144
|
+
}
|
|
145
|
+
throw err;
|
|
146
|
+
} finally {
|
|
147
|
+
fs.rmSync(formerDataStorePath, { force: true });
|
|
148
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const vm = require('vm');
|
|
6
|
+
|
|
7
|
+
const testName = 'proxy-cookie-sanitizing';
|
|
8
|
+
const outputDir = process.env.ALEXA_COOKIE_TEST_OUTPUT_DIR || path.join(__dirname, '..', 'test-output');
|
|
9
|
+
const outputFile = path.join(outputDir, `${testName}.txt`);
|
|
10
|
+
const lines = [];
|
|
11
|
+
|
|
12
|
+
function line(value = '') {
|
|
13
|
+
lines.push(value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function writeOutput() {
|
|
17
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
18
|
+
fs.writeFileSync(outputFile, `${lines.join('\n')}\n`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function recordAssertion(description, fn) {
|
|
22
|
+
try {
|
|
23
|
+
fn();
|
|
24
|
+
line(`${description}: PASS`);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
line(`${description}: FAIL`);
|
|
27
|
+
writeOutput();
|
|
28
|
+
throw err;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const proxyFile = path.join(__dirname, '..', 'lib', 'proxy.js');
|
|
33
|
+
const source = fs.readFileSync(proxyFile, 'utf8');
|
|
34
|
+
|
|
35
|
+
let capturedProxyOptions;
|
|
36
|
+
|
|
37
|
+
function createExpressStub() {
|
|
38
|
+
return {
|
|
39
|
+
use() {},
|
|
40
|
+
get() {},
|
|
41
|
+
listen() {
|
|
42
|
+
const server = {
|
|
43
|
+
address: () => ({ port: 3456 }),
|
|
44
|
+
on: () => server
|
|
45
|
+
};
|
|
46
|
+
return server;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function loadProxyModule() {
|
|
52
|
+
capturedProxyOptions = undefined;
|
|
53
|
+
const module = { exports: {} };
|
|
54
|
+
const sandbox = {
|
|
55
|
+
Buffer,
|
|
56
|
+
URL,
|
|
57
|
+
__dirname: path.dirname(proxyFile),
|
|
58
|
+
console,
|
|
59
|
+
module,
|
|
60
|
+
exports: module.exports,
|
|
61
|
+
require(name) {
|
|
62
|
+
if (name === 'express') return createExpressStub;
|
|
63
|
+
if (name === 'http-proxy-response-rewrite') return () => {};
|
|
64
|
+
if (name === 'http-proxy-middleware') {
|
|
65
|
+
return {
|
|
66
|
+
createProxyMiddleware(_context, options) {
|
|
67
|
+
capturedProxyOptions = options;
|
|
68
|
+
return function proxyMiddleware() {};
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return require(name);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
vm.runInNewContext(source, sandbox, { filename: proxyFile });
|
|
76
|
+
return module.exports;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function createProxyRequest(cookie) {
|
|
80
|
+
const headers = {
|
|
81
|
+
cookie,
|
|
82
|
+
host: 'www.amazon.de',
|
|
83
|
+
origin: 'https://www.amazon.de'
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
getHeader(name) {
|
|
87
|
+
return headers[name.toLowerCase()];
|
|
88
|
+
},
|
|
89
|
+
setHeader(name, value) {
|
|
90
|
+
headers[name.toLowerCase()] = value;
|
|
91
|
+
},
|
|
92
|
+
getHeaders() {
|
|
93
|
+
return headers;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function createProxyResponse(location) {
|
|
99
|
+
return {
|
|
100
|
+
statusCode: 302,
|
|
101
|
+
headers: {
|
|
102
|
+
location
|
|
103
|
+
},
|
|
104
|
+
socket: {
|
|
105
|
+
_host: 'www.amazon.de',
|
|
106
|
+
parser: {
|
|
107
|
+
outgoing: {
|
|
108
|
+
method: 'GET',
|
|
109
|
+
path: '/ap/maplanding?openid.oa2.authorization_code=AUTH_CODE_FROM_PROXY',
|
|
110
|
+
getHeader() {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const proxyModule = loadProxyModule();
|
|
120
|
+
const formerDataStorePath = path.join(os.tmpdir(), `alexa-cookie-proxy-cookie-sanitizing-test-${Date.now()}.json`);
|
|
121
|
+
let callbackData;
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const input = {
|
|
125
|
+
proxyOwnIp: '127.0.0.1',
|
|
126
|
+
proxyPort: 3456,
|
|
127
|
+
proxyListenBind: '0.0.0.0',
|
|
128
|
+
baseAmazonPage: 'amazon.de',
|
|
129
|
+
baseAmazonPageHandle: '_de',
|
|
130
|
+
amazonPageProxyLanguage: 'de_DE',
|
|
131
|
+
acceptLanguage: 'de-DE',
|
|
132
|
+
proxyLogLevel: 'silent',
|
|
133
|
+
formerDataStorePath
|
|
134
|
+
};
|
|
135
|
+
proxyModule.initAmazonProxy(input, (_err, data) => {
|
|
136
|
+
callbackData = data;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const proxyReq = createProxyRequest('connect.sid=LOCAL_BROWSER; session-id=SID_BROWSER; ubid-acbde=UBID_BROWSER');
|
|
140
|
+
const req = {
|
|
141
|
+
method: 'GET',
|
|
142
|
+
url: '/www.amazon.de/ap/maplanding?openid.oa2.authorization_code=AUTH_CODE_FROM_PROXY',
|
|
143
|
+
originalUrl: '/www.amazon.de/ap/maplanding?openid.oa2.authorization_code=AUTH_CODE_FROM_PROXY',
|
|
144
|
+
headers: {
|
|
145
|
+
host: '127.0.0.1:3456'
|
|
146
|
+
},
|
|
147
|
+
on() {}
|
|
148
|
+
};
|
|
149
|
+
capturedProxyOptions.onProxyReq(proxyReq, req);
|
|
150
|
+
capturedProxyOptions.onProxyRes(createProxyResponse('https://www.amazon.de/ap/maplanding?openid.oa2.authorization_code=AUTH_CODE_FROM_PROXY'), req, {});
|
|
151
|
+
|
|
152
|
+
line('TEST: proxy callback sanitizes login cookie');
|
|
153
|
+
line('');
|
|
154
|
+
line('CODE UNDER TEST:');
|
|
155
|
+
line('- lib/proxy.js: onProxyReq() proxy cookie capture');
|
|
156
|
+
line('- lib/proxy.js: callbackCookie loginCookie value');
|
|
157
|
+
line('');
|
|
158
|
+
line('OBSERVED:');
|
|
159
|
+
line(`loginCookie: ${callbackData && callbackData.loginCookie}`);
|
|
160
|
+
line('');
|
|
161
|
+
line('ASSERTIONS:');
|
|
162
|
+
recordAssertion('callback data is present', () => {
|
|
163
|
+
assert.ok(callbackData);
|
|
164
|
+
});
|
|
165
|
+
recordAssertion('loginCookie keeps Amazon session-id', () => {
|
|
166
|
+
assert.ok(callbackData.loginCookie.includes('session-id=SID_BROWSER'));
|
|
167
|
+
});
|
|
168
|
+
recordAssertion('loginCookie keeps Amazon ubid cookie', () => {
|
|
169
|
+
assert.ok(callbackData.loginCookie.includes('ubid-acbde=UBID_BROWSER'));
|
|
170
|
+
});
|
|
171
|
+
recordAssertion('loginCookie removes unrelated local browser cookie', () => {
|
|
172
|
+
assert.ok(!callbackData.loginCookie.includes('connect.sid=LOCAL_BROWSER'));
|
|
173
|
+
});
|
|
174
|
+
line('');
|
|
175
|
+
line('RESULT: PASS');
|
|
176
|
+
writeOutput();
|
|
177
|
+
} catch (err) {
|
|
178
|
+
if (!lines.includes('RESULT: PASS')) {
|
|
179
|
+
line('');
|
|
180
|
+
line('RESULT: FAIL');
|
|
181
|
+
writeOutput();
|
|
182
|
+
}
|
|
183
|
+
throw err;
|
|
184
|
+
} finally {
|
|
185
|
+
fs.rmSync(formerDataStorePath, { force: true });
|
|
186
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const vm = require('vm');
|
|
6
|
+
|
|
7
|
+
const testName = 'proxy-host-switch';
|
|
8
|
+
const outputDir = process.env.ALEXA_COOKIE_TEST_OUTPUT_DIR || path.join(__dirname, '..', 'test-output');
|
|
9
|
+
const outputFile = path.join(outputDir, `${testName}.txt`);
|
|
10
|
+
const lines = [];
|
|
11
|
+
|
|
12
|
+
function line(value = '') {
|
|
13
|
+
lines.push(value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function writeOutput() {
|
|
17
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
18
|
+
fs.writeFileSync(outputFile, `${lines.join('\n')}\n`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function recordAssertion(description, fn) {
|
|
22
|
+
try {
|
|
23
|
+
fn();
|
|
24
|
+
line(`${description}: PASS`);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
line(`${description}: FAIL`);
|
|
27
|
+
writeOutput();
|
|
28
|
+
throw err;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const proxyFile = path.join(__dirname, '..', 'lib', 'proxy.js');
|
|
33
|
+
const source = fs.readFileSync(proxyFile, 'utf8');
|
|
34
|
+
|
|
35
|
+
let capturedProxyOptions;
|
|
36
|
+
|
|
37
|
+
function createExpressStub() {
|
|
38
|
+
return {
|
|
39
|
+
use() {},
|
|
40
|
+
get() {},
|
|
41
|
+
listen() {
|
|
42
|
+
const server = {
|
|
43
|
+
address: () => ({ port: 3456 }),
|
|
44
|
+
on: () => server
|
|
45
|
+
};
|
|
46
|
+
return server;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function loadProxyModule() {
|
|
52
|
+
capturedProxyOptions = undefined;
|
|
53
|
+
const module = { exports: {} };
|
|
54
|
+
const sandbox = {
|
|
55
|
+
Buffer,
|
|
56
|
+
URL,
|
|
57
|
+
__dirname: path.dirname(proxyFile),
|
|
58
|
+
console,
|
|
59
|
+
module,
|
|
60
|
+
exports: module.exports,
|
|
61
|
+
require(name) {
|
|
62
|
+
if (name === 'express') return createExpressStub;
|
|
63
|
+
if (name === 'http-proxy-response-rewrite') return () => {};
|
|
64
|
+
if (name === 'http-proxy-middleware') {
|
|
65
|
+
return {
|
|
66
|
+
createProxyMiddleware(_context, options) {
|
|
67
|
+
capturedProxyOptions = options;
|
|
68
|
+
return function proxyMiddleware() {};
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (name === 'cookie') {
|
|
73
|
+
return { parse: () => ({}) };
|
|
74
|
+
}
|
|
75
|
+
return require(name);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
vm.runInNewContext(source, sandbox, { filename: proxyFile });
|
|
79
|
+
return module.exports;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function applyPathRewrite(pathname, req) {
|
|
83
|
+
const rewrite = capturedProxyOptions.pathRewrite;
|
|
84
|
+
if (typeof rewrite === 'function') return rewrite(pathname, req);
|
|
85
|
+
for (const pattern of Object.keys(rewrite)) {
|
|
86
|
+
const regex = new RegExp(pattern);
|
|
87
|
+
if (regex.test(pathname)) return pathname.replace(regex, rewrite[pattern]);
|
|
88
|
+
}
|
|
89
|
+
return pathname;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const proxyModule = loadProxyModule();
|
|
93
|
+
const formerDataStorePath = path.join(os.tmpdir(), `alexa-cookie-proxy-host-test-${Date.now()}.json`);
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const input = {
|
|
97
|
+
proxyOwnIp: '127.0.0.1',
|
|
98
|
+
proxyPort: 3456,
|
|
99
|
+
proxyListenBind: '0.0.0.0',
|
|
100
|
+
baseAmazonPage: 'amazon.de',
|
|
101
|
+
baseAmazonPageHandle: '_de',
|
|
102
|
+
amazonPageProxyLanguage: 'de_DE',
|
|
103
|
+
acceptLanguage: 'de-DE',
|
|
104
|
+
proxyLogLevel: 'silent',
|
|
105
|
+
formerDataStorePath
|
|
106
|
+
};
|
|
107
|
+
proxyModule.initAmazonProxy(input);
|
|
108
|
+
|
|
109
|
+
const directReq = {
|
|
110
|
+
method: 'GET',
|
|
111
|
+
url: '/www.amazon.com/ap/signin',
|
|
112
|
+
headers: { host: '127.0.0.1:3456' }
|
|
113
|
+
};
|
|
114
|
+
const refererReq = {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
url: '/ap/cvf/verify',
|
|
117
|
+
headers: {
|
|
118
|
+
host: '127.0.0.1:3456',
|
|
119
|
+
referer: 'http://127.0.0.1:3456/www.amazon.com/ap/signin'
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const directTarget = capturedProxyOptions.router(directReq);
|
|
124
|
+
const directRewrittenPath = applyPathRewrite(directReq.url, directReq);
|
|
125
|
+
const refererTarget = capturedProxyOptions.router(refererReq);
|
|
126
|
+
|
|
127
|
+
line('TEST: proxy host switch');
|
|
128
|
+
line('');
|
|
129
|
+
line('CODE UNDER TEST:');
|
|
130
|
+
line('- lib/proxy.js: router()');
|
|
131
|
+
line('- lib/proxy.js: rewriteProxyPath()');
|
|
132
|
+
line('');
|
|
133
|
+
line('INPUT:');
|
|
134
|
+
line(`baseAmazonPage: ${input.baseAmazonPage}`);
|
|
135
|
+
line(`direct request url: ${directReq.url}`);
|
|
136
|
+
line(`referer request url: ${refererReq.url}`);
|
|
137
|
+
line(`referer header: ${refererReq.headers.referer}`);
|
|
138
|
+
line('');
|
|
139
|
+
line('OBSERVED:');
|
|
140
|
+
line(`direct router target: ${directTarget}`);
|
|
141
|
+
line(`direct rewritten path: ${directRewrittenPath}`);
|
|
142
|
+
line(`referer router target: ${refererTarget}`);
|
|
143
|
+
line('');
|
|
144
|
+
line('ASSERTIONS:');
|
|
145
|
+
recordAssertion('direct router target === "https://www.amazon.com"', () => {
|
|
146
|
+
assert.strictEqual(directTarget, 'https://www.amazon.com');
|
|
147
|
+
});
|
|
148
|
+
recordAssertion('direct rewritten path === "/ap/signin"', () => {
|
|
149
|
+
assert.strictEqual(directRewrittenPath, '/ap/signin');
|
|
150
|
+
});
|
|
151
|
+
recordAssertion('referer router target === "https://www.amazon.com"', () => {
|
|
152
|
+
assert.strictEqual(refererTarget, 'https://www.amazon.com');
|
|
153
|
+
});
|
|
154
|
+
line('');
|
|
155
|
+
line('RESULT: PASS');
|
|
156
|
+
writeOutput();
|
|
157
|
+
} catch (err) {
|
|
158
|
+
if (!lines.includes('RESULT: PASS')) {
|
|
159
|
+
line('');
|
|
160
|
+
line('RESULT: FAIL');
|
|
161
|
+
writeOutput();
|
|
162
|
+
}
|
|
163
|
+
throw err;
|
|
164
|
+
} finally {
|
|
165
|
+
fs.rmSync(formerDataStorePath, { force: true });
|
|
166
|
+
}
|