From 2d9b9a4862f3ebe4f6ec3571e4310df03aac2629 Mon Sep 17 00:00:00 2001 From: Matthew Grotke Date: Mon, 1 Jun 2026 09:45:55 -0400 Subject: [PATCH] Development --- docker/routlin-dash/app/factory.py | 15 +++++++++++---- docker/routlin-dash/app/pages/dhcp/content.json | 2 +- .../app/pages/networklayout/content.json | 10 +++++----- .../app/pages/portwrangling/content.json | 2 +- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/docker/routlin-dash/app/factory.py b/docker/routlin-dash/app/factory.py index 7b91f2b..c96b2a3 100644 --- a/docker/routlin-dash/app/factory.py +++ b/docker/routlin-dash/app/factory.py @@ -145,10 +145,17 @@ function _checkLine(s){ var anyPartial=false,firstMsg=''; function _acc(r){if(r.ok)return r;if(r.partial)anyPartial=true;else if(!firstMsg)firstMsg=r.msg;return null;} var t; - if(validation&1){t=_acc(function(){var rv=_ipv4(s);if(rv==='ok')return _ok();if(rv==='partial'||rv==='empty')return _par('');if(rv==='badchar')return _err('Invalid character');if(rv==='badrange')return _err('Octet out of range');return _err('Invalid format');}());if(t)return t;} - if(validation&2){t=_acc(function(){var rv=_ipv6(s);if(rv==='ok')return _ok();if(rv==='partial'||rv==='empty')return _par('');if(rv==='badchar')return _err('Invalid character');return _err('Invalid format');}());if(t)return t;} - if(validation&4){t=_acc(function(){if(!arg1)return _par('');var prefix=parseInt(arg1,10);if(isNaN(prefix)||prefix<1||prefix>30)return _err('Prefix must be 1-30');var rv=_ipv4(s);if(rv!=='ok')return(rv==='partial'||rv==='empty')?_par(''):(rv==='badchar'?_err('Invalid character'):_err('Invalid format'));var mB=prefix===0?0:((0xFFFFFFFF<<(32-prefix))>>>0);var ipN=s.split('.').reduce(function(a,o){return(a<<8|+o)>>>0;},0);return((ipN&(~mB>>>0))!==0)?_err('Host bits must be zero'):_ok();}());if(t)return t;} - if(validation&8){t=_acc(function(){var rv=_ipv4(s);if(rv!=='ok')return(rv==='partial'||rv==='empty')?_par(''):(rv==='badchar'?_err('Invalid character'):_err('Invalid format'));if(!arg1||!arg2)return _par('');var prefix=parseInt(arg1,10);if(isNaN(prefix)||prefix<1||prefix>30)return _par('');if(_ipv4(arg2)!=='ok')return _par('');var mB=prefix===0?0:((0xFFFFFFFF<<(32-prefix))>>>0);var snN=arg2.split('.').reduce(function(a,o){return(a<<8|+o)>>>0;},0);if((snN&(~mB>>>0))!==0)return _par('');var iPts=s.split('.').map(Number),sPts=arg2.split('.').map(Number);var ipN=((iPts[0]<<24)|(iPts[1]<<16)|(iPts[2]<<8)|iPts[3])>>>0,sN=((sPts[0]<<24)|(sPts[1]<<16)|(sPts[2]<<8)|sPts[3])>>>0;if((ipN&mB)!==(sN&mB))return _err('IP not in VLAN subnet');var hM=(~mB)>>>0,netN=(sN&mB)>>>0;if(ipN===netN)return _err('Network address not allowed');if(ipN===(netN|hM)>>>0)return _err('Broadcast address not allowed');return _ok();}());if(t)return t;} + var fmtMask=validation&3; + if(fmtMask){ + var fmtPassed=false; + if(fmtMask&1){t=_acc(function(){var rv=_ipv4(s);if(rv==='ok')return _ok();if(rv==='partial'||rv==='empty')return _par('');if(rv==='badchar')return _err('Invalid character');if(rv==='badrange')return _err('Octet out of range');return _err('Invalid format');}());if(t)fmtPassed=true;} + if(!fmtPassed&&(fmtMask&2)){t=_acc(function(){var rv=_ipv6(s);if(rv==='ok')return _ok();if(rv==='partial'||rv==='empty')return _par('');if(rv==='badchar')return _err('Invalid character');return _err('Invalid format');}());if(t)fmtPassed=true;} + if(!fmtPassed)return anyPartial?_par(''):_err(firstMsg||'Invalid format'); + if(!(validation&~3))return _ok(); + anyPartial=false;firstMsg=''; + } + if(validation&4){t=_acc(function(){if(!arg1)return _par('');var prefix=parseInt(arg1,10);if(isNaN(prefix)||prefix<1||prefix>30)return _err('Prefix must be 1-30');if(!fmtMask){var rv=_ipv4(s);if(rv!=='ok')return(rv==='partial'||rv==='empty')?_par(''):(rv==='badchar'?_err('Invalid character'):_err('Invalid format'));}var mB=prefix===0?0:((0xFFFFFFFF<<(32-prefix))>>>0);var ipN=s.split('.').reduce(function(a,o){return(a<<8|+o)>>>0;},0);return((ipN&(~mB>>>0))!==0)?_err('Host bits must be zero'):_ok();}());if(t)return t;} + if(validation&8){t=_acc(function(){if(!fmtMask){var rv=_ipv4(s);if(rv!=='ok')return(rv==='partial'||rv==='empty')?_par(''):(rv==='badchar'?_err('Invalid character'):_err('Invalid format'));}if(!arg1||!arg2)return _par('');var prefix=parseInt(arg1,10);if(isNaN(prefix)||prefix<1||prefix>30)return _par('');if(_ipv4(arg2)!=='ok')return _par('');var mB=prefix===0?0:((0xFFFFFFFF<<(32-prefix))>>>0);var snN=arg2.split('.').reduce(function(a,o){return(a<<8|+o)>>>0;},0);if((snN&(~mB>>>0))!==0)return _par('');var iPts=s.split('.').map(Number),sPts=arg2.split('.').map(Number);var ipN=((iPts[0]<<24)|(iPts[1]<<16)|(iPts[2]<<8)|iPts[3])>>>0,sN=((sPts[0]<<24)|(sPts[1]<<16)|(sPts[2]<<8)|sPts[3])>>>0;if((ipN&mB)!==(sN&mB))return _err('IP not in VLAN subnet');var hM=(~mB)>>>0,netN=(sN&mB)>>>0;if(ipN===netN)return _err('Network address not allowed');if(ipN===(netN|hM)>>>0)return _err('Broadcast address not allowed');return _ok();}());if(t)return t;} if(validation&16){t=_acc(function(){if(!s)return _par('');if(/[^0-9a-fA-F:]/.test(s))return _err('Invalid character');if(/::/.test(s))return _err('Invalid format');var g=s.split(':');if(g.length>6)return _err('Too many groups');for(var i=0;i2)return _err('Each group must be exactly 2 hex characters');}return(g.length===6&&g.every(function(x){return x.length===2;}))?_ok():_par('');}());if(t)return t;} if(validation&32){t=_acc(function(){if(!s)return _par('');if(/[^A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]/.test(s))return _err('Invalid character');var sl=s.toLowerCase();if('https://'.startsWith(sl)||'http://'.startsWith(sl))return _par('');var sep=sl.indexOf('://');if(sep===-1)return _err('Invalid URL format');var scheme=sl.slice(0,sep);if(scheme!=='http'&&scheme!=='https')return _err('Invalid URL format');var after=s.slice(sep+3);if(!after)return _par('');var he=after.search(/[/:?#]/),host=he===-1?after:after.slice(0,he),rest=he===-1?'':after.slice(he);if(!host)return _par('');if(/\.\./.test(host)||host[0]==='.'||host[host.length-1]==='.')return _err('Invalid URL format');var lb=host.split('.');for(var i=0;i65535)return _err('Invalid URL format');}return _ok();}());if(t)return t;} if(validation&64){t=_acc(function(){if(!s)return _par('');if(/[^0-9]/.test(s))return _err('Digits only');var n=parseInt(s,10);return(n>=1&&n<=65535)?_ok():_err('Must be between 1 and 65535');}());if(t)return t;} diff --git a/docker/routlin-dash/app/pages/dhcp/content.json b/docker/routlin-dash/app/pages/dhcp/content.json index 837f31d..9bdef24 100644 --- a/docker/routlin-dash/app/pages/dhcp/content.json +++ b/docker/routlin-dash/app/pages/dhcp/content.json @@ -118,7 +118,7 @@ { "col": "ip", "input_type": "text", - "validate": "VALIDATION_ADDRESS", + "validate": "VALIDATION_IPV4_FORMAT|VALIDATION_ADDRESS", "attrs": { "data-vlan-subnets": "%VLAN_SUBNET_INFO_JSON%" } diff --git a/docker/routlin-dash/app/pages/networklayout/content.json b/docker/routlin-dash/app/pages/networklayout/content.json index 8377a14..98f52e9 100644 --- a/docker/routlin-dash/app/pages/networklayout/content.json +++ b/docker/routlin-dash/app/pages/networklayout/content.json @@ -181,7 +181,7 @@ { "label": "IP Address", "name": "ip", - "validate": "VALIDATION_ADDRESS", + "validate": "VALIDATION_IPV4_FORMAT|VALIDATION_ADDRESS", "attrs": { "data-dep-subnet": "[name='subnet']", "data-dep-mask": ".subnet-prefix-input" @@ -220,7 +220,7 @@ "label": "DNS Server(s)", "name": "dns_servers", "override_name": "dns_servers_override", - "validate": "VALIDATION_ADDRESS", + "validate": "VALIDATION_IPV4_FORMAT|VALIDATION_ADDRESS", "hint": "DNS server(s) advertised to clients via DHCP." }, { @@ -228,7 +228,7 @@ "label": "NTP Server(s)", "name": "ntp_servers", "override_name": "ntp_servers_override", - "validate": "VALIDATION_ADDRESS", + "validate": "VALIDATION_IPV4_FORMAT", "hint": "NTP server(s) advertised to clients via DHCP." }, { @@ -254,7 +254,7 @@ "label": "DHCP Dynamic Pool Start", "name": "dhcp_pool_start", "input_type": "text", - "validate": "VALIDATION_ADDRESS", + "validate": "VALIDATION_IPV4_FORMAT|VALIDATION_ADDRESS", "attrs": { "data-dep-subnet": "[name='subnet']", "data-dep-mask": ".subnet-prefix-input" @@ -265,7 +265,7 @@ "label": "DHCP Dynamic Pool End", "name": "dhcp_pool_end", "input_type": "text", - "validate": "VALIDATION_ADDRESS", + "validate": "VALIDATION_IPV4_FORMAT|VALIDATION_ADDRESS", "attrs": { "data-dep-subnet": "[name='subnet']", "data-dep-mask": ".subnet-prefix-input" diff --git a/docker/routlin-dash/app/pages/portwrangling/content.json b/docker/routlin-dash/app/pages/portwrangling/content.json index 306fe2a..8eba17b 100644 --- a/docker/routlin-dash/app/pages/portwrangling/content.json +++ b/docker/routlin-dash/app/pages/portwrangling/content.json @@ -83,7 +83,7 @@ { "col": "redirect_to", "input_type": "text", - "validate": "VALIDATION_ADDRESS", + "validate": "VALIDATION_IPV4_FORMAT|VALIDATION_ADDRESS", "attrs": { "data-vlan-subnets": "%VLAN_SUBNET_INFO_JSON%" }