Skip to content

Commit a6debac

Browse files
committed
Add abuse detection for demos using sockets
1 parent 1f1133c commit a6debac

5 files changed

Lines changed: 149 additions & 22 deletions

File tree

sockets.js

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,54 @@ const Generators = app.get('Generators');
99
const Snippet = require('./models/Snippet');
1010
const Version = require('./models/Version');
1111

12+
const abuseLimit = 20;
13+
let abuseCache = {};
14+
let timeout = {};
15+
16+
function checkAbuse(id) {
17+
// abuse is considered <abuseLimit> requests per second from same socket
18+
// Block further requests until until 5 seconds have passed without another barrage
19+
20+
if (!(id in abuseCache)) {
21+
abuseCache[id] = {
22+
firstAccess: new Date().getTime(),
23+
lastAccess: new Date().getTime(),
24+
total: 1
25+
};
26+
27+
// Update current stats
28+
} else {
29+
abuseCache[id].lastAccess = new Date().getTime();
30+
abuseCache[id].total++;
31+
}
32+
33+
// Check for abuse
34+
if (abuseCache[id] && abuseCache[id].total > abuseLimit && abuseCache[id].lastAccess - abuseCache[id].firstAccess < 1000) {
35+
timeout[id] = new Date().getTime(); // time they were placed into time out
36+
return true;
37+
}
38+
39+
if (id in timeout) {
40+
return true;
41+
}
42+
return false;
43+
}
44+
45+
function checkTimeout() {
46+
_.each(timeout, (a, b) => {
47+
if (new Date().getTime() - a > 1000) {
48+
delete timeout[b];
49+
}
50+
});
51+
}
52+
53+
// Reset abuse cache every second
54+
setInterval(() => {
55+
abuseCache = {};
56+
}, 1000);
57+
58+
setInterval(checkTimeout, 2500);
59+
1260
// Attach the user's session to the socket object
1361
// If user isn't logged in, set session to null for front page demo
1462
io.use((socket, next) => {
@@ -27,8 +75,11 @@ io.use((socket, next) => {
2775
});
2876

2977
io.on('connection', socket => {
78+
3079
// Search
3180
socket.on('search', msg => {
81+
if (checkAbuse(socket.id)) return socket.emit('abuse');
82+
3283
let tags = _.uniq(msg.query.slice(0, 255).split(',').map(tag => tag.trim()).filter(tag => tag !== "")).filter(tag => tag.match(/^([a-zA-Z0-9 _\-\.+\[\]\{\}\(\)]{1,32})$/g) !== null);
3384
Snippet.search(tags).then(data => {
3485
let obj = data.reduce(function(o, v, i) {
@@ -41,11 +92,27 @@ io.on('connection', socket => {
4192

4293
// Snippet info
4394
socket.on('snippet', msg => {
44-
Version.getCond({snippetID: msg.id}).then(snippet => socket.emit('snippetResults', snippet));
95+
if (checkAbuse(socket.id)) return socket.emit('abuse');
96+
97+
Version.getCond({snippetID: msg.id}).then(snippet => {
98+
Snippet.getCond({['s.id']: snippet.snippetID}).then(orig => {
99+
if (snippet === null) {
100+
socket.emit('snippetResults', null);
101+
} else if (snippet.published === 0) {
102+
Version.getVersion(orig.ref, snippet.version-1).then(ver => {
103+
socket.emit('snippetResults', ver)
104+
});
105+
} else {
106+
socket.emit('snippetResults', snippet)
107+
}
108+
});
109+
});
45110
});
46111

47112
// Generators
48113
socket.on('lintCode', msg => {
114+
if (checkAbuse(socket.id)) return socket.emit('abuse');
115+
49116
msg.code = String(msg.code).slice(0, 8192);
50117
let shortest = Math.floor(Math.random() * Generators.realtime.length);
51118
for (let i = 0; i < Generators.realtime.length; i++) {
@@ -68,6 +135,8 @@ io.on('connection', socket => {
68135
});
69136

70137
socket.on('lintDemoCode', msg => {
138+
if (checkAbuse(socket.id)) return socket.emit('abuse');
139+
71140
msg.code = String(msg.code).slice(0, 8192);
72141
let shortest = Math.floor(Math.random() * Generators.demo.length);
73142
for (let i = 0; i < Generators.demo.length; i++) {
@@ -90,6 +159,8 @@ io.on('connection', socket => {
90159
});
91160

92161
socket.on('lintSnippetCode', msg => {
162+
if (checkAbuse(socket.id)) return socket.emit('abuse');
163+
93164
msg.code = String(msg.code).slice(0, 8192);
94165
let shortest = Math.floor(Math.random() * Generators.realtime.length);
95166
for (let i = 0; i < Generators.realtime.length; i++) {

src/js/demoEditor.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
socket = io($('server').html());
22

3-
let editor = ace.edit("aceEditor");
3+
let typingTimer, lastAbuse;
4+
let editor = ace.edit("aceEditor");
5+
let codeArea = $(editor.textInput.getElement());
6+
47
editor.setTheme("ace/theme/twilight");
58
editor.session.setMode("ace/mode/javascript");
69
editor.setValue(editor.getValue(), 1);
710
editor.focus();
8-
911
lintCode();
1012

11-
let typingTimer;
12-
let codeArea = $(editor.textInput.getElement());
1313
codeArea.keyup(() => {
1414
clearTimeout(typingTimer);
1515
typingTimer = setTimeout(lintCode, 250);
@@ -27,6 +27,23 @@ socket.on('codeLinted', msg => {
2727
}
2828
});
2929

30+
socket.on('abuse', msg => {
31+
if (new Date().getTime() - lastAbuse < 1000) return;
32+
lastAbuse = new Date().getTime();
33+
noty({
34+
text: "An error has occurred, please try again later.",
35+
layout: 'top',
36+
type: 'error',
37+
theme: 'relax',
38+
timeout: 2500,
39+
closeWith: ['click'],
40+
animation: {
41+
open: 'animated flipInX',
42+
close: 'animated flipOutX'
43+
}
44+
});
45+
});
46+
3047
function lintCode() {
3148
socket.emit('lintDemoCode', {code: String(editor.getValue()).slice(0, 8192), ref: null});
3249
};

src/js/realtimeEditor.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
socket = io($('server').html());
22
ref = $('ref').html();
33

4+
let typingTimer, lastAbuse;
45
let editor = ace.edit("aceEditor");
6+
let codeArea = $(editor.textInput.getElement());
7+
58
editor.setTheme("ace/theme/twilight");
69
editor.session.setMode("ace/mode/javascript");
710
editor.setValue(editor.getValue(), 1);
811
editor.focus();
12+
updateCharCount();
13+
lintCode();
914

1015
$("#submit").click(() => {
1116
$.post('', {rename: $("#limitsInput").val(), code: editor.getValue()}, url => {
1217
window.location.replace(url);
1318
});
1419
});
1520

16-
updateCharCount();
17-
lintCode();
18-
19-
let typingTimer;
20-
let codeArea = $(editor.textInput.getElement());
2121
codeArea.keyup(() => {
2222
clearTimeout(typingTimer);
2323
typingTimer = setTimeout(lintCode, 250);
@@ -37,6 +37,23 @@ socket.on('codeLinted', msg => {
3737
}
3838
});
3939

40+
socket.on('abuse', msg => {
41+
if (new Date().getTime() - lastAbuse < 1000) return;
42+
lastAbuse = new Date().getTime();
43+
noty({
44+
text: "An error has occurred, please try again later.",
45+
layout: 'top',
46+
type: 'error',
47+
theme: 'relax',
48+
timeout: 2500,
49+
closeWith: ['click'],
50+
animation: {
51+
open: 'animated flipInX',
52+
close: 'animated flipOutX'
53+
}
54+
});
55+
});
56+
4057
function lintCode() {
4158
socket.emit('lintCode', {code: String(editor.getValue()).slice(0, 8192), ref});
4259
};

src/js/realtimeSnippetEditor.js

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@ let socket = io($('server').html());
22
let ref = $('ref').html();
33
let readonly = $('readonly').html();
44

5+
let typingTimer, lastAbuse;
56
let editor = ace.edit("aceEditor");
7+
let codeArea = $(editor.textInput.getElement());
8+
69
editor.setTheme("ace/theme/twilight");
710
editor.session.setMode("ace/mode/javascript");
811
editor.setValue(editor.getValue(), 1);
912
editor.focus();
13+
updateCharCount();
14+
lintCode();
15+
1016
if (readonly == "true") {
1117
editor.setReadOnly(true);
1218
}
@@ -17,12 +23,6 @@ $("#submit").click(() => {
1723
});
1824
});
1925

20-
updateCharCount();
21-
lintCode();
22-
23-
let typingTimer;
24-
let codeArea = $(editor.textInput.getElement());
25-
2626
codeArea.keyup(() => {
2727
clearTimeout(typingTimer);
2828
typingTimer = setTimeout(lintCode, 250);
@@ -42,6 +42,23 @@ socket.on('codeLinted', msg => {
4242
}
4343
});
4444

45+
socket.on('abuse', msg => {
46+
if (new Date().getTime() - lastAbuse < 1000) return;
47+
lastAbuse = new Date().getTime();
48+
noty({
49+
text: "An error has occurred, please try again later.",
50+
layout: 'top',
51+
type: 'error',
52+
theme: 'relax',
53+
timeout: 2500,
54+
closeWith: ['click'],
55+
animation: {
56+
open: 'animated flipInX',
57+
close: 'animated flipOutX'
58+
}
59+
});
60+
});
61+
4562
function lintCode() {
4663
socket.emit('lintSnippetCode', {code: String(editor.getValue()).slice(0, 8192), ref});
4764
};

src/js/search.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,14 @@ $(() => {
4646
socket.on('snippetResults', msg => {
4747
$('#snippetInfo').empty();
4848

49-
let revisions = "<select name='revision'>";
50-
for (let i = msg.version; i >= 1; i--) {
49+
let revisions = "<select name='revision'>",
50+
sub = 0;
51+
52+
if (msg.published === 0) {
53+
sub = 1;
54+
}
55+
56+
for (let i = msg.version-sub; i >= 1; i--) {
5157
revisions += `<option value='${i}'>${i}</option>`;
5258
}
5359
revisions += "</select>";
@@ -56,20 +62,20 @@ $(() => {
5662
Choose a revision:
5763
${revisions}
5864
<p>Owner: ${snippets[msg.snippetID].user}<br>
59-
Total revisions: ${msg.version}<br>
65+
Total revisions: ${msg.version-sub}<br>
6066
Tags: ${snippets[msg.snippetID].tags.join(", ")}
6167
</p>
6268
<div id="revisionInfo">
6369
<p>
6470
Created: <span id="created" class="date" data-date="${snippets[msg.snippetID].created}"></span><br>
6571
Modified: <span id="modified" class="date" data-date="${snippets[msg.snippetID].modified}"></span><br>
6672
</p>
73+
Usage
74+
<pre><code id='usage' class="javascript">const mySnippet = require('${snippets[msg.snippetID].user}/${snippets[msg.snippetID].name}/${msg.version}');</code></pre>
6775
Snippet Description<br>
6876
<textarea id='snippetDescription' rows='5' readonly>${snippets[msg.snippetID].description}</textarea>
6977
Revision Description<br>
7078
<textarea id='revisionDescription' rows='5' readonly></textarea>
71-
Usage
72-
<pre><code id='usage' class="javascript">const mySnippet = require('${snippets[msg.snippetID].user}/${snippets[msg.snippetID].name}/${msg.version}');</code></pre>
7379
</div>
7480
`);
7581

@@ -86,7 +92,6 @@ $(() => {
8692
function updateRevision() {
8793
let self = $("select[name='revision']");
8894
$.when($.ajax(`ajax/snippetLookup/${snippets[selected].ref}/${self.val()}`)).then(function(data) {
89-
console.log(data);
9095
$('#created').data('date', data.created);
9196
$('#modified').data('date', data.modified);
9297
$('#revisionDescription').val(data.description);

0 commit comments

Comments
 (0)