-
-
Save tyage/cac08c8e17b90b840fb22cb434cff127 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const datastore = require('@google-cloud/datastore')(); | |
const htmlparser = require('htmlparser2'); | |
const uuidV4 = require('uuid/v4'); | |
const http = require('http'); | |
const KIND = 'Comment'; | |
const allowedTags = ['p', 'a', 'b', 'img', 'br', 'i']; | |
const allowedTypes = ['tag', 'text'] | |
function insert_comment(content) { | |
var id = uuidV4(); | |
var key = datastore.key([KIND, id]); | |
return datastore.save({ | |
key: key, | |
data: { content: content, viewed: false }, | |
}).then(() => { | |
return id; | |
}); | |
} | |
function get_comment(id) { | |
var key = datastore.key([KIND, id]); | |
return datastore.get(key).then((result) => { | |
if (!result[0]) | |
throw new Exception(); | |
return result[0]; | |
}); | |
} | |
function delete_comment(id) { | |
var key = datastore.key([KIND, id]); | |
return datastore.delete(key); | |
} | |
function set_comment_viewed(id) { | |
var key = datastore.key([KIND, id]); | |
return datastore.get(key).then((result) => { | |
if (!result[0]) | |
return; | |
var comment = result[0]; | |
comment.viewed = true; | |
return datastore.save({ | |
key: key, | |
data: comment | |
}); | |
}); | |
} | |
function check_comment(comment) { | |
function validate(node) | |
{ | |
if (allowedTypes.indexOf(node.type) == -1) | |
throw 'Invalid type'; | |
else if (node.type == 'tag' && allowedTags.indexOf(node.name) === -1) | |
throw 'Invalid tag'; | |
for (var name in node.attribs) { | |
var value = node.attribs[name]; | |
if (/^on.+/.test(name) && value !== '') | |
throw 'Invalid event'; | |
console.log(value) | |
if (name == 'href' && /^(https?:)/.test(value) === false) | |
throw 'Invalid link'; | |
} | |
for (var i in node.children) { | |
validate(node.children[i]); | |
} | |
} | |
var handler = new htmlparser.DomHandler(function (error, dom) { | |
if (error) { | |
throw error; | |
} else { | |
for (var i in dom) | |
validate(dom[i]); | |
} | |
}); | |
try { | |
var parser = new htmlparser.Parser(handler); | |
parser.write(comment); | |
parser.end(); | |
} catch (err) { | |
console.log(err); | |
return false; | |
} | |
return true; | |
} | |
module.exports = { | |
check_comment: check_comment, | |
insert_comment: insert_comment, | |
get_comment: get_comment, | |
delete_comment: delete_comment, | |
set_comment_viewed: set_comment_viewed | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Lets require/import the HTTP module | |
const pubsub = require('@google-cloud/pubsub')(); | |
const http = require('http'); | |
const dispatcher = require('httpdispatcher'); | |
const fs = require('fs'); | |
const recaptcha = require('express-recaptcha'); | |
const blog = require('./blog'); | |
//Lets define a port we want to listen to | |
const PORT = process.env.PORT || 8080; | |
//Lets use our dispatcher | |
function handleRequest(request, response){ | |
try { | |
//Disptach | |
dispatcher.dispatch(request, response); | |
} catch(err) { | |
console.log(err); | |
throw err; | |
} | |
} | |
recaptcha.init('unused', process.env.RECAPTCHA_PRIVKEY); | |
process.env.RECAPTCHA_PRIVKEY= 'deleted' | |
//For all your static (js/css/images/etc.) set the directory name (relative path). | |
dispatcher.setStatic('static'); | |
dispatcher.setStaticDirname('.'); | |
//A sample GET request | |
dispatcher.onGet("/", function(req, res) { | |
res.writeHead(200, {'Content-Type': 'text/html'}); | |
fs.readFile('static/pages/index.html', function(err, data) { | |
if (err) throw err; | |
res.end(data); | |
}); | |
}); | |
dispatcher.beforeFilter("/reply", function(req, res, chain) { | |
req.body = req.params; | |
recaptcha.verify(req, function(error) { | |
if(!error) { | |
chain.next(req, res, chain); | |
} else { | |
res.writeHead(403); | |
res.end('Wrong captcha.'); | |
} | |
}); | |
}); | |
//A sample POST request | |
dispatcher.onPost("/reply", function(req, res) { | |
if (req.params.comment && blog.check_comment(req.params.comment)) { | |
console.log(req.params.comment); | |
blog.insert_comment(req.params.comment).then((id) => { | |
res.writeHead(200, {'Content-Type': 'text/html', 'X-XSS-Protection': '0'}); | |
fs.readFile('static/pages/valid.html', 'utf-8', function(err, data) { | |
if (err) throw err; | |
res.end(data.replace('$comment$', id)); // Templates not invented yet... | |
var topic = pubsub.topic('xss'); | |
var comment_url = `https://${process.env.GAE_SERVICE}-dot-${process.env.GCLOUD_PROJECT}.appspot.com/comment?id=${id}`; | |
topic.publish({service: 'geokitties', | |
url: comment_url}); | |
}); | |
}); | |
} else { | |
res.writeHead(403, {'Content-Type': 'text/html'}); | |
fs.readFile('static/pages/invalid.html', function(err, data) { | |
if (err) throw err; | |
res.end(data); | |
}); | |
} | |
}); | |
dispatcher.onGet("/comment", function(req, res) { | |
var post_id = req.params.id || ''; | |
if (!post_id) { | |
res.writeHead(404); | |
res.end(); | |
} else { | |
blog.get_comment(post_id).then((result) => { | |
res.writeHead(200, {'Content-Type': 'text/html'}); | |
if (result.viewed) | |
res.end('Comment already validated.'); | |
else | |
res.end(result.content); | |
if (req.headers['user-agent'] && req.headers['user-agent'].indexOf('Headless') !== -1) | |
blog.set_comment_viewed(post_id); | |
}, () => { | |
res.writeHead(404); | |
res.end(); | |
}).catch((err) => { | |
console.log(err); | |
res.writeHead(500); | |
res.end(); | |
}); | |
} | |
}); | |
dispatcher.beforeFilter(/\//, function(req, res, chain) { //any url | |
var filter = /dog|\.\.\/|passwd|select|union|insert|update|script|from|where|"|'/ig | |
if (filter.test(req.url)) | |
{ | |
req.url = req.url.replace(filter, ''); | |
res.setHeader('WAF-Debug', new Buffer(req.url).toString('base64')); | |
} else { | |
res.setHeader('WAF-Debug', 'OK'); | |
} | |
chain.next(req, res, chain); | |
}); | |
//Create a server | |
var server = http.createServer(handleRequest); | |
//Lets start our server | |
server.listen(PORT, function() { | |
//Callback triggered when server is successfully listening. Hurray! | |
console.log("Server listening..."); | |
}); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "frontend", | |
"version": "0.0.1", | |
"private": true, | |
"scripts": { | |
"start": "node main.js" | |
}, | |
"engines" : { | |
"node" : ">=7.9.0" | |
}, | |
"dependencies" : { | |
"httpdispatcher": "0.4.0", | |
"htmlparser2": "3.9.2", | |
"uuid": "latest", | |
"express-recaptcha": "~2.1.0", | |
"@google-cloud/datastore": "~1.0.0", | |
"@google-cloud/pubsub": "~0.11.0" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment