Repository: louislam/uptime-kuma
File: server/notification-providers/teltonika.js (JavaScript)

Code before:

        let passedUrl = "";
        try {
            passedUrl = new URL(notification.teltonikaUrl);
        } catch (error) {
            throw Error("Invalid URL: " + notification.teltonikaUrl);
        }

        const baseUrl = passedUrl.origin;
        const loginUrl = baseUrl + "/api/login";
        const smsUrl = baseUrl + "/api/messages/actions/send";

        // Performing input validation and cleanup for the other fields.

        // According to Teltonika's UI, a valid username is:
        //   A string of lowercase Latin letters, numbers, -, . and _ characters is accepted.
        //   First character must be a lowercase Latin letter. Length between 1 and 32 characters.
        const userRegex = /^[a-z][a-zA-Z0-9-._]{0,31}$/;
        const cleanUser = userRegex.exec(notification.teltonikaUsername);
        if (!cleanUser) {
            throw Error("Username is empty.");
        }

        // According to Teltonika's UI, a valid username is:
        //   Min length is 15, max is 256. Must contain digit, uppercase letter, special symbol.
        const passRegex = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!@#$%^&*()_=+{}|;:,<.>?[\]\/\\\\]).{15,256}$/;
        const cleanPass = passRegex.exec(notification.teltonikaPassword);
        if (!cleanPass) {
            throw Error("Password is empty, or does not follow Teltonika requirements.");
        }

        const modemRegex = /[1-9][0-9]?-[1-9][0-9]?/; // matches 1-1, 10-1, 10-10, etc.
        const cleanModem = modemRegex.exec(notification.teltonikaModem);
        if (!cleanModem) {
            throw Error("Modem is empty.");
        }

        const phoneRegex = /^\+(?:[0-9] ?){6,14}[0-9]$/; // regex for ITU-T E.123 international phone number
        const cleanPhoneNumber = phoneRegex.exec(notification.teltonikaPhoneNumber);
        if (!cleanPhoneNumber) {
            throw Error("Phone number is empty.");
        }

        // Teltonika SMS gateway supports a max of 160 chars for its messages.
        const msgRegex = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!@#$%^&*()_=+{}|;:,<.>?[\]\/\\\\]).{1,159}$/;
        const cleanMsg = msgRegex.exec(msg);
        if (!cleanMsg) {
            throw Error("Message is empty.");
        }

        // Starting communications with the API from here on out.

Review comment (refactor):
I have no clue if this is correct and I don't think we need to validate this password like this.

they will tell us that the password does not match, which is the same information.
Lets remove this check entirely

Code after:

        let passedUrl = "";
        try {
            passedUrl = new URL(notification.teltonikaUrl);
        } catch (error) {
            throw Error("Invalid URL: " + notification.teltonikaUrl);
        }

        const baseUrl = passedUrl.origin;
        const loginUrl = baseUrl + "/api/login";
        const smsUrl = baseUrl + "/api/messages/actions/send";

        // Teltonika SMS gateway supports a max of 160 chars for its messages.
        const cleanMsg = msg.substring(0, 159);

        // Starting communications with the API from here on out.
        try {
            let axiosConfig = {
                headers: {
                    "Content-Type": "application/json",
                    "cache-control": "no-cache",
                    Accept: "application/json",
                },
            };

            // In many cases, Teltonika routers will be setup using a self-signed
            // certificate. Here we give them an option to disable certificate
            // validation. It's not desirable, but sometimes the only option.
            if (notification.teltonikaUnsafeTls) {
                axiosConfig.httpsAgent = new https.Agent({
                    rejectUnauthorized: false, // Danger! Disables SSL verification
                });
            }

            axiosConfig = this.getAxiosConfigWithProxy(axiosConfig);

            // Logging in, to get an access token.
            // API reference https://developers.teltonika-networks.com/reference/rut241/7.19.4/v1.11.1/authentication
            // Teltonika's API access tokens expire in 5 minutes, so we always get a new one.
            let loginData = {
                username: notification.teltonikaUsername,
                password: notification.teltonikaPassword,
            };

            let loginResp = await axios.post(loginUrl, loginData, axiosConfig);

            if (loginResp.data.success !== true) {
                throw Error("Login failed: " + loginResp.data.errors.error);
            }

            // Sending the SMS.