Added URL validator

This commit is contained in:
Sebastian Seedorf
2020-05-27 01:51:47 +02:00
parent d33ce96d18
commit d0cea00ff4
2 changed files with 104 additions and 5 deletions

View File

@@ -38,17 +38,27 @@ Deno.test("isString (no match)", async () => {
Deno.test("isURL (match)", async () => {
const values = [
"http://google.com",
"http://10.1.1.1",
"http://10.1.1.254",
"http://223.255.255.254",
" data:,Hello World!"
];
for (const value of values) {
assertEquals(await validate(value, isURL()), []);
assertEquals(await validate(value, isURL({allowLocal: true, allowDataUrl: true})), [], value);
}
});
Deno.test("isURL (no match)", async () => {
const values = [
"invalid",
"http://0.0.0.0",
"http://10.1.1.0",
"http://10.1.1.255",
"http://224.1.1.1",
"http://1.1.1.1.1",
" data:,Hello World!"
];
for (const value of values) {
assertNotEquals(await validate(value, isURL()), []);
assertNotEquals(await validate(value, isURL({allowLocal: true})), [], value);
}
});

View File

@@ -15,15 +15,104 @@ export function isString(): Validator {
};
}
export function isURL(): Validator {
/**
* Thanks to https://gist.github.com/dperini/729294
* Data URL https://gist.github.com/bgrins/6194623
*
* @param param Options
*/
export function isURL(
{
protocols = ["http", "https"],
allowDataUrl = false,
allowUrl = true,
allowLocal = false,
allowIp = true,
allowDomain = true,
allowBasicAuth = false,
allowPort = true,
allowRecourcePath = true
}: {
protocols?: string[] | null;
allowDataUrl?: boolean;
allowUrl?: boolean;
allowLocal?: boolean;
allowIp?: boolean;
allowDomain?: boolean;
allowBasicAuth?: boolean;
allowPort?: boolean;
allowRecourcePath?: boolean;
} = {},
): Validator {
return {
type: "isURL",
extends: [isString()],
check: (value: any) => {
if (value === null || value === undefined) return;
if (value !== "http://google.com") {
return {};
if (allowUrl) {
let regex = "^";
// protocol identifier (optional)
// short syntax // still required
if (protocols) {
regex += `(?:(?:(?:${protocols.join("|")}):)?\\/\\/)`
} else {
regex += `(?:(?:(?:[a-z]+):)?\/\/)`
}
// user:pass BasicAuth (optional)
if (allowBasicAuth) {
regex += "(?:\\S+(?::\\S*)?@)?";
}
regex += "(?:"; // [hostname] start
if (allowIp && !allowLocal) {
regex += "(?!(?:10|127)(?:\\.\\d{1,3}){3})" +
"(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +
"(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})";
}
if (allowIp) {
regex += "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
"(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
"(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))";
}
if (allowIp || allowDomain) {
regex += "|"; // [hostname] ip end / domain start
}
if (allowDomain) {
// host & domain names, may end with dot
// can be replaced by a shortest alternative
// (?![-_])(?:[-\\w\\u00a1-\\uffff]{0,63}[^-_]\\.)+
regex +=
"(?:" +
"(?:" +
"[a-z0-9\\u00a1-\\uffff]" +
"[a-z0-9\\u00a1-\\uffff_-]{0,62}" +
")?" +
"[a-z0-9\\u00a1-\\uffff]\\." +
")+" +
// TLD identifier name, may end with dot
"(?:[a-z\\u00a1-\\uffff]{2,}\\.?)"
}
regex += ")"; // [hostname] end
if (allowPort) {
// port number (optional)
regex += "(?::\\d{2,5})?"
}
if (allowRecourcePath) {
// resource path (optional)
regex += "(?:[/?#]\\S*)?"
}
regex += "$";
if (value.match(new RegExp(regex, "i"))) {
return;
}
}
if (allowDataUrl) {
const regex = /^\s*data:([a-z]+\/[a-z]+(;[a-z\-]+\=[a-z\-]+)?)?(;base64)?,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$/i;
if (value.match(regex)) {
return;
}
}
return { };
},
message: (value: any, args?: Args) => {
return `This value is not a valid URL.`;