Quellcode durchsuchen

Add banning URLs API and check for banned domains and hosts

poeti8 vor 7 Jahren
Ursprung
Commit
eb99fc5e4a
3 geänderte Dateien mit 117 neuen und 0 gelöschten Zeilen
  1. 63 0
      server/controllers/urlController.js
  2. 46 0
      server/db/url.js
  3. 8 0
      server/server.js

+ 63 - 0
server/controllers/urlController.js

@@ -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' });
+};

+ 46 - 0
server/db/url.js

@@ -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;
+};

+ 8 - 0
server/server.js

@@ -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':