Viewing File: /usr/local/cpanel/share/libraries/cjt2/src/validator/ip-validators.js

/*
# ip-validators.js                                   Copyright 2022 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/

/**
 * This module has a collection of IP Address validators
 *
 * @module ip-validators
 * @requires angular, validator-utils, validate, locale
 */

define([
    "angular",
    "cjt/validator/validator-utils",
    "cjt/util/locale",
    "cjt/util/inet6",
    "cjt/validator/validateDirectiveFactory",
],
function(angular, UTILS, LOCALE, INET6) {
    "use strict";

    var POSITIVE_INTEGER = /^\d+$/;
    var LEADING_ZEROES = /^0+[0-9]+$/;

    var validators = {

        /**
             * Validate an IPv4 Address
             *
             * @method ipv4
             * @param {string} str - A string representing an IP address
             * @return {object} validation result
             */
        ipv4: function(str) {
            var result = UTILS.initializeValidationResult();

            if (str === null || typeof str === "undefined") {
                result.isValid = false;
                result.add("ipv4", LOCALE.maketext("You must specify a valid [asis,IP] address."));
                return result;
            }

            var chunks = str.split(".");

            if (chunks.length !== 4 || chunks[0] === "0") {
                result.isValid = false;
                result.add("ipv4", LOCALE.maketext("You must specify a valid [asis,IP] address."));
                return result;
            }

            for (var i = 0; i < chunks.length; i++) {
                if (!POSITIVE_INTEGER.test(chunks[i])) {
                    result.isValid = false;
                    break;
                }

                if (chunks[i] > 255) {
                    result.isValid = false;
                    break;
                }

                // We need to account for leading zeroes, since those cause issues with BIND
                // Check for leading zeroes and error out if the value is not just a zero
                if (LEADING_ZEROES.test(chunks[i])) {
                    result.isValid = false;
                    break;
                }
            }

            if (!result.isValid) {
                result.add("ipv4", LOCALE.maketext("You must specify a valid [asis,IP] address."));
            }

            return result;
        },

        /**
             * Validate an IPv6 Address
             *
             * @method ipv6
             * @param {string} str - A string representing an IPv6 address
             * @return {object} validation result
             */
        ipv6: function(str) {
            var result = UTILS.initializeValidationResult();
            var check = INET6.isValid(str);

            if (!check) {
                result.isValid = false;
                result.add("ipv6", LOCALE.maketext("You must specify a valid [asis,IP] address."));
                return result;
            }

            return result;
        },

        /**
         * Validate an IPv4 CIDR Range
         *
         * @method cidr4
         * @param {string} str - A string representing an IPv6 address
         * @return {object} validation result
         */
        cidr4: function(str) {
            var cidr = str.split("/");
            var range = cidr[1], address = cidr[0];

            var isOctetValid = function(rule, octet, name, result) {
                if (!rule) {
                    return false;
                } else if ( !Object.prototype.hasOwnProperty.call(rule, name) ) {

                    // octent can be anything
                    return true;
                } else if (rule[name] === 0 && octet !== 0) {
                    result.add("cidr-details", LOCALE.maketext("In an [asis,IP] address like [asis,a.b.c.d], the “[_1]” octet must be the value 0 for this CIDR range.", name));

                    // octet must be 0
                    return false;
                } else if ( rule[name].length && !rule[name].includes(octet)) {
                    result.add("cidr-details", LOCALE.maketext("In an [asis,IP] address like [asis,a.b.c.d], the “[_1]” octet must be one of the values in: [list_or,_2].", name, rule[name]));

                    // octet must be one of the list
                    return false;
                } else if ( rule[name].max ) {
                    if (octet < rule[name].min || octet > rule[name].max) {
                        result.add("cidr-details", LOCALE.maketext("In an [asis,IP] address like [asis,a.b.c.d], the “[_1]” octet must be greater than or equal to “[_2]” and less than or equal to “[_3]”.", name, rule[name].min, rule[name].max));

                        // octet out of the allowed range
                        return false;
                    } else if (octet % rule[name].by !== 0) {
                        result.add("cidr-details", LOCALE.maketext("In an [asis,IP] address like [asis,a.b.c.d], the “[_1]” octet must be evenly divisible by “[_2]”.", name, rule[name].by));

                        // octet not evenly divisible
                        return false;
                    }
                }
                return true;
            };

            var result = this.ipv4(address);
            if (result.isValid) {

                // check the cidr range
                if (range) {
                    if (range < 0 || range > 32) {
                        result.isValid = false;
                        result.add("cidr", LOCALE.maketext("You must specify a valid [asis,CIDR] range between 0 and 32."));
                        return result;
                    }

                    // Precalculate the validation rules
                    // CIDR Format:
                    //
                    //   a.b.c.d/range
                    //
                    // Each record below defines the rules for a specific range
                    // For any octet slot (a,b,c,d) that underfined in the rule,
                    //  any value is allowed.
                    var rules = {
                        32: {},
                        31: { d: { min: 0, max: 254, by: 2 } },
                        30: { d: { min: 0, max: 252, by: 4 } },
                        29: { d: { min: 0, max: 248, by: 8 } },
                        28: { d: { min: 0, max: 240, by: 16 } },
                        27: { d: { min: 0, max: 224, by: 32 } },
                        26: { d: [ 0, 64, 128, 192 ] },
                        25: { d: [ 0, 128 ] },
                        24: { d: 0 },
                        23: { d: 0, c: { min: 0, max: 254, by: 2 } },
                        22: { d: 0, c: { min: 0, max: 252, by: 4 } },
                        21: { d: 0, c: { min: 0, max: 248, by: 8 } },
                        20: { d: 0, c: { min: 0, max: 240, by: 16 } },
                        19: { d: 0, c: { min: 0, max: 224, by: 32 } },
                        18: { d: 0, c: [ 0, 64, 128, 192 ] },
                        17: { d: 0, c: [ 0, 128 ] },
                        16: { d: 0, c: 0 },
                        15: { d: 0, c: 0, b: { min: 0, max: 254, by: 2 } },
                        14: { d: 0, c: 0, b: { min: 0, max: 252, by: 4 } },
                        13: { d: 0, c: 0, b: { min: 0, max: 248, by: 8 } },
                        12: { d: 0, c: 0, b: { min: 0, max: 240, by: 16 } },
                        11: { d: 0, c: 0, b: { min: 0, max: 224, by: 32 } },
                        10: { d: 0, c: 0, b: [ 0, 64, 128, 192 ] },
                        9: { d: 0, c: 0, b: [ 0, 128 ] },
                        8: { d: 0, c: 0, b: 0 },
                        7: { d: 0, c: 0, b: 0, a: { min: 0, max: 254, by: 2 } },
                        6: { d: 0, c: 0, b: 0, a: { min: 0, max: 252, by: 4 } },
                        5: { d: 0, c: 0, b: 0, a: { min: 0, max: 248, by: 8 } },
                        4: { d: 0, c: 0, b: 0, a: { min: 0, max: 240, by: 16 } },
                        3: { d: 0, c: 0, b: 0, a: { min: 0, max: 224, by: 32 } },
                        2: { d: 0, c: 0, b: 0, a: [ 0, 64, 128, 192 ] },
                        1: { d: 0, c: 0, b: 0, a: [ 0, 128 ] },
                        0: { d: 0, c: 0, b: 0, a: 0 },
                    };

                    var octets = address.split(/\./); // a.b.c.d
                    var rule = rules[range];

                    var isValid = ["a", "b", "c", "d"].reduce(function(isValid, name, index) {
                        isValid = isValid && isOctetValid(rule, Number(octets[index]), name, result);
                        return isValid;
                    }, true);

                    if (!isValid) {
                        result.isValid = false;
                        result.add("cidr", LOCALE.maketext("The [asis,IP] address, [_1], in the [asis,CIDR] range is not supported for the range /[_2].", address, range));
                    }
                } else {
                    result.isValid = false;
                    result.add("cidr", LOCALE.maketext("The [asis,CIDR] range must include a ‘/’ followed by the range."));
                }
            }
            return result;
        },
    };

    // Generate a directive for each validation function
    var validatorModule = angular.module("cjt2.validate");
    validatorModule.run(["validatorFactory",
        function(validatorFactory) {
            validatorFactory.generate(validators);
        },
    ]);

    return {
        methods: validators,
        name: "ip-validators",
        description: "Validation library for IP Addresses.",
        version: 2.0,
    };
});
Back to Directory File Manager