Added URL validator
This commit is contained in:
@@ -38,17 +38,27 @@ Deno.test("isString (no match)", async () => {
|
|||||||
Deno.test("isURL (match)", async () => {
|
Deno.test("isURL (match)", async () => {
|
||||||
const values = [
|
const values = [
|
||||||
"http://google.com",
|
"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) {
|
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 () => {
|
Deno.test("isURL (no match)", async () => {
|
||||||
const values = [
|
const values = [
|
||||||
"invalid",
|
"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) {
|
for (const value of values) {
|
||||||
assertNotEquals(await validate(value, isURL()), []);
|
assertNotEquals(await validate(value, isURL({allowLocal: true})), [], value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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 {
|
return {
|
||||||
type: "isURL",
|
type: "isURL",
|
||||||
extends: [isString()],
|
extends: [isString()],
|
||||||
check: (value: any) => {
|
check: (value: any) => {
|
||||||
if (value === null || value === undefined) return;
|
if (value === null || value === undefined) return;
|
||||||
if (value !== "http://google.com") {
|
if (allowUrl) {
|
||||||
return {};
|
|
||||||
|
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) => {
|
message: (value: any, args?: Args) => {
|
||||||
return `This value is not a valid URL.`;
|
return `This value is not a valid URL.`;
|
||||||
|
|||||||
Reference in New Issue
Block a user