Skip to content

Commit

Permalink
fix url
Browse files Browse the repository at this point in the history
  • Loading branch information
yiminghe committed Jun 17, 2022
1 parent cc126d6 commit 51c6b05
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 33 deletions.
6 changes: 4 additions & 2 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "async-validator",
"version": "4.1.1",
"version": "4.2.2",
"description": "validate form asynchronous",
"typings": "typings/index.d.ts",
"keywords": [
Expand Down Expand Up @@ -52,8 +52,9 @@
"port": 8010
},
"scripts": {
"test-url":"yarn ts-node tests/url.ts",
"lint-staged": "lint-staged",
"prettier": "prettier --write \"{src,__tests__}/**/*.{js,tsx}\"",
"prettier": "prettier --write \"{src,__tests__,tests}/**/*.{js,tsx,ts}\"",
"pub": "np --no-publish --no-release-draft && npm run build && cd pkg && npm publish",
"build": "pika-pack build",
"test": "jest",
Expand All @@ -79,6 +80,7 @@
"pika-plugin-ts-types": "0.1.x",
"pre-commit": "^1.2.2",
"prettier": "^1.11.1",
"ts-node": "^10.8.1",
"typescript": "^4.3.2"
},
"lint-staged": {
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Expand Up @@ -88,7 +88,7 @@ class Schema {
validate(source: Values, callback: ValidateCallback): Promise<Values>;
validate(source: Values): Promise<Values>;

validate(source_: Values, o: any = {}, oc: any = () => { }): Promise<Values> {
validate(source_: Values, o: any = {}, oc: any = () => {}): Promise<Values> {
let source: Values = source_;
let options: ValidateOption = o;
let callback: ValidateCallback = oc;
Expand Down
12 changes: 6 additions & 6 deletions src/rule/type.ts
@@ -1,16 +1,16 @@
import { ExecuteRule, Value } from '../interface';
import { format } from '../util';
import required from './required';

import getUrlRegex from './url';
/* eslint max-len:0 */

const pattern = {
// http://emailregex.com/
email: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+\.)+[a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{2,}))$/,
url: new RegExp(
'^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$',
'i',
),
// url: new RegExp(
// '^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$',
// 'i',
// ),
hex: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/i,
};

Expand Down Expand Up @@ -65,7 +65,7 @@ const types = {
return (
typeof value === 'string' &&
value.length <= 2048 &&
!!value.match(pattern.url)
!!value.match(getUrlRegex())
);
},
hex(value: Value) {
Expand Down
71 changes: 71 additions & 0 deletions src/rule/url.ts
@@ -0,0 +1,71 @@
// https://github.com/kevva/url-regex/blob/master/index.js

const word = '[a-fA-F\\d:]';
const b = options =>
options && options.includeBoundaries
? `(?:(?<=\\s|^)(?=${word})|(?<=${word})(?=\\s|$))`
: '';

const v4 =
'(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}';

const v6seg = '[a-fA-F\\d]{1,4}';
const v6 = `
(?:
(?:${v6seg}:){7}(?:${v6seg}|:)| // 1:2:3:4:5:6:7:: 1:2:3:4:5:6:7:8
(?:${v6seg}:){6}(?:${v4}|:${v6seg}|:)| // 1:2:3:4:5:6:: 1:2:3:4:5:6::8 1:2:3:4:5:6::8 1:2:3:4:5:6::1.2.3.4
(?:${v6seg}:){5}(?::${v4}|(?::${v6seg}){1,2}|:)| // 1:2:3:4:5:: 1:2:3:4:5::7:8 1:2:3:4:5::8 1:2:3:4:5::7:1.2.3.4
(?:${v6seg}:){4}(?:(?::${v6seg}){0,1}:${v4}|(?::${v6seg}){1,3}|:)| // 1:2:3:4:: 1:2:3:4::6:7:8 1:2:3:4::8 1:2:3:4::6:7:1.2.3.4
(?:${v6seg}:){3}(?:(?::${v6seg}){0,2}:${v4}|(?::${v6seg}){1,4}|:)| // 1:2:3:: 1:2:3::5:6:7:8 1:2:3::8 1:2:3::5:6:7:1.2.3.4
(?:${v6seg}:){2}(?:(?::${v6seg}){0,3}:${v4}|(?::${v6seg}){1,5}|:)| // 1:2:: 1:2::4:5:6:7:8 1:2::8 1:2::4:5:6:7:1.2.3.4
(?:${v6seg}:){1}(?:(?::${v6seg}){0,4}:${v4}|(?::${v6seg}){1,6}|:)| // 1:: 1::3:4:5:6:7:8 1::8 1::3:4:5:6:7:1.2.3.4
(?::(?:(?::${v6seg}){0,5}:${v4}|(?::${v6seg}){1,7}|:)) // ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 ::1.2.3.4
)(?:%[0-9a-zA-Z]{1,})? // %eth0 %1
`
.replace(/\s*\/\/.*$/gm, '')
.replace(/\n/g, '')
.trim();

// Pre-compile only the exact regexes because adding a global flag make regexes stateful
const v46Exact = new RegExp(`(?:^${v4}$)|(?:^${v6}$)`);
const v4exact = new RegExp(`^${v4}$`);
const v6exact = new RegExp(`^${v6}$`);

const ip = options =>
options && options.exact
? v46Exact
: new RegExp(
`(?:${b(options)}${v4}${b(options)})|(?:${b(options)}${v6}${b(
options,
)})`,
'g',
);

ip.v4 = options =>
options && options.exact
? v4exact
: new RegExp(`${b(options)}${v4}${b(options)}`, 'g');
ip.v6 = options =>
options && options.exact
? v6exact
: new RegExp(`${b(options)}${v6}${b(options)}`, 'g');

let reg;

export default () => {
if (reg) {
return reg;
}
const protocol = `(?:(?:[a-z]+:)?//)`;
const auth = '(?:\\S+(?::\\S*)?@)?';
const ipSource = ip.v4({}).source;
const host = '(?:(?:[a-z\\u00a1-\\uffff0-9][-_]*)*[a-z\\u00a1-\\uffff0-9]+)';
const domain =
'(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*';
const tld = `(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))`;
const port = '(?::\\d{2,5})?';
const path = '(?:[/?#][^\\s"]*)?';
const regex = `(?:${protocol}|www\\.)${auth}(?:localhost|${ipSource}|${host}${domain}${tld})${port}${path}`;
reg = new RegExp(`(?:^${regex}$)`, 'i');
return reg;
};
10 changes: 6 additions & 4 deletions src/util.ts
Expand Up @@ -15,7 +15,7 @@ const formatRegExp = /%[sdj%]/g;

declare var ASYNC_VALIDATOR_NO_WARNING;

export let warning: (type: string, errors: SyncErrorType[]) => void = () => { };
export let warning: (type: string, errors: SyncErrorType[]) => void = () => {};

// don't print warning message when in production env or node runtime
if (
Expand All @@ -26,7 +26,9 @@ if (
typeof document !== 'undefined'
) {
warning = (type, errors) => {
if (typeof console !== 'undefined' && console.warn &&
if (
typeof console !== 'undefined' &&
console.warn &&
typeof ASYNC_VALIDATOR_NO_WARNING === 'undefined'
) {
if (errors.every(e => typeof e === 'string')) {
Expand Down Expand Up @@ -227,8 +229,8 @@ export function asyncMap(
callback(results);
return results.length
? reject(
new AsyncValidationError(results, convertFieldsError(results)),
)
new AsyncValidationError(results, convertFieldsError(results)),
)
: resolve(source);
}
};
Expand Down
19 changes: 0 additions & 19 deletions tests/url.js

This file was deleted.

35 changes: 35 additions & 0 deletions tests/url.ts
@@ -0,0 +1,35 @@
import AsyncValidator from '../src';

const validator = new AsyncValidator({
v: {
type: 'url',
},
});

for (var i = 1; i <= 1000; i++) {
var time = Date.now();
var attack_str = '//a.b' + 'c1'.repeat(i) + 'a';
validator.validate({
v: attack_str,
});
var time_cost = Date.now() - time;
console.log(
'attack_str.length: ' + attack_str.length + ': ' + time_cost + ' ms',
);
}

if (false) {
console.log('*'.repeat(10));

for (var i = 1; i <= 50000; i++) {
var time = Date.now();
var attack_str = '//' + ':'.repeat(i * 10000) + '@';
validator.validate({
v: attack_str,
});
var time_cost = Date.now() - time;
console.log(
'attack_str.length: ' + attack_str.length + ': ' + time_cost + ' ms',
);
}
}
2 changes: 1 addition & 1 deletion tsconfig.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"outDir": "lib",
"module": "ESNext",
"module": "commonjs",
"target": "ES2020",
"lib": [
"es2016",
Expand Down

0 comments on commit 51c6b05

Please sign in to comment.