validateBodyController.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. const axios = require('axios');
  2. const urlRegex = require('url-regex');
  3. const validator = require('express-validator/check');
  4. const { subHours } = require('date-fns/');
  5. const { validationResult } = require('express-validator/check');
  6. const { addCooldown, banUser, getCooldowns } = require('../db/user');
  7. const config = require('../config');
  8. exports.validationCriterias = [
  9. validator
  10. .body('email')
  11. .exists()
  12. .withMessage('Email must be provided.')
  13. .isEmail()
  14. .withMessage('Email is not valid.')
  15. .trim()
  16. .normalizeEmail(),
  17. validator
  18. .body('password', 'Password must be at least 8 chars long.')
  19. .exists()
  20. .withMessage('Password must be provided.')
  21. .isLength({ min: 8 }),
  22. ];
  23. exports.validateBody = (req, res, next) => {
  24. const errors = validationResult(req);
  25. if (!errors.isEmpty()) {
  26. const errorsObj = errors.mapped();
  27. const emailError = errorsObj.email && errorsObj.email.msg;
  28. const passwordError = errorsObj.password && errorsObj.password.msg;
  29. return res.status(400).json({ error: emailError || passwordError });
  30. }
  31. return next();
  32. };
  33. const preservedUrls = [
  34. 'login',
  35. 'logout',
  36. 'signup',
  37. 'reset-password',
  38. 'resetpassword',
  39. 'url-password',
  40. 'url-info',
  41. 'settings',
  42. 'stats',
  43. 'verify',
  44. 'api',
  45. '404',
  46. 'static',
  47. 'images',
  48. 'banned',
  49. 'terms',
  50. 'privacy',
  51. 'report',
  52. ];
  53. exports.preservedUrls = preservedUrls;
  54. exports.validateUrl = async ({ body, user }, res, next) => {
  55. // Validate URL existence
  56. if (!body.target) return res.status(400).json({ error: 'No target has been provided.' });
  57. // validate URL length
  58. if (body.target.length > 3000) {
  59. return res.status(400).json({ error: 'Maximum URL length is 3000.' });
  60. }
  61. // Validate URL
  62. const isValidUrl = urlRegex({ exact: true, strict: false }).test(body.target);
  63. if (!isValidUrl) return res.status(400).json({ error: 'URL is not valid.' });
  64. // Validate password length
  65. if (body.password && body.password.length > 64) {
  66. return res.status(400).json({ error: 'Maximum password length is 64.' });
  67. }
  68. // Custom URL validations
  69. if (user && body.customurl) {
  70. // Validate custom URL
  71. if (!/^[a-zA-Z0-9-_]+$/g.test(body.customurl.trim())) {
  72. return res.status(400).json({ error: 'Custom URL is not valid.' });
  73. }
  74. // Prevent from using preserved URLs
  75. if (preservedUrls.some(url => url === body.customurl)) {
  76. return res.status(400).json({ error: "You can't use this custom URL name." });
  77. }
  78. // Validate custom URL length
  79. if (body.customurl.length > 64) {
  80. return res.status(400).json({ error: 'Maximum custom URL length is 64.' });
  81. }
  82. }
  83. return next();
  84. };
  85. exports.cooldownCheck = async ({ user }, res, next) => {
  86. if (user) {
  87. const { cooldowns } = await getCooldowns(user);
  88. if (cooldowns.length > 4) {
  89. await banUser(user);
  90. return res.status(400).json({ error: 'Too much malware requests. You are now banned.' });
  91. }
  92. const hasCooldownNow = cooldowns.some(cooldown => cooldown > subHours(new Date(), 12).toJSON());
  93. if (hasCooldownNow) {
  94. return res.status(400).json({ error: 'Cooldown because of a malware URL. Wait 12h' });
  95. }
  96. }
  97. return next();
  98. };
  99. exports.malwareCheck = async ({ body, user }, res, next) => {
  100. const isMalware = await axios.post(
  101. `https://safebrowsing.googleapis.com/v4/threatMatches:find?key=${
  102. config.GOOGLE_SAFE_BROWSING_KEY
  103. }`,
  104. {
  105. client: {
  106. clientId: config.DEFAULT_DOMAIN.toLowerCase().replace('.', ''),
  107. clientVersion: '1.0.0',
  108. },
  109. threatInfo: {
  110. threatTypes: [
  111. 'THREAT_TYPE_UNSPECIFIED',
  112. 'MALWARE',
  113. 'SOCIAL_ENGINEERING',
  114. 'UNWANTED_SOFTWARE',
  115. 'POTENTIALLY_HARMFUL_APPLICATION',
  116. ],
  117. platformTypes: ['ANY_PLATFORM', 'PLATFORM_TYPE_UNSPECIFIED'],
  118. threatEntryTypes: ['EXECUTABLE', 'URL', 'THREAT_ENTRY_TYPE_UNSPECIFIED'],
  119. threatEntries: [{ url: body.target }],
  120. },
  121. }
  122. );
  123. if (isMalware.data && isMalware.data.matches) {
  124. if (user) {
  125. await addCooldown(user);
  126. }
  127. return res
  128. .status(400)
  129. .json({ error: user ? 'Malware detected! Cooldown for 12h.' : 'Malware detected!' });
  130. }
  131. return next();
  132. };