generate-reader.uc: implement string format validation

- Generate code for validating formats
 - Remove dead code
 - Fix a wrong schema format discovered by the resulting reader code

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich
2021-05-21 14:49:58 +02:00
parent 505c0e1d1d
commit 73a9ee7834
4 changed files with 202 additions and 29 deletions

View File

@@ -48,6 +48,60 @@ function to_value_descr(path)
let GeneratorProto = {
format_validators: {
"uc-cidr4": [
'let m = match(s, /^(auto|[0-9.]+)\\/([0-9]+)$/);',
'return m ? ((m[1] == "auto" || length(iptoarr(m[1])) == 4) && +m[2] <= 32) : false;'
],
"uc-cidr6": [
'let m = match(s, /^(auto|[0-9a-fA-F:.]+)\\/([0-9]+)$/);',
'return m ? ((m[1] == "auto" || length(iptoarr(m[1])) == 16) && +m[2] <= 128) : false;'
],
"uc-cidr": [
'let m = match(s, /^(auto|[0-9a-fA-F:.]+)\\/([0-9]+)$/);',
'if (!m) return false;',
'let l = (m[1] == "auto") ? 16 : length(iptoarr(m[1]));',
'return (l > 0 && +m[2] <= (l * 8));'
],
"uc-mac": [
'return match(s, /^[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]$/i);'
],
"uc-host": [
'if (length(iptoarr(s)) != 0) return true;',
'if (length(s) > 255) return false;',
'let labels = split(s, ".");',
'return (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 0);'
],
"uc-timeout": [
'return match(s, /^[0-9]+[smhdw]$/);'
],
"hostname": [
'if (length(s) > 255) return false;',
'let labels = split(s, ".");',
'return (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 0);'
],
"fqdn": [
'if (length(s) > 255) return false;',
'let labels = split(s, ".");',
'return (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 1);'
],
"ipv4": [
'return (length(iptoarr(s)) == 4);'
],
"ipv6": [
'return (length(iptoarr(s)) == 16);'
],
"uri": [
'if (index(s, "data:") == 0) return true;',
'let m = match(s, /^[a-z+-]+:\\/\\/([^\\/]+).*$/);',
'if (!m) return false;',
'if (length(iptoarr(m[1])) != 0) return true;',
'if (length(m[1]) > 255) return false;',
'let labels = split(m[1], ".");',
'return (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 0);'
]
},
is_ref: function(value)
{
return (
@@ -93,6 +147,29 @@ let GeneratorProto = {
return ref;
},
emit_format_asserts: function(path, valueExpr, valueSpec)
{
if (!valueSpec.format)
return;
if (!exists(this.format_validators, valueSpec.format)) {
warn("Unrecognized string format '" + valueSpec.format + '".\n');
return;
}
let code = this.format_validators[valueSpec.format];
this.print(path, 'assert((s => {');
for (let line in code)
this.print(path, ' ' + line);
this.print(path, '})(%s), "%s has invalid format: " + %s);',
valueExpr,
to_value_descr(path),
valueExpr);
},
emit_generic_asserts: function(path, valueExpr, valueSpec)
{
if (type(valueSpec.enum) == 'array' && length(valueSpec.enum) > 0)
@@ -185,33 +262,6 @@ let GeneratorProto = {
}
},
emit_constraints: function(propertyName, valueSpec)
{
this.emit_generic_asserts(propertyName, valueSpec);
switch (valueSpec.type) {
case 'integer':
this.emit_number_asserts(propertyName, valueSpec, true);
break;
case 'number':
this.emit_number_asserts(propertyName, valueSpec, false);
break;
case 'string':
this.emit_string_asserts(propertyName, valueSpec);
break;
case 'array':
this.emit_array_asserts(propertyName, valueSpec);
break;
case 'object':
this.emit_object_asserts(propertyName, valueSpec);
break;
}
},
read_schema: function(path)
{
let fd = fs.open(path, "r");
@@ -271,6 +321,7 @@ let GeneratorProto = {
}
this.emit_generic_asserts(path, valueExpr, valueSpec);
this.emit_format_asserts(path, valueExpr, valueSpec);
let variantSpecs, variantSuccessCond;

View File

@@ -25,7 +25,7 @@ properties:
description:
This option defines the static IPv4 gateway of the logical interface.
type: string
format: uc-cidr4
format: ipv4
examples:
- 192.168.1.1
send-hostname:

View File

@@ -32,11 +32,19 @@ function instantiateGlobals(value) {
if (exists(value, "ipv4-network")) {
assert(type(value["ipv4-network"]) == "string", "Property globals.ipv4-network must be of type string");
assert((s => {
let m = match(s, /^(auto|[0-9.]+)\/([0-9]+)$/);
return m ? ((m[1] == "auto" || length(iptoarr(m[1])) == 4) && +m[2] <= 32) : false;
})(value["ipv4-network"]), "Property globals.ipv4-network has invalid format: " + value["ipv4-network"]);
obj.ipv4_network = value["ipv4-network"];
}
if (exists(value, "ipv6-network")) {
assert(type(value["ipv6-network"]) == "string", "Property globals.ipv6-network must be of type string");
assert((s => {
let m = match(s, /^(auto|[0-9a-fA-F:.]+)\/([0-9]+)$/);
return m ? ((m[1] == "auto" || length(iptoarr(m[1])) == 16) && +m[2] <= 128) : false;
})(value["ipv6-network"]), "Property globals.ipv6-network has invalid format: " + value["ipv6-network"]);
obj.ipv6_network = value["ipv6-network"];
}
@@ -329,6 +337,9 @@ function instantiateInterfaceEthernet(value) {
if (exists(value, "macaddr")) {
assert(type(value["macaddr"]) == "string", "Property interface.ethernet.macaddr must be of type string");
assert((s => {
return match(s, /^[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]$/i);
})(value["macaddr"]), "Property interface.ethernet.macaddr has invalid format: " + value["macaddr"]);
obj.macaddr = value["macaddr"];
}
@@ -360,6 +371,9 @@ function instantiateInterfaceIpv4Dhcp(value) {
if (exists(value, "lease-time")) {
assert(type(value["lease-time"]) == "string", "Property interface.ipv4.dhcp.lease-time must be of type string");
assert((s => {
return match(s, /^[0-9]+[smhdw]$/);
})(value["lease-time"]), "Property interface.ipv4.dhcp.lease-time has invalid format: " + value["lease-time"]);
obj.lease_time = value["lease-time"];
}
else {
@@ -376,6 +390,9 @@ function instantiateInterfaceIpv4DhcpLease(value) {
if (exists(value, "macaddr")) {
assert(type(value["macaddr"]) == "string", "Property interface.ipv4.dhcp-lease.macaddr must be of type string");
assert((s => {
return match(s, /^[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]$/i);
})(value["macaddr"]), "Property interface.ipv4.dhcp-lease.macaddr has invalid format: " + value["macaddr"]);
obj.macaddr = value["macaddr"];
}
@@ -386,6 +403,9 @@ function instantiateInterfaceIpv4DhcpLease(value) {
if (exists(value, "lease-time")) {
assert(type(value["lease-time"]) == "string", "Property interface.ipv4.dhcp-lease.lease-time must be of type string");
assert((s => {
return match(s, /^[0-9]+[smhdw]$/);
})(value["lease-time"]), "Property interface.ipv4.dhcp-lease.lease-time has invalid format: " + value["lease-time"]);
obj.lease_time = value["lease-time"];
}
else {
@@ -416,11 +436,18 @@ function instantiateInterfaceIpv4(value) {
if (exists(value, "subnet")) {
assert(type(value["subnet"]) == "string", "Property interface.ipv4.subnet must be of type string");
assert((s => {
let m = match(s, /^(auto|[0-9.]+)\/([0-9]+)$/);
return m ? ((m[1] == "auto" || length(iptoarr(m[1])) == 4) && +m[2] <= 32) : false;
})(value["subnet"]), "Property interface.ipv4.subnet has invalid format: " + value["subnet"]);
obj.subnet = value["subnet"];
}
if (exists(value, "gateway")) {
assert(type(value["gateway"]) == "string", "Property interface.ipv4.gateway must be of type string");
assert((s => {
return (length(iptoarr(s)) == 4);
})(value["gateway"]), "Property interface.ipv4.gateway has invalid format: " + value["gateway"]);
obj.gateway = value["gateway"];
}
@@ -437,6 +464,9 @@ function instantiateInterfaceIpv4(value) {
return map(value, (item) => {
assert(type(item) == "string", "Items of interface.ipv4.use-dns must be of type string");
assert((s => {
return (length(iptoarr(s)) == 4);
})(item), "Items of interface.ipv4.use-dns has invalid format: " + item);
return item;
});
}
@@ -478,6 +508,9 @@ function instantiateInterfaceIpv6Dhcpv6(value) {
return map(value, (item) => {
assert(type(item) == "string", "Items of interface.ipv6.dhcpv6.announce-dns must be of type string");
assert((s => {
return (length(iptoarr(s)) == 16);
})(item), "Items of interface.ipv6.dhcpv6.announce-dns has invalid format: " + item);
return item;
});
}
@@ -488,6 +521,10 @@ function instantiateInterfaceIpv6Dhcpv6(value) {
if (exists(value, "filter-prefix")) {
assert(type(value["filter-prefix"]) == "string", "Property interface.ipv6.dhcpv6.filter-prefix must be of type string");
assert((s => {
let m = match(s, /^(auto|[0-9a-fA-F:.]+)\/([0-9]+)$/);
return m ? ((m[1] == "auto" || length(iptoarr(m[1])) == 16) && +m[2] <= 128) : false;
})(value["filter-prefix"]), "Property interface.ipv6.dhcpv6.filter-prefix has invalid format: " + value["filter-prefix"]);
obj.filter_prefix = value["filter-prefix"];
}
else {
@@ -510,11 +547,18 @@ function instantiateInterfaceIpv6(value) {
if (exists(value, "subnet")) {
assert(type(value["subnet"]) == "string", "Property interface.ipv6.subnet must be of type string");
assert((s => {
let m = match(s, /^(auto|[0-9a-fA-F:.]+)\/([0-9]+)$/);
return m ? ((m[1] == "auto" || length(iptoarr(m[1])) == 16) && +m[2] <= 128) : false;
})(value["subnet"]), "Property interface.ipv6.subnet has invalid format: " + value["subnet"]);
obj.subnet = value["subnet"];
}
if (exists(value, "gateway")) {
assert(type(value["gateway"]) == "string", "Property interface.ipv6.gateway must be of type string");
assert((s => {
return (length(iptoarr(s)) == 16);
})(value["gateway"]), "Property interface.ipv6.gateway has invalid format: " + value["gateway"]);
obj.gateway = value["gateway"];
}
@@ -571,6 +615,9 @@ function instantiateInterfaceSsidMultiPsk(value) {
if (exists(value, "mac")) {
assert(type(value["mac"]) == "string", "Property interface.ssid.multi-psk.mac must be of type string");
assert((s => {
return match(s, /^[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]$/i);
})(value["mac"]), "Property interface.ssid.multi-psk.mac has invalid format: " + value["mac"]);
obj.mac = value["mac"];
}
@@ -607,6 +654,11 @@ function instantiateInterfaceSsidCaptive(value) {
if (exists(value, "gateway-fqdn")) {
assert(type(value["gateway-fqdn"]) == "string", "Property interface.ssid.captive.gateway-fqdn must be of type string");
assert((s => {
if (length(s) > 255) return false;
let labels = split(s, ".");
return (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 1);
})(value["gateway-fqdn"]), "Property interface.ssid.captive.gateway-fqdn has invalid format: " + value["gateway-fqdn"]);
obj.gateway_fqdn = value["gateway-fqdn"];
}
else {
@@ -789,6 +841,9 @@ function instantiateInterfaceSsidRadiusLocalUser(value) {
if (exists(value, "mac")) {
assert(type(value["mac"]) == "string", "Property interface.ssid.radius.local-user.mac must be of type string");
assert((s => {
return match(s, /^[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]$/i);
})(value["mac"]), "Property interface.ssid.radius.local-user.mac has invalid format: " + value["mac"]);
obj.mac = value["mac"];
}
@@ -826,6 +881,12 @@ function instantiateInterfaceSsidRadiusServer(value) {
if (exists(value, "host")) {
assert(type(value["host"]) == "string", "Property interface.ssid.radius.server.host must be of type string");
assert((s => {
if (length(iptoarr(s)) != 0) return true;
if (length(s) > 255) return false;
let labels = split(s, ".");
return (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 0);
})(value["host"]), "Property interface.ssid.radius.server.host has invalid format: " + value["host"]);
obj.host = value["host"];
}
@@ -959,6 +1020,15 @@ function instantiateInterfaceSsidPassPoint(value) {
if (exists(value, "venue-url")) {
assert(type(value["venue-url"]) == "string", "Property interface.ssid.pass-point.venue-url must be of type string");
assert((s => {
if (index(s, "data:") == 0) return true;
let m = match(s, /^[a-z+-]+:\/\/([^\/]+).*$/);
if (!m) return false;
if (length(iptoarr(m[1])) != 0) return true;
if (length(m[1]) > 255) return false;
let labels = split(m[1], ".");
return (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 0);
})(value["venue-url"]), "Property interface.ssid.pass-point.venue-url has invalid format: " + value["venue-url"]);
obj.venue_url = value["venue-url"];
}
@@ -975,6 +1045,15 @@ function instantiateInterfaceSsidPassPoint(value) {
if (exists(value, "uri")) {
assert(type(value["uri"]) == "string", "Property interface.ssid.pass-point.auth-type.uri must be of type string");
assert((s => {
if (index(s, "data:") == 0) return true;
let m = match(s, /^[a-z+-]+:\/\/([^\/]+).*$/);
if (!m) return false;
if (length(iptoarr(m[1])) != 0) return true;
if (length(m[1]) > 255) return false;
let labels = split(m[1], ".");
return (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 0);
})(value["uri"]), "Property interface.ssid.pass-point.auth-type.uri has invalid format: " + value["uri"]);
obj.uri = value["uri"];
}
@@ -987,6 +1066,11 @@ function instantiateInterfaceSsidPassPoint(value) {
if (exists(value, "domain-name")) {
assert(type(value["domain-name"]) == "string", "Property interface.ssid.pass-point.domain-name must be of type string");
assert((s => {
if (length(s) > 255) return false;
let labels = split(s, ".");
return (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 0);
})(value["domain-name"]), "Property interface.ssid.pass-point.domain-name has invalid format: " + value["domain-name"]);
obj.domain_name = value["domain-name"];
}
@@ -1037,6 +1121,15 @@ function instantiateInterfaceSsidPassPoint(value) {
if (exists(value, "uri")) {
assert(type(value["uri"]) == "string", "Property interface.ssid.pass-point.icon.item.uri must be of type string");
assert((s => {
if (index(s, "data:") == 0) return true;
let m = match(s, /^[a-z+-]+:\/\/([^\/]+).*$/);
if (!m) return false;
if (length(iptoarr(m[1])) != 0) return true;
if (length(m[1]) > 255) return false;
let labels = split(m[1], ".");
return (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 0);
})(value["uri"]), "Property interface.ssid.pass-point.icon.item.uri has invalid format: " + value["uri"]);
obj.uri = value["uri"];
}
@@ -1099,6 +1192,9 @@ function instantiateInterfaceSsid(value) {
if (exists(value, "bssid")) {
assert(type(value["bssid"]) == "string", "Property interface.ssid.bssid must be of type string");
assert((s => {
return match(s, /^[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]$/i);
})(value["bssid"]), "Property interface.ssid.bssid has invalid format: " + value["bssid"]);
obj.bssid = value["bssid"];
}
@@ -1232,6 +1328,10 @@ function instantiateInterfaceTunnelVxlan(value) {
if (exists(value, "peer-address")) {
assert(type(value["peer-address"]) == "string", "Property interface.tunnel.vxlan.peer-address must be of type string");
assert((s => {
let m = match(s, /^(auto|[0-9.]+)\/([0-9]+)$/);
return m ? ((m[1] == "auto" || length(iptoarr(m[1])) == 4) && +m[2] <= 32) : false;
})(value["peer-address"]), "Property interface.tunnel.vxlan.peer-address has invalid format: " + value["peer-address"]);
obj.peer_address = value["peer-address"];
}
@@ -1258,6 +1358,10 @@ function instantiateInterfaceTunnelGre(value) {
if (exists(value, "peer-address")) {
assert(type(value["peer-address"]) == "string", "Property interface.tunnel.gre.peer-address must be of type string");
assert((s => {
let m = match(s, /^(auto|[0-9.]+)\/([0-9]+)$/);
return m ? ((m[1] == "auto" || length(iptoarr(m[1])) == 4) && +m[2] <= 32) : false;
})(value["peer-address"]), "Property interface.tunnel.gre.peer-address has invalid format: " + value["peer-address"]);
obj.peer_address = value["peer-address"];
}
@@ -1464,6 +1568,12 @@ function instantiateServiceNtp(value) {
return map(value, (item) => {
assert(type(item) == "string", "Items of service.ntp.servers must be of type string");
assert((s => {
if (length(iptoarr(s)) != 0) return true;
if (length(s) > 255) return false;
let labels = split(s, ".");
return (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 0);
})(item), "Items of service.ntp.servers has invalid format: " + item);
return item;
});
}
@@ -1503,6 +1613,12 @@ function instantiateServiceRtty(value) {
if (exists(value, "host")) {
assert(type(value["host"]) == "string", "Property service.rtty.host must be of type string");
assert((s => {
if (length(iptoarr(s)) != 0) return true;
if (length(s) > 255) return false;
let labels = split(s, ".");
return (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 0);
})(value["host"]), "Property service.rtty.host has invalid format: " + value["host"]);
obj.host = value["host"];
}
@@ -1534,6 +1650,12 @@ function instantiateServiceLog(value) {
if (exists(value, "host")) {
assert(type(value["host"]) == "string", "Property service.log.host must be of type string");
assert((s => {
if (length(iptoarr(s)) != 0) return true;
if (length(s) > 255) return false;
let labels = split(s, ".");
return (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 0);
})(value["host"]), "Property service.log.host has invalid format: " + value["host"]);
obj.host = value["host"];
}

View File

@@ -383,7 +383,7 @@
},
"gateway": {
"type": "string",
"format": "uc-cidr4",
"format": "ipv4",
"examples": [
"192.168.1.1"
]