Fix input validation for fields in monitor edit form

This commit is contained in:
ekrekeler 2025-02-26 00:18:47 -06:00
parent aaf438a248
commit 21f629e055
No known key found for this signature in database
GPG key ID: 4C66C864B6B00854
2 changed files with 55 additions and 24 deletions

View file

@ -290,9 +290,9 @@
type="text"
class="form-control"
:pattern="`${
monitor.type === 'mqtt' ? mqttIpOrHostnameRegexPattern :
monitor.type === 'dns' ? ipOrDnsNameRegexPattern :
ipOrHostnameRegexPattern
monitor.type === 'mqtt' ? mqttIpOrHostnameRegexPattern.source :
monitor.type === 'dns' ? ipOrDnsNameRegexPattern.source :
ipOrHostnameRegexPattern.source
}`"
required
data-testid="hostname-input"
@ -371,7 +371,7 @@
<template v-if="monitor.type === 'dns'">
<div class="my-3">
<label for="dns_resolve_server" class="form-label">{{ $t("Resolver Server") }}</label>
<input id="dns_resolve_server" v-model="monitor.dns_resolve_server" type="text" class="form-control" :pattern="ipOrHostnameRegex" required>
<input id="dns_resolve_server" v-model="monitor.dns_resolve_server" type="text" class="form-control" :pattern="dnsResolverRegex" required>
<div class="form-text">
{{ $t("resolverserverDescription") }}
</div>
@ -384,7 +384,7 @@
<input id="doh_query_path" v-model="monitor.doh_query_path" type="text" class="form-control" :pattern="urlQueryRegex" placeholder="dns-query?dns={query}">
</div>
<div class="form-text">
{{ $t("dohQueryPathDescription") }}{{ ' "{query}".' }}
{{ $t("dohQueryPathDescription") + " " }}{{ '"{query}".' }}
</div>
</div>
@ -1179,7 +1179,8 @@ export default {
dnsresolvetypeOptions: [],
dnsTransportOptions: [],
kafkaSaslMechanismOptions: [],
ipRegexPattern: hostNameRegexPattern().split("|")[0],
ipRegexPattern: hostNameRegexPattern(false, true, false),
hostnameRegexPattern: hostNameRegexPattern(false, false, true),
ipOrHostnameRegexPattern: hostNameRegexPattern(),
mqttIpOrHostnameRegexPattern: hostNameRegexPattern(true),
ipOrDnsNameRegexPattern: dnsNameRegexPattern(),
@ -1202,7 +1203,7 @@ export default {
// Allow to test with simple dns server with port (127.0.0.1:5300)
if (! isDev) {
return this.ipRegexPattern;
return this.ipRegexPattern.source;
}
return null;
},
@ -1211,16 +1212,24 @@ export default {
// Permit either IP address or hostname (127.0.0.1, dns.example.com)
if (! isDev) {
return this.ipOrHostnameRegexPattern;
return this.ipOrHostnameRegexPattern.source;
}
return null;
},
dnsNameRegex() {
dnsResolverRegex() {
// Permit IP address, hostname, TLD, or root
// Permit IP address for TCP/UDP resolvers, hostname for DoH/DoT
if (! isDev) {
return this.ipOrDnsNameRegexPattern;
switch (this.monitor.dns_transport) {
case "UDP":
case "TCP":
return this.ipRegexPattern.source;
case "DoH":
case "DoT":
return this.hostnameRegexPattern.source;
}
}
return null;
},
@ -1229,7 +1238,7 @@ export default {
// Permit only URL paths with a query parameter ( {query} )
if (! isDev) {
return this.queryRegexPattern;
return this.queryRegexPattern.source;
}
return null;
},

View file

@ -112,17 +112,37 @@ export function getDevContainerServerHostname() {
* Regex pattern for identifying hostnames and IP addresses
* @param {boolean} mqtt whether or not the regex should take into
* account the fact that it is an mqtt uri
* @param {boolean} ip whether the regex should match IP addresses
* @param {boolean} hostname whether the regex should match hostnames
* @returns {RegExp} The requested regex
*/
export function hostNameRegexPattern(mqtt = false) {
export function hostNameRegexPattern(mqtt = false, ip = true, hostname = true) {
// mqtt, mqtts, ws and wss schemes accepted by mqtt.js (https://github.com/mqttjs/MQTT.js/#connect)
const mqttSchemeRegexPattern = "((mqtt|ws)s?:\\/\\/)?";
const mqttSchemeRegexPattern = /((mqtt|ws)s?:\/\/)?/;
// Source: https://digitalfortress.tech/tips/top-15-commonly-used-regex/
const ipRegexPattern = `((^${mqtt ? mqttSchemeRegexPattern : ""}((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))$)|(^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?$))`;
// Source: https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address
const hostNameRegexPattern = `^${mqtt ? mqttSchemeRegexPattern : ""}([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\\-_]*[A-Za-z0-9_])(\\.)?$`;
const ipv4RegexPattern = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/;
const ipv6RegexPattern = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
// See this answer for detailed a explanation: https://stackoverflow.com/a/53875771/1854468
/* eslint-disable-next-line no-useless-escape */
const hostNameRegexPattern = /^[a-zA-Z][a-zA-Z0-9\-]{0,62}\.([a-zA-Z0-9][a-zA-Z0-9\-]{0,62}\.)*([a-zA-Z]{2,63}|[xX][nN]--[a-zA-Z0-9]{0,59})$/;
/* eslint-disable-next-line no-useless-escape */
const localNameRegexPattern = /^[a-zA-Z0-9][a-zA-Z0-9\-]{0,127}$/;
return `${ipRegexPattern}|${hostNameRegexPattern}`;
let patterns = [];
if (ip) {
patterns.push(ipv4RegexPattern, ipv6RegexPattern);
}
if (hostname) {
patterns.push(hostNameRegexPattern, localNameRegexPattern);
}
if (mqtt) {
// all modified patterns must start with "^" for this to work
patterns = patterns.map(pattern => {
return new RegExp(`^${mqttSchemeRegexPattern.source}${pattern.source.slice(1)}`);
});
}
return new RegExp(patterns.map(pattern => `(${pattern.source})`).join("|"));
}
/**
@ -131,12 +151,13 @@ export function hostNameRegexPattern(mqtt = false) {
*/
export function dnsNameRegexPattern() {
// This borrows ipRegexPattern from hostNameRegexPattern above
const ipRegexPattern = hostNameRegexPattern().split("|")[0];
const ipRegexPattern = hostNameRegexPattern(false, true, false);
// Similar to hostNameRegexPattern, except the hostname pattern
// can also match root (.) and top-level domains (.com, .org)
const dnsNamePattern = "^(\\.|(\\.?[a-zA-Z0-9_]+)+)$";
// can also match root (.) and top-level domains (.com, .org),
// and may contain underscores (_)
const dnsNamePattern = /^(\.|(\.?[a-zA-Z0-9\-_]+)+)$/;
return `${ipRegexPattern}|${dnsNamePattern}`;
return new RegExp(`(${ipRegexPattern.source})|(${dnsNamePattern.source})`);
}
/**
@ -146,9 +167,10 @@ export function dnsNameRegexPattern() {
*/
export function urlPathRegexPattern(qstr = false) {
// Ensures a URL path follows query string format
const queryStringRegexPattern = "^/?(([a-zA-Z0-9\\-_%])+/)*[a-zA-Z0-9\\-_%]*\\?([a-zA-Z0-9\\-_%]+=[a-zA-Z0-9\\-_%]*&?)+$";
const queryStringRegexPattern = /^\/?(([a-zA-Z0-9\-_%])+\/)*[a-zA-Z0-9\-_%]*\?([a-zA-Z0-9\-_%]+=[a-zA-Z0-9\-_%]*&?)+$/;
// Only checks for valid URL path containing "{query}"
const queryRegexPattern = "^[a-zA-Z0-9\\-._~:\\/?#\\[\\]@!$&'\\(\\)*+,;=]*\\{query\\}[a-zA-Z0-9\\-._~:\\/?#\\[\\]@!$&'\\(\\)*+,;=]*$";
/* eslint-disable-next-line no-useless-escape */
const queryRegexPattern = /^[a-zA-Z0-9\-._~:\/?#\[\]@!$&'\(\)*+,;=]*\{query\}[a-zA-Z0-9\-._~:\/?#\[\]@!$&'\(\)*+,;=]*$/;
if (qstr) {
return queryStringRegexPattern;