ab4b568b3f9c7087b3380d1a75132c12bb8db83c7d9cc1bd016f2e721a020632a72fe8c76542f883da73b802f14a62feb7eb1333b59cc15100de71ee688b21-exec 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. 'use strict';
  2. const Net = require('net');
  3. const Address = require('@hapi/address');
  4. const Hoek = require('@hapi/hoek');
  5. const Any = require('../any');
  6. const Ref = require('../../ref');
  7. const JoiDate = require('../date');
  8. const Uri = require('./uri');
  9. const Ip = require('./ip');
  10. const internals = {
  11. uriRegex: Uri.createUriRegex(),
  12. ipRegex: Ip.createIpRegex(['ipv4', 'ipv6', 'ipvfuture'], 'optional'),
  13. guidBrackets: {
  14. '{': '}', '[': ']', '(': ')', '': ''
  15. },
  16. guidVersions: {
  17. uuidv1: '1',
  18. uuidv2: '2',
  19. uuidv3: '3',
  20. uuidv4: '4',
  21. uuidv5: '5'
  22. },
  23. cidrPresences: ['required', 'optional', 'forbidden'],
  24. normalizationForms: ['NFC', 'NFD', 'NFKC', 'NFKD']
  25. };
  26. internals.String = class extends Any {
  27. constructor() {
  28. super();
  29. this._type = 'string';
  30. this._invalids.add('');
  31. }
  32. _base(value, state, options) {
  33. if (typeof value === 'string' &&
  34. options.convert) {
  35. if (this._flags.normalize) {
  36. value = value.normalize(this._flags.normalize);
  37. }
  38. if (this._flags.case) {
  39. value = (this._flags.case === 'upper' ? value.toLocaleUpperCase() : value.toLocaleLowerCase());
  40. }
  41. if (this._flags.trim) {
  42. value = value.trim();
  43. }
  44. if (this._inner.replacements) {
  45. for (let i = 0; i < this._inner.replacements.length; ++i) {
  46. const replacement = this._inner.replacements[i];
  47. value = value.replace(replacement.pattern, replacement.replacement);
  48. }
  49. }
  50. if (this._flags.truncate) {
  51. for (let i = 0; i < this._tests.length; ++i) {
  52. const test = this._tests[i];
  53. if (test.name === 'max') {
  54. value = value.slice(0, test.arg);
  55. break;
  56. }
  57. }
  58. }
  59. if (this._flags.byteAligned && value.length % 2 !== 0) {
  60. value = `0${value}`;
  61. }
  62. }
  63. return {
  64. value,
  65. errors: (typeof value === 'string') ? null : this.createError('string.base', { value }, state, options)
  66. };
  67. }
  68. insensitive() {
  69. if (this._flags.insensitive) {
  70. return this;
  71. }
  72. const obj = this.clone();
  73. obj._flags.insensitive = true;
  74. return obj;
  75. }
  76. creditCard() {
  77. return this._test('creditCard', undefined, function (value, state, options) {
  78. let i = value.length;
  79. let sum = 0;
  80. let mul = 1;
  81. while (i--) {
  82. const char = value.charAt(i) * mul;
  83. sum = sum + (char - (char > 9) * 9);
  84. mul = mul ^ 3;
  85. }
  86. const check = (sum % 10 === 0) && (sum > 0);
  87. return check ? value : this.createError('string.creditCard', { value }, state, options);
  88. });
  89. }
  90. regex(pattern, patternOptions) {
  91. Hoek.assert(pattern instanceof RegExp, 'pattern must be a RegExp');
  92. Hoek.assert(!pattern.flags.includes('g') && !pattern.flags.includes('y'), 'pattern should not use global or sticky mode');
  93. const patternObject = { pattern };
  94. if (typeof patternOptions === 'string') {
  95. patternObject.name = patternOptions;
  96. }
  97. else if (typeof patternOptions === 'object') {
  98. patternObject.invert = !!patternOptions.invert;
  99. if (patternOptions.name) {
  100. patternObject.name = patternOptions.name;
  101. }
  102. }
  103. const errorCode = ['string.regex', patternObject.invert ? '.invert' : '', patternObject.name ? '.name' : '.base'].join('');
  104. return this._test('regex', patternObject, function (value, state, options) {
  105. const patternMatch = patternObject.pattern.test(value);
  106. if (patternMatch ^ patternObject.invert) {
  107. return value;
  108. }
  109. return this.createError(errorCode, { name: patternObject.name, pattern: patternObject.pattern, value }, state, options);
  110. });
  111. }
  112. alphanum() {
  113. return this._test('alphanum', undefined, function (value, state, options) {
  114. if (/^[a-zA-Z0-9]+$/.test(value)) {
  115. return value;
  116. }
  117. return this.createError('string.alphanum', { value }, state, options);
  118. });
  119. }
  120. token() {
  121. return this._test('token', undefined, function (value, state, options) {
  122. if (/^\w+$/.test(value)) {
  123. return value;
  124. }
  125. return this.createError('string.token', { value }, state, options);
  126. });
  127. }
  128. email(validationOptions) {
  129. if (validationOptions) {
  130. Hoek.assert(typeof validationOptions === 'object', 'email options must be an object');
  131. // Migration validation for unsupported options
  132. Hoek.assert(validationOptions.checkDNS === undefined, 'checkDNS option is not supported');
  133. Hoek.assert(validationOptions.errorLevel === undefined, 'errorLevel option is not supported');
  134. Hoek.assert(validationOptions.minDomainAtoms === undefined, 'minDomainAtoms option is not supported, use minDomainSegments instead');
  135. Hoek.assert(validationOptions.tldBlacklist === undefined, 'tldBlacklist option is not supported, use tlds.deny instead');
  136. Hoek.assert(validationOptions.tldWhitelist === undefined, 'tldWhitelist option is not supported, use tlds.allow instead');
  137. // Validate options
  138. if (validationOptions.tlds &&
  139. typeof validationOptions.tlds === 'object') {
  140. Hoek.assert(validationOptions.tlds.allow === undefined ||
  141. validationOptions.tlds.allow === false ||
  142. validationOptions.tlds.allow === true ||
  143. Array.isArray(validationOptions.tlds.allow) ||
  144. validationOptions.tlds.allow instanceof Set, 'tlds.allow must be an array, Set, or boolean');
  145. Hoek.assert(validationOptions.tlds.deny === undefined ||
  146. Array.isArray(validationOptions.tlds.deny) ||
  147. validationOptions.tlds.deny instanceof Set, 'tlds.deny must be an array or Set');
  148. const normalizeTable = (table) => {
  149. if (table === undefined ||
  150. typeof table === 'boolean' ||
  151. table instanceof Set) {
  152. return table;
  153. }
  154. return new Set(table);
  155. };
  156. validationOptions = Object.assign({}, validationOptions); // Shallow cloned
  157. validationOptions.tlds = {
  158. allow: normalizeTable(validationOptions.tlds.allow),
  159. deny: normalizeTable(validationOptions.tlds.deny)
  160. };
  161. }
  162. Hoek.assert(validationOptions.minDomainSegments === undefined ||
  163. Number.isSafeInteger(validationOptions.minDomainSegments) && validationOptions.minDomainSegments > 0, 'minDomainSegments must be a positive integer');
  164. }
  165. return this._test('email', validationOptions, function (value, state, options) {
  166. if (Address.email.isValid(value, validationOptions)) {
  167. return value;
  168. }
  169. return this.createError('string.email', { value }, state, options);
  170. });
  171. }
  172. ip(ipOptions = {}) {
  173. let regex = internals.ipRegex;
  174. Hoek.assert(typeof ipOptions === 'object', 'options must be an object');
  175. if (ipOptions.cidr) {
  176. Hoek.assert(typeof ipOptions.cidr === 'string', 'cidr must be a string');
  177. ipOptions.cidr = ipOptions.cidr.toLowerCase();
  178. Hoek.assert(Hoek.contain(internals.cidrPresences, ipOptions.cidr), 'cidr must be one of ' + internals.cidrPresences.join(', '));
  179. // If we only received a `cidr` setting, create a regex for it. But we don't need to create one if `cidr` is "optional" since that is the default
  180. if (!ipOptions.version && ipOptions.cidr !== 'optional') {
  181. regex = Ip.createIpRegex(['ipv4', 'ipv6', 'ipvfuture'], ipOptions.cidr);
  182. }
  183. }
  184. else {
  185. // Set our default cidr strategy
  186. ipOptions.cidr = 'optional';
  187. }
  188. let versions;
  189. if (ipOptions.version) {
  190. if (!Array.isArray(ipOptions.version)) {
  191. ipOptions.version = [ipOptions.version];
  192. }
  193. Hoek.assert(ipOptions.version.length >= 1, 'version must have at least 1 version specified');
  194. versions = [];
  195. for (let i = 0; i < ipOptions.version.length; ++i) {
  196. let version = ipOptions.version[i];
  197. Hoek.assert(typeof version === 'string', 'version at position ' + i + ' must be a string');
  198. version = version.toLowerCase();
  199. Hoek.assert(Ip.versions[version], 'version at position ' + i + ' must be one of ' + Object.keys(Ip.versions).join(', '));
  200. versions.push(version);
  201. }
  202. // Make sure we have a set of versions
  203. versions = Array.from(new Set(versions));
  204. regex = Ip.createIpRegex(versions, ipOptions.cidr);
  205. }
  206. return this._test('ip', ipOptions, function (value, state, options) {
  207. if (regex.test(value)) {
  208. return value;
  209. }
  210. if (versions) {
  211. return this.createError('string.ipVersion', { value, cidr: ipOptions.cidr, version: versions }, state, options);
  212. }
  213. return this.createError('string.ip', { value, cidr: ipOptions.cidr }, state, options);
  214. });
  215. }
  216. uri(uriOptions) {
  217. let customScheme = '';
  218. let allowRelative = false;
  219. let relativeOnly = false;
  220. let allowQuerySquareBrackets = false;
  221. let regex = internals.uriRegex;
  222. if (uriOptions) {
  223. Hoek.assert(typeof uriOptions === 'object', 'options must be an object');
  224. const unknownOptions = Object.keys(uriOptions).filter((key) => !['scheme', 'allowRelative', 'relativeOnly', 'allowQuerySquareBrackets'].includes(key));
  225. Hoek.assert(unknownOptions.length === 0, `options contain unknown keys: ${unknownOptions}`);
  226. if (uriOptions.scheme) {
  227. Hoek.assert(uriOptions.scheme instanceof RegExp || typeof uriOptions.scheme === 'string' || Array.isArray(uriOptions.scheme), 'scheme must be a RegExp, String, or Array');
  228. if (!Array.isArray(uriOptions.scheme)) {
  229. uriOptions.scheme = [uriOptions.scheme];
  230. }
  231. Hoek.assert(uriOptions.scheme.length >= 1, 'scheme must have at least 1 scheme specified');
  232. // Flatten the array into a string to be used to match the schemes.
  233. for (let i = 0; i < uriOptions.scheme.length; ++i) {
  234. const scheme = uriOptions.scheme[i];
  235. Hoek.assert(scheme instanceof RegExp || typeof scheme === 'string', 'scheme at position ' + i + ' must be a RegExp or String');
  236. // Add OR separators if a value already exists
  237. customScheme = customScheme + (customScheme ? '|' : '');
  238. // If someone wants to match HTTP or HTTPS for example then we need to support both RegExp and String so we don't escape their pattern unknowingly.
  239. if (scheme instanceof RegExp) {
  240. customScheme = customScheme + scheme.source;
  241. }
  242. else {
  243. Hoek.assert(/[a-zA-Z][a-zA-Z0-9+-\.]*/.test(scheme), 'scheme at position ' + i + ' must be a valid scheme');
  244. customScheme = customScheme + Hoek.escapeRegex(scheme);
  245. }
  246. }
  247. }
  248. if (uriOptions.allowRelative) {
  249. allowRelative = true;
  250. }
  251. if (uriOptions.relativeOnly) {
  252. relativeOnly = true;
  253. }
  254. if (uriOptions.allowQuerySquareBrackets) {
  255. allowQuerySquareBrackets = true;
  256. }
  257. }
  258. if (customScheme || allowRelative || relativeOnly || allowQuerySquareBrackets) {
  259. regex = Uri.createUriRegex(customScheme, allowRelative, relativeOnly, allowQuerySquareBrackets);
  260. }
  261. return this._test('uri', uriOptions, function (value, state, options) {
  262. if (regex.test(value)) {
  263. return value;
  264. }
  265. if (relativeOnly) {
  266. return this.createError('string.uriRelativeOnly', { value }, state, options);
  267. }
  268. if (customScheme) {
  269. return this.createError('string.uriCustomScheme', { scheme: customScheme, value }, state, options);
  270. }
  271. return this.createError('string.uri', { value }, state, options);
  272. });
  273. }
  274. isoDate() {
  275. return this._test('isoDate', undefined, function (value, state, options) {
  276. if (JoiDate._isIsoDate(value)) {
  277. if (!options.convert) {
  278. return value;
  279. }
  280. const d = new Date(value);
  281. if (!isNaN(d.getTime())) {
  282. return d.toISOString();
  283. }
  284. }
  285. return this.createError('string.isoDate', { value }, state, options);
  286. });
  287. }
  288. guid(guidOptions) {
  289. let versionNumbers = '';
  290. if (guidOptions && guidOptions.version) {
  291. if (!Array.isArray(guidOptions.version)) {
  292. guidOptions.version = [guidOptions.version];
  293. }
  294. Hoek.assert(guidOptions.version.length >= 1, 'version must have at least 1 valid version specified');
  295. const versions = new Set();
  296. for (let i = 0; i < guidOptions.version.length; ++i) {
  297. let version = guidOptions.version[i];
  298. Hoek.assert(typeof version === 'string', 'version at position ' + i + ' must be a string');
  299. version = version.toLowerCase();
  300. const versionNumber = internals.guidVersions[version];
  301. Hoek.assert(versionNumber, 'version at position ' + i + ' must be one of ' + Object.keys(internals.guidVersions).join(', '));
  302. Hoek.assert(!(versions.has(versionNumber)), 'version at position ' + i + ' must not be a duplicate.');
  303. versionNumbers += versionNumber;
  304. versions.add(versionNumber);
  305. }
  306. }
  307. const guidRegex = new RegExp(`^([\\[{\\(]?)[0-9A-F]{8}([:-]?)[0-9A-F]{4}\\2?[${versionNumbers || '0-9A-F'}][0-9A-F]{3}\\2?[${versionNumbers ? '89AB' : '0-9A-F'}][0-9A-F]{3}\\2?[0-9A-F]{12}([\\]}\\)]?)$`, 'i');
  308. return this._test('guid', guidOptions, function (value, state, options) {
  309. const results = guidRegex.exec(value);
  310. if (!results) {
  311. return this.createError('string.guid', { value }, state, options);
  312. }
  313. // Matching braces
  314. if (internals.guidBrackets[results[1]] !== results[results.length - 1]) {
  315. return this.createError('string.guid', { value }, state, options);
  316. }
  317. return value;
  318. });
  319. }
  320. hex(hexOptions = {}) {
  321. Hoek.assert(typeof hexOptions === 'object', 'hex options must be an object');
  322. Hoek.assert(typeof hexOptions.byteAligned === 'undefined' || typeof hexOptions.byteAligned === 'boolean',
  323. 'byteAligned must be boolean');
  324. const byteAligned = hexOptions.byteAligned === true;
  325. const regex = /^[a-f0-9]+$/i;
  326. const obj = this._test('hex', regex, function (value, state, options) {
  327. if (regex.test(value)) {
  328. if (byteAligned && value.length % 2 !== 0) {
  329. return this.createError('string.hexAlign', { value }, state, options);
  330. }
  331. return value;
  332. }
  333. return this.createError('string.hex', { value }, state, options);
  334. });
  335. if (byteAligned) {
  336. obj._flags.byteAligned = true;
  337. }
  338. return obj;
  339. }
  340. base64(base64Options = {}) {
  341. // Validation.
  342. Hoek.assert(typeof base64Options === 'object', 'base64 options must be an object');
  343. Hoek.assert(typeof base64Options.paddingRequired === 'undefined' || typeof base64Options.paddingRequired === 'boolean',
  344. 'paddingRequired must be boolean');
  345. // Determine if padding is required.
  346. const paddingRequired = base64Options.paddingRequired === false ?
  347. base64Options.paddingRequired
  348. : base64Options.paddingRequired || true;
  349. // Set validation based on preference.
  350. const regex = paddingRequired ?
  351. // Padding is required.
  352. /^(?:[A-Za-z0-9+\/]{2}[A-Za-z0-9+\/]{2})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/
  353. // Padding is optional.
  354. : /^(?:[A-Za-z0-9+\/]{2}[A-Za-z0-9+\/]{2})*(?:[A-Za-z0-9+\/]{2}(==)?|[A-Za-z0-9+\/]{3}=?)?$/;
  355. return this._test('base64', regex, function (value, state, options) {
  356. if (regex.test(value)) {
  357. return value;
  358. }
  359. return this.createError('string.base64', { value }, state, options);
  360. });
  361. }
  362. dataUri(dataUriOptions = {}) {
  363. const regex = /^data:[\w+.-]+\/[\w+.-]+;((charset=[\w-]+|base64),)?(.*)$/;
  364. // Determine if padding is required.
  365. const paddingRequired = dataUriOptions.paddingRequired === false ?
  366. dataUriOptions.paddingRequired
  367. : dataUriOptions.paddingRequired || true;
  368. const base64regex = paddingRequired ?
  369. /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/
  370. : /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}(==)?|[A-Za-z0-9+\/]{3}=?)?$/;
  371. return this._test('dataUri', regex, function (value, state, options) {
  372. const matches = value.match(regex);
  373. if (matches) {
  374. if (!matches[2]) {
  375. return value;
  376. }
  377. if (matches[2] !== 'base64') {
  378. return value;
  379. }
  380. if (base64regex.test(matches[3])) {
  381. return value;
  382. }
  383. }
  384. return this.createError('string.dataUri', { value }, state, options);
  385. });
  386. }
  387. hostname() {
  388. const regex = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/;
  389. return this._test('hostname', undefined, function (value, state, options) {
  390. if ((value.length <= 255 && regex.test(value)) ||
  391. Net.isIPv6(value)) {
  392. return value;
  393. }
  394. return this.createError('string.hostname', { value }, state, options);
  395. });
  396. }
  397. normalize(form = 'NFC') {
  398. Hoek.assert(Hoek.contain(internals.normalizationForms, form), 'normalization form must be one of ' + internals.normalizationForms.join(', '));
  399. const obj = this._test('normalize', form, function (value, state, options) {
  400. if (options.convert ||
  401. value === value.normalize(form)) {
  402. return value;
  403. }
  404. return this.createError('string.normalize', { value, form }, state, options);
  405. });
  406. obj._flags.normalize = form;
  407. return obj;
  408. }
  409. lowercase() {
  410. const obj = this._test('lowercase', undefined, function (value, state, options) {
  411. if (options.convert ||
  412. value === value.toLocaleLowerCase()) {
  413. return value;
  414. }
  415. return this.createError('string.lowercase', { value }, state, options);
  416. });
  417. obj._flags.case = 'lower';
  418. return obj;
  419. }
  420. uppercase() {
  421. const obj = this._test('uppercase', undefined, function (value, state, options) {
  422. if (options.convert ||
  423. value === value.toLocaleUpperCase()) {
  424. return value;
  425. }
  426. return this.createError('string.uppercase', { value }, state, options);
  427. });
  428. obj._flags.case = 'upper';
  429. return obj;
  430. }
  431. trim(enabled = true) {
  432. Hoek.assert(typeof enabled === 'boolean', 'option must be a boolean');
  433. if ((this._flags.trim && enabled) || (!this._flags.trim && !enabled)) {
  434. return this;
  435. }
  436. let obj;
  437. if (enabled) {
  438. obj = this._test('trim', undefined, function (value, state, options) {
  439. if (options.convert ||
  440. value === value.trim()) {
  441. return value;
  442. }
  443. return this.createError('string.trim', { value }, state, options);
  444. });
  445. }
  446. else {
  447. obj = this.clone();
  448. obj._tests = obj._tests.filter((test) => test.name !== 'trim');
  449. }
  450. obj._flags.trim = enabled;
  451. return obj;
  452. }
  453. replace(pattern, replacement) {
  454. if (typeof pattern === 'string') {
  455. pattern = new RegExp(Hoek.escapeRegex(pattern), 'g');
  456. }
  457. Hoek.assert(pattern instanceof RegExp, 'pattern must be a RegExp');
  458. Hoek.assert(typeof replacement === 'string', 'replacement must be a String');
  459. // This can not be considere a test like trim, we can't "reject"
  460. // anything from this rule, so just clone the current object
  461. const obj = this.clone();
  462. if (!obj._inner.replacements) {
  463. obj._inner.replacements = [];
  464. }
  465. obj._inner.replacements.push({
  466. pattern,
  467. replacement
  468. });
  469. return obj;
  470. }
  471. truncate(enabled) {
  472. const value = enabled === undefined ? true : !!enabled;
  473. if (this._flags.truncate === value) {
  474. return this;
  475. }
  476. const obj = this.clone();
  477. obj._flags.truncate = value;
  478. return obj;
  479. }
  480. };
  481. internals.compare = function (type, compare) {
  482. return function (limit, encoding) {
  483. const isRef = Ref.isRef(limit);
  484. Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');
  485. Hoek.assert(!encoding || Buffer.isEncoding(encoding), 'Invalid encoding:', encoding);
  486. return this._test(type, limit, function (value, state, options) {
  487. let compareTo;
  488. if (isRef) {
  489. compareTo = limit(state.reference || state.parent, options);
  490. if (!Number.isSafeInteger(compareTo)) {
  491. return this.createError('string.ref', { ref: limit, value: compareTo }, state, options);
  492. }
  493. }
  494. else {
  495. compareTo = limit;
  496. }
  497. if (compare(value, compareTo, encoding)) {
  498. return value;
  499. }
  500. return this.createError('string.' + type, { limit: compareTo, value, encoding }, state, options);
  501. });
  502. };
  503. };
  504. internals.String.prototype.min = internals.compare('min', (value, limit, encoding) => {
  505. const length = encoding ? Buffer.byteLength(value, encoding) : value.length;
  506. return length >= limit;
  507. });
  508. internals.String.prototype.max = internals.compare('max', (value, limit, encoding) => {
  509. const length = encoding ? Buffer.byteLength(value, encoding) : value.length;
  510. return length <= limit;
  511. });
  512. internals.String.prototype.length = internals.compare('length', (value, limit, encoding) => {
  513. const length = encoding ? Buffer.byteLength(value, encoding) : value.length;
  514. return length === limit;
  515. });
  516. // Aliases
  517. internals.String.prototype.uuid = internals.String.prototype.guid;
  518. module.exports = new internals.String();