Skip to content

Commit db04bcc

Browse files
committed
Add a user-data class for generating intercomSettings
1 parent 34b1ac4 commit db04bcc

3 files changed

Lines changed: 204 additions & 0 deletions

File tree

lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export Client from './client';
22
export User from './user';
33
export Snippet from './snippet';
4+
export UserData from './user-data';
45

56
import crypto from 'crypto';
67

lib/user-data.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { IdentityVerification } from './index';
2+
import htmlencode from 'htmlencode';
3+
4+
export default class UserData {
5+
constructor(settings) {
6+
this.loggedOut = !settings.user_id && !settings.email;
7+
8+
if (!settings.app_id) {
9+
throw new Error('You must provide an app_id in your Intercom settings');
10+
}
11+
if (!this.loggedOut && !settings.verificationSecret) {
12+
throw new Error('You must provide your verification secret in your Intercom settings');
13+
}
14+
15+
this.settings = settings;
16+
}
17+
json() {
18+
const verificationSecret = this.getVerificationSecret();
19+
const identifier = this.getIdentifier();
20+
this.setUserHash(verificationSecret, identifier);
21+
return this.escapedSettings(this.settings);
22+
}
23+
getVerificationSecret() {
24+
const { verificationSecret } = this.settings;
25+
delete this.settings.verificationSecret;
26+
return verificationSecret;
27+
}
28+
getIdentifier() {
29+
if (this.settings.user_id) {
30+
return this.settings.user_id.toString();
31+
} else {
32+
return this.settings.email;
33+
}
34+
}
35+
setUserHash(verificationSecret, identifier) {
36+
if (this.loggedOut) {
37+
return;
38+
}
39+
40+
const userHash = IdentityVerification.userHash({
41+
secretKey: verificationSecret,
42+
identifier
43+
});
44+
this.settings.user_hash = userHash;
45+
}
46+
escapedSettings(settings) {
47+
const intercomSettings = {};
48+
Object.keys(settings).map(key => {
49+
if (typeof settings[key] === 'object' && settings[key] !== null) {
50+
intercomSettings[key] = this.escapedSettings(settings[key]);
51+
} else {
52+
const escapedKey = this.escapeString(key);
53+
const value = this.escapeString(settings[key]);
54+
intercomSettings[escapedKey] = value;
55+
}
56+
});
57+
return intercomSettings;
58+
}
59+
escapeString(string) {
60+
if (typeof string === 'string') {
61+
string = htmlencode.htmlEncode(string).replace(/\"/gi, '\\"');
62+
}
63+
return string;
64+
}
65+
}

test/user-data.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import UserData from '../lib/user-data';
2+
import assert from 'assert';
3+
4+
describe('userData', () => {
5+
it('should be able to grab the verification secret', () => {
6+
const settings = {
7+
verificationSecret: 'abc123',
8+
app_id: 'xyz789',
9+
user_id: 1
10+
};
11+
const userData = new UserData(settings);
12+
assert.equal(userData.getVerificationSecret(), 'abc123');
13+
});
14+
15+
it('should grab the user_id as the identifier', () => {
16+
const settings = {
17+
verificationSecret: 'abc123',
18+
app_id: 'xyz789',
19+
user_id: 1,
20+
email: 'jess@intercom.io'
21+
};
22+
const userData = new UserData(settings);
23+
assert.equal(userData.getIdentifier(), 1);
24+
});
25+
26+
it('should grab the email as the identifier if no user_id', () => {
27+
const settings = {
28+
verificationSecret: 'abc123',
29+
app_id: 'xyz789',
30+
email: 'jess@intercom.io'
31+
};
32+
const userData = new UserData(settings);
33+
assert.equal(userData.getIdentifier(), 'jess@intercom.io');
34+
});
35+
36+
it('should throw an error if there\'s no verification secret', () => {
37+
const settings = {
38+
app_id: 'xyz789',
39+
user_id: 1
40+
};
41+
assert.throws(() => new UserData(settings), Error);
42+
});
43+
44+
it('should error if there\'s no app_id', () => {
45+
const settings = {
46+
verificationSecret: 'abc123',
47+
user_id: 1
48+
};
49+
assert.throws(() => new UserData(settings), Error);
50+
});
51+
52+
it('should return the logged out userData if no identifier', () => {
53+
const settings = {
54+
app_id: 'xyz789'
55+
};
56+
const userData = new UserData(settings);
57+
assert.equal(JSON.stringify(userData.json()), JSON.stringify({ app_id: 'xyz789'}));
58+
});
59+
60+
it('should escape bad stuff in values', () => {
61+
const settings = {
62+
verificationSecret: 'abc123',
63+
app_id: 'xyz789',
64+
email: 'jess@"<script>alert(1)</script>intercom.io'
65+
};
66+
const userData = new UserData(settings);
67+
assert.equal(userData.json().email, 'jess@\\"&lt;script&gt;alert(1)&lt;/script&gt;intercom.io');
68+
});
69+
70+
it('should escape bad stuff in object keys', () => {
71+
const settings = {
72+
verificationSecret: 'abc123',
73+
app_id: 'xyz789',
74+
user_id: '1'
75+
};
76+
settings['<script>doSomethingEvil();</script>'] = 'jess@"<script>alert(1)</script>intercom.io';
77+
const userData = new UserData(settings);
78+
const result = userData.json();
79+
assert.equal(result['&lt;script&gt;doSomethingEvil();&lt;/script&gt;'], 'jess@\\\"&lt;script&gt;alert(1)&lt;/script&gt;intercom.io');
80+
});
81+
82+
it('should escape bad stuff in next object keys and values', () => {
83+
const settings = {
84+
verificationSecret: 'abc123',
85+
app_id: 'xyz789',
86+
user_id: '1'
87+
};
88+
settings.maliciousSettings = {
89+
'<script>doSomethingEvil();</script>': 'jess@"<script>alert(1)</script>intercom.io'
90+
};
91+
const userData = new UserData(settings);
92+
const result = userData.json();
93+
assert.equal(result.maliciousSettings['&lt;script&gt;doSomethingEvil();&lt;/script&gt;'], 'jess@\\"&lt;script&gt;alert(1)&lt;/script&gt;intercom.io');
94+
});
95+
96+
it('should not include the verification secret in the userData', () => {
97+
const settings = {
98+
verificationSecret: 'abc123',
99+
app_id: 'xyz789',
100+
user_id: 1,
101+
email: 'jess@intercom.io',
102+
name: 'Jess OB',
103+
company: {
104+
id: 123,
105+
name: 'Intercom'
106+
}
107+
};
108+
const userData = new UserData(settings);
109+
const result = userData.json();
110+
assert.equal(Object.keys(result).indexOf(settings.verificationSecret), -1);
111+
});
112+
113+
it('should return the userData', () => {
114+
const settings = {
115+
verificationSecret: 'abc123',
116+
app_id: 'xyz789',
117+
user_id: 1,
118+
email: 'jess@intercom.io',
119+
name: 'Jess OB',
120+
company: {
121+
id: 123,
122+
name: 'Intercom'
123+
}
124+
};
125+
const userData = new UserData(settings);
126+
assert.equal(JSON.stringify(userData.json()), JSON.stringify({
127+
app_id: 'xyz789',
128+
user_id: 1,
129+
email: 'jess@intercom.io',
130+
name: 'Jess OB',
131+
company: {
132+
id: 123,
133+
name: 'Intercom'
134+
},
135+
user_hash: 'f02877f24c9dd37542268a28627ebaf2e07d0d114d9482abcdc20f60874b40b3'
136+
}));
137+
});
138+
});

0 commit comments

Comments
 (0)