no-insecure-random.js 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. // Copyright (c) Microsoft Corporation.
  2. // Licensed under the MIT License.
  3. /**
  4. * @fileoverview Rule to disallow call to Math.random and crypto.pseudoRandomBytes functions
  5. * @author Antonios Katopodis
  6. */
  7. "use strict";
  8. const astUtils = require("../ast-utils");
  9. const path = require('path');
  10. const bannedRandomLibraries = [
  11. 'chance',
  12. 'random-number',
  13. 'random-int',
  14. 'random-float',
  15. 'random-seed',
  16. 'unique-random'
  17. ]
  18. //------------------------------------------------------------------------------
  19. // Rule Definition
  20. //------------------------------------------------------------------------------
  21. module.exports = {
  22. meta: {
  23. type: "suggestion",
  24. fixable: "code",
  25. schema: [],
  26. docs:{
  27. description: `Methods such as Math.random or crypto.pseudoRandomBytes do not produce cryptographically-secure random numbers and must not be used for security purposes such as generating tokens, passwords or keys.
  28. Use crypto.randomBytes() or window.crypto.getRandomValues() instead.
  29. `,
  30. url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-insecure-random.md"
  31. },
  32. messages: {
  33. default: 'Do not use pseudo-random number generators for generating secret values such as tokens, passwords or keys.'
  34. }
  35. },
  36. create: function(context) {
  37. const fullTypeChecker = astUtils.getFullTypeChecker(context);
  38. return {
  39. "CallExpression > MemberExpression[property.name='pseudoRandomBytes']"(node) {
  40. var notFalsePositive = false;
  41. if (fullTypeChecker) {
  42. const type = astUtils.getNodeTypeAsString(fullTypeChecker, node.object, context);
  43. notFalsePositive = type === "any" || type === "Crypto";
  44. }else{
  45. notFalsePositive = node.object.name === 'crypto';
  46. }
  47. if(notFalsePositive){
  48. context.report({
  49. node: node,
  50. messageId: "default"
  51. });
  52. }
  53. },
  54. "CallExpression > MemberExpression[property.name='random']"(node) {
  55. var notFalsePositive = false;
  56. if (fullTypeChecker) {
  57. const type = astUtils.getNodeTypeAsString(fullTypeChecker, node.object, context);
  58. notFalsePositive = type === "any" || type === "Math";
  59. }else{
  60. notFalsePositive = node.object.name === 'Math';
  61. }
  62. if(notFalsePositive){
  63. context.report({
  64. node: node,
  65. messageId: "default"
  66. });
  67. }
  68. },
  69. ImportDeclaration(node){
  70. if(bannedRandomLibraries.includes(path.basename(node.source.value))){
  71. context.report({
  72. node: node,
  73. messageId: "default"
  74. });
  75. }
  76. },
  77. "CallExpression[callee.name='require'][arguments.length=1]"(node){
  78. var requireName = path.parse(path.basename(node.arguments[0].value)).name;
  79. if(bannedRandomLibraries.includes(requireName)){
  80. context.report({
  81. node: node,
  82. messageId: "default"
  83. });
  84. }
  85. }
  86. };
  87. }
  88. };