@aikidosec/firewall
Version:
Zen by Aikido is an embedded Web Application Firewall that autonomously protects Node.js apps against common and critical attacks
190 lines (189 loc) • 5.7 kB
JavaScript
"use strict";
// Based on https://github.com/demskie/netparser
// MIT License - Copyright (c) 2019 alex
Object.defineProperty(exports, "__esModule", { value: true });
exports.network = network;
exports.v4AddrToBytes = v4AddrToBytes;
exports.v6AddrToBytes = v6AddrToBytes;
function network(s) {
s = s.trim();
const parts = s.split("/");
if (parts.length === 0 || parts.length > 2)
return null;
const isIPv4 = looksLikeIPv4(s);
if (isIPv4 === null) {
return null;
}
let cidr = isIPv4 ? 32 : 128;
if (parts.length === 2) {
const x = parseIntRange(parts[1], 0, cidr);
if (x === null) {
return null;
}
cidr = x;
}
const bytes = isIPv4 ? v4AddrToBytes(parts[0]) : v6AddrToBytes(parts[0]);
if (bytes === null) {
return null;
}
return { bytes, cidr };
}
function looksLikeIPv4(s) {
for (const c of s) {
if (c === ".")
return true;
if (c === ":")
return false;
}
return null;
}
function parseIntRange(old, min, max) {
let s = "";
for (let i = 0; i < old.length; i++) {
if (Number.isNaN(parseInt(old[i], 10)))
break;
s += old[i];
}
const x = parseInt(s, 10);
if (x >= min && x <= max)
return x;
return null;
}
function v4AddrToBytes(old) {
const bytes = new Array(4);
const parts = old.split(".");
if (parts.length === 4) {
for (let i = 0; i < parts.length; i++) {
const x = parseInt(parts[i], 10);
if (x >= 0 && x <= 255) {
bytes[i] = x;
}
else {
return null;
}
}
return bytes;
}
return null;
}
/*
https://tools.ietf.org/html/rfc4291
https://tools.ietf.org/html/rfc5952#section-4
https://tools.ietf.org/html/rfc3986
ffff:fc00::1:1234/64
[fde4:3510:269e:ffbd::]
[2001:db8::1]:80
2001:db8::1:80 // not valid!
2001:db8::1.80
2001:db8::1 port 80
2001:db8::1p80
2001:db8::1#80
*/
function v6AddrToBytes(s) {
const bytes = new Array(16).fill(0);
if (s.length === 0)
return null;
s = removeBrackets(s);
if (s === "::")
return bytes;
const halves = s.split("::");
if (halves.length === 0 || halves.length > 2)
return null;
const leftByteIndex = parseLeftHalf(bytes, halves[0]);
if (leftByteIndex === null)
return null;
if (halves.length === 2) {
const rightByteIndex = parseRightHalf(bytes, halves[1], leftByteIndex);
if (rightByteIndex === null)
return null;
}
return bytes;
}
function removeBrackets(s) {
if (s.startsWith("[")) {
for (let i = s.length - 1; i >= 0; i--) {
if (s[i] === "]") {
return s.substring(1, i);
}
}
}
return s;
}
function parseHextet(s) {
if (s.trim().length < 1 || s.trim().length > 4)
return Number.NaN;
let val = 0;
for (let i = 0; i < s.length; i++) {
const x = parseInt(s[i], 16);
if (Number.isNaN(x))
return x;
val += x * Math.pow(2, 4 * (s.length - i - 1));
}
return val;
}
function parseLeftHalf(bytes, leftHalf) {
let leftByteIndex = 0;
if (leftHalf !== "") {
const leftParts = leftHalf.split(":");
for (let i = 0; i < leftParts.length; i++) {
if (leftByteIndex >= 16)
return null;
const ipv4Parts = leftParts[i].split(".");
if (ipv4Parts.length === 0)
return null;
if (ipv4Parts.length !== 4) {
const x = parseHextet(leftParts[i]);
if (Number.isNaN(x) || x < 0 || x > 65535)
return null;
bytes[leftByteIndex++] = Math.floor(x / 256);
bytes[leftByteIndex++] = Math.floor(x % 256);
}
else {
for (let j = 0; j < ipv4Parts.length; j++) {
const x = Number(ipv4Parts[j]);
if (Number.isNaN(x) || x < 0 || x > 255)
return null;
bytes[leftByteIndex++] = x;
}
}
}
}
return leftByteIndex;
}
function removePortInfo(s) {
return s.replace(/(#|p|\.).*/g, "").trim();
}
function parseRightHalf(bytes, rightHalf, leftByteIndex) {
let rightByteIndex = 15;
if (rightHalf !== "") {
const rightParts = rightHalf.split(":");
for (let i = rightParts.length - 1; i >= 0; i--) {
if (rightParts[i].trim() === "")
return null;
if (leftByteIndex > rightByteIndex)
return null;
const ipv4Parts = rightParts[i].split(".");
if (ipv4Parts.length === 0)
return null;
if (ipv4Parts.length !== 4) {
if (i === rightParts.length - 1) {
rightParts[i] = removePortInfo(rightParts[i]);
}
const x = parseHextet(rightParts[i]);
if (Number.isNaN(x) || x < 0 || x > 65535)
return null;
bytes[rightByteIndex--] = Math.floor(x % 256);
bytes[rightByteIndex--] = Math.floor(x / 256);
}
else {
for (let j = ipv4Parts.length - 1; j >= 0; j--) {
const x = Number(ipv4Parts[j]);
if (Number.isNaN(x) || x < 0 || x > 255)
return null;
bytes[rightByteIndex--] = x;
}
}
}
}
return rightByteIndex;
}