Add banning URLs API and check for banned domains and hosts

This commit is contained in:
poeti8 2018-11-27 23:14:26 +03:30
parent 82a561bd4c
commit eb99fc5e4a
3 changed files with 117 additions and 0 deletions

View File

@ -1,5 +1,7 @@
const urlRegex = require('url-regex');
const URL = require('url');
const dns = require('dns');
const { promisify } = require('util');
const generate = require('nanoid/generate');
const useragent = require('useragent');
const geoip = require('geoip-lite');
@ -17,8 +19,13 @@ const {
getUrls,
setCustomDomain,
urlCountFromDate,
banUrl,
getBannedDomain,
getBannedHost,
} = require('../db/url');
const dnsLookup = promisify(dns.lookup);
const { addProtocol, generateShortUrl } = require('../utils');
const config = require('../config');
@ -72,6 +79,22 @@ exports.urlShortener = async ({ body, user }, res) => {
}
}
// If domain or host is banned
const domain = URL.parse(body.target).hostname;
const isDomainBanned = await getBannedDomain(domain);
let isHostBanned;
try {
const dnsRes = await dnsLookup(domain);
isHostBanned = await getBannedHost(dnsRes && dnsRes.address);
} catch (error) {
isHostBanned = null;
}
if (isDomainBanned || isHostBanned) {
return res.status(400).json({ error: 'URL is containing malware/scam.' });
}
// Create new URL
const id = (user && body.customurl) || (await generateId());
const target = addProtocol(body.target);
@ -104,12 +127,20 @@ exports.goToUrl = async (req, res, next) => {
botList.some(bot => agent.source.toLowerCase().includes(bot)) || agent.family === 'Other';
if (!urls && !urls.length) return next();
const url = urls.find(item => (domain ? item.domain === domain : !item.domain));
if (!url) return next();
if (url.banned) {
return res.redirect('/banned');
}
const doesRequestInfo = /.*\+$/gi.test(reqestedId);
if (doesRequestInfo && !url.password) {
req.urlTarget = url.target;
req.pageType = 'info';
return next();
}
if (url.password && !req.body.password) {
req.protectedUrl = id;
req.pageType = 'password';
@ -194,3 +225,35 @@ exports.getStats = async ({ query: { id, domain }, user }, res) => {
if (!stats) return res.status(400).json({ error: 'Could not get the short URL stats.' });
return res.status(200).json(stats);
};
exports.ban = async ({ body }, res) => {
if (!body.id) return res.status(400).json({ error: 'No id has been provided.' });
const urls = await findUrl({ id: body.id });
const [url] = urls.filter(item => !item.domain);
if (!url) return res.status(400).json({ error: "Couldn't find the URL." });
if (url.banned) return res.status(200).json({ message: 'URL was banned already' });
const domain = URL.parse(url.target).hostname;
let host;
if (body.host) {
try {
const dnsRes = await dnsLookup(domain);
host = dnsRes && dnsRes.address;
} catch (error) {
host = null;
}
}
await banUrl({
domain: body.domain && domain,
host,
id: body.id,
user: body.user,
});
return res.status(200).json({ message: 'URL has been banned successfully' });
};

View File

@ -434,3 +434,49 @@ exports.urlCountFromDate = ({ date, email }) =>
})
.catch(err => reject(err));
});
exports.banUrl = async ({ id, domain, host, user }) => {
const session = driver.session();
const userQuery = user
? 'OPTIONAL MATCH (u:USER)-[:CREATED]->(l) SET u.banned = true WITH u ' +
'OPTIONAL MATCH (u)-[:CREATED]->(ls:URL) SET ls.banned = true'
: '';
const domainQuery = domain
? 'MERGE (d:DOMAIN { name: $domain }) ON CREATE SET d.banned = true'
: '';
const hostQuery = host ? 'MERGE (h:HOST { name: $host }) ON CREATE SET h.banned = true' : '';
await session.writeTransaction(tx =>
tx.run(
'MATCH (l:URL { id: $id }) WHERE NOT (l)-[:USES]->(:DOMAIN) ' +
`SET l.banned = true WITH l ${userQuery} ${domainQuery} ${hostQuery}`,
{
id,
domain,
host,
}
)
);
session.close();
};
exports.getBannedDomain = async (domain = '') => {
const session = driver.session();
const { records } = await session.readTransaction(tx =>
tx.run('MATCH (d:DOMAIN { name: $domain, banned: true }) RETURN d', {
domain,
})
);
session.close();
return records.length > 0;
};
exports.getBannedHost = async (host = '') => {
const session = driver.session();
const { records } = await session.readTransaction(tx =>
tx.run('MATCH (h:HOST { name: $host, banned: true }) RETURN h', {
host,
})
);
session.close();
return records.length > 0;
};

View File

@ -78,6 +78,7 @@ app.prepare().then(() => {
server.get('/stats', (req, res) => app.render(req, res, '/stats', req.query));
server.get('/terms', (req, res) => app.render(req, res, '/terms'));
server.get('/report', (req, res) => app.render(req, res, '/report'));
server.get('/banned', (req, res) => app.render(req, res, '/banned'));
server.get('/reset-password/:resetPasswordToken?', catchErrors(auth.resetPassword), (req, res) =>
app.render(req, res, '/reset-password', req.user)
);
@ -111,6 +112,13 @@ app.prepare().then(() => {
server.delete('/api/url/customdomain', auth.authJwt, catchErrors(url.deleteCustomDomain));
server.get('/api/url/stats', auth.authApikey, auth.authJwt, catchErrors(url.getStats));
server.post('/api/url/requesturl', catchErrors(url.goToUrl));
server.post(
'/api/url/admin/ban',
auth.authApikey,
auth.authJwt,
auth.authAdmin,
catchErrors(url.ban)
);
server.get('/:id', catchErrors(url.goToUrl), (req, res) => {
switch (req.pageType) {
case 'password':