Functional Weave
Code in TypeScript

net.device-validate@1.0.0

README.md

2,654 bytes · view raw

# net.device-validate

Checks one entry of a network inventory file (the list of devices a monitor
probes) and reports every problem with it at once, instead of the first one.
An entry looks like this:

```json
{ "name": "core-switch", "host": "10.0.0.2", "ports": [22, 443], "mac": "AA-BB-CC-DD-EE-FF", "subnet": "10.0.0.0/24" }
```

`name`, `host` and `ports` are required; `mac` and `subnet` are optional (null
is the same as leaving them out). The function never throws: a bad entry is a
result with `valid: false` and a list of `errors`, so a monitor can report it
and carry on with the rest of the file.

## What each field must be

| field | rule | built on |
|-------|------|----------|
| `name` | text, not blank; ASCII whitespace around it is trimmed | |
| `host` | an IPv4 address, an IPv6 address or an RFC 1123 host name, tried in that order | `net.ipv4`, `net.ipv6`, `net.hostname-validate` |
| `ports` | a non-empty list of whole numbers 1-65535, each once | `net.port` |
| `mac` | an EUI-48 MAC in any of the forms `net.mac-address` accepts | `net.mac-address` |
| `subnet` | an IPv4 or IPv6 CIDR block | `net.cidr` |

Anything else in the entry is reported as `unknown field "x"`, which catches
typos such as `"port"` for `"ports"`. Unknown fields are listed in sorted
order after the field errors, because JavaScript orders integer-like keys
first and "file order" would differ between languages.

## The normal forms returned

- `host`: an IPv4 address as given (it can only be valid in one spelling),
  IPv6 in RFC 5952 canonical form, a host name lower-cased without its
  trailing dot. `hostKind` says which of `ipv4`, `ipv6` or `hostname` it is.
- `ports`: the valid ports in order, a repeated one only once. `22.0` is port
  22, since a JSON reader may store it as a float.
- `mac`: lower-case, colon-separated.
- `subnet`: host bits cleared (`10.0.0.77/24` is `10.0.0.0/24`), the address
  of an IPv6 block in RFC 5952 form.

Fields that fail are null (or left out of `ports`) in the result, while the
rest are still normalised, so a report can name the device whose MAC is bad.

## Edge cases

- `192.168.1.256` and `010.0.0.1` are not IPv4 addresses (out of range, and a
  leading zero that some parsers read as octal), and they are not host names
  either: a top-level label may not be all digits (RFC 3696 section 2). A
  naive check lets them through as names.
- A trailing newline is not trimmed from `host`; it makes the entry invalid.
- Subnet membership is not checked here: a host name has no address until it
  is resolved, which is I/O. Check it afterwards with `net.cidr`'s
  `cidrContains` on the resolved address.