Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't leak the real JIDs of participants when using mentions in semi-anonymous rooms. #1451

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
- Bugfix. Prevent duplicate messages by comparing MAM archive id to XEP-0359 stanza ids.
- Bugfix. Open groupchats not shown when logging in after disconnection.
- #1406: `TypeError: e.devicelists is undefined` when unchecking the "trusted device" checkbox
- Bugfix. Don't send real jids in mentions in a semi-anonymous or anonymous room when we are a moderator.
- Bugfix. Always set the "uri" field in reference tags, as required by the XEP.

## 4.1.1 (2019-02-18)

Expand Down
59 changes: 49 additions & 10 deletions spec/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -2686,6 +2686,9 @@
})));
});

// This room is not anonymous, so real JIDs can be used.
view.model.features.save({'nonanonymous': true, 'semianonymous': false});

// Run a few unit tests for the parseTextForReferences method
let [text, references] = view.model.parseTextForReferences('hello z3r0')
expect(references.length).toBe(0);
Expand All @@ -2694,15 +2697,17 @@
[text, references] = view.model.parseTextForReferences('hello @z3r0')
expect(references.length).toBe(1);
expect(text).toBe('hello z3r0');
expect(JSON.stringify(references))
.toBe('[{"begin":6,"end":10,"value":"z3r0","type":"mention","uri":"xmpp:z3r0@localhost"}]');
expect(references)
.toEqual([{"begin":6,"end":10,"value":"z3r0","type":"mention","uri":"xmpp:z3r0@localhost"}]);

[text, references] = view.model.parseTextForReferences('hello @some1 @z3r0 @gibson @mr.robot, how are you?')
expect(text).toBe('hello @some1 z3r0 gibson mr.robot, how are you?');
expect(JSON.stringify(references))
.toBe('[{"begin":13,"end":17,"value":"z3r0","type":"mention","uri":"xmpp:z3r0@localhost"},'+
'{"begin":18,"end":24,"value":"gibson","type":"mention","uri":"xmpp:gibson@localhost"},'+
'{"begin":25,"end":33,"value":"mr.robot","type":"mention","uri":"xmpp:mr.robot@localhost"}]');
expect(references)
.toEqual([
{"begin":13,"end":17,"value":"z3r0","type":"mention","uri":"xmpp:z3r0@localhost"},
{"begin":18,"end":24,"value":"gibson","type":"mention","uri":"xmpp:gibson@localhost"},
{"begin":25,"end":33,"value":"mr.robot","type":"mention","uri":"xmpp:mr.robot@localhost"}
]);

[text, references] = view.model.parseTextForReferences('yo @gib')
expect(text).toBe('yo @gib');
Expand All @@ -2715,14 +2720,14 @@
[text, references] = view.model.parseTextForReferences('@gibson')
expect(text).toBe('gibson');
expect(references.length).toBe(1);
expect(JSON.stringify(references))
.toBe('[{"begin":0,"end":6,"value":"gibson","type":"mention","uri":"xmpp:gibson@localhost"}]');
expect(references)
.toEqual([{"begin":0,"end":6,"value":"gibson","type":"mention","uri":"xmpp:gibson@localhost"}]);

[text, references] = view.model.parseTextForReferences('hi @Link Mauve how are you?')
expect(text).toBe('hi Link Mauve how are you?');
expect(references.length).toBe(1);
expect(JSON.stringify(references))
.toBe('[{"begin":3,"end":13,"value":"Link Mauve","type":"mention","uri":"xmpp:Link-Mauve@localhost"}]');
expect(references)
.toEqual([{"begin":3,"end":13,"value":"Link Mauve","type":"mention","uri":"xmpp:Link-Mauve@localhost"}]);
done();
}));

Expand All @@ -2747,6 +2752,9 @@
})));
});

// This room is not anonymous, so real JIDs can be used.
view.model.features.save({'nonanonymous': true, 'semianonymous': false});

const textarea = view.el.querySelector('textarea.chat-textarea');
textarea.value = 'hello @z3r0 @gibson @mr.robot, how are you?'
const enter_event = {
Expand Down Expand Up @@ -2800,6 +2808,34 @@
done();
}));

it("will not leak the real JIDs of participants in XEP-0372 references",
mock.initConverse(
null, ['rosterGroupsFetched'], {},
async function (done, _converse) {

await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
const view = _converse.chatboxviews.get('lounge@localhost');
view.model.features.save({'nonanonymous': false, 'semianonymous': true});
const textarea = view.el.querySelector('.chat-textarea');
textarea.value = "Meow @dummy!";
spyOn(_converse.connection, 'send');
const enter_event = {
'target': textarea,
'preventDefault': _.noop,
'stopPropagation': _.noop,
'keyCode': 13 // Enter
};
view.keyPressed(enter_event);
await new Promise((resolve, reject) => view.once('messageInserted', resolve));
const msg = _converse.connection.send.calls.all()[0].args[0];
const selector = `message[to="lounge@localhost"] reference[xmlns="urn:xmpp:reference:0"]`;
const node = msg.nodeTree.querySelector(selector);
const refUri = node.getAttribute('uri');
expect(refUri).toBe('xmpp:lounge@localhost/dummy');

done();
}));

it("includes XEP-0372 references to that person",
mock.initConverse(
null, ['rosterGroupsFetched'], {},
Expand All @@ -2822,6 +2858,9 @@
})));
});

// This room is not anonymous, so real JIDs can be used.
view.model.features.save({'nonanonymous': true, 'semianonymous': false});

spyOn(_converse.connection, 'send');
const textarea = view.el.querySelector('textarea.chat-textarea');
textarea.value = 'hello @z3r0 @gibson @mr.robot, how are you?'
Expand Down
15 changes: 11 additions & 4 deletions src/headless/converse-muc.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,15 +356,22 @@ converse.plugins.add('converse-muc', {
if (!occupant) {
return null;
}
const roomJid = this.get('jid');
const nonanon = this.features.get('nonanonymous');
let uri;
if (occupant.get('jid') && nonanon) {
uri = `xmpp:${occupant.get('jid')}`;
}
else {
uri = `xmpp:${roomJid}/${occupant.get('nick')}`;
}
const obj = {
'begin': index,
'end': index + longest_match.length,
'value': longest_match,
'type': 'mention'
'type': 'mention',
'uri': uri,
};
if (occupant.get('jid')) {
obj.uri = `xmpp:${occupant.get('jid')}`
}
return obj;
},

Expand Down