Repository: louislam/uptime-kuma
File: server/monitor-types/snmp.js (JavaScript)

Code before:

            // Resolve the net-snmp version identifier from monitor.snmpVersion using the `getKey` method (see utils.ts).
            // If the specified version exists, use it; otherwise, default to SNMP version 2c.
            version: getKey(snmp.Version, monitor.snmpVersion) || snmp.Version2c,
        };

        try {
            const session = snmp.createSession(monitor.hostname, monitor.snmpCommunityString, options);

            // Handle errors during session creation
            session.on("error", (error) => {
                heartbeat.status = DOWN;
                heartbeat.msg = `SNMP: Error creating SNMP session: ${error.message}`;
                log.debug("monitor", heartbeat.msg);
            });

            const varbinds = await new Promise((resolve, reject) => {
                session.get([ monitor.snmpOid ], (error, varbinds) => {
                    if (error) {
                        reject(error);
                    } else {
                        log.debug("monitor", `SNMP: Received varbinds (Type=${getKey(snmp.ObjectType, varbinds[0].type)}): ${varbinds[0].value}`);
                        resolve(varbinds);
                    }
                });
            });

            // Verify if any varbinds were returned from the SNMP session or if the varbind type indicates a non-existent instance.
            // The `getKey` method retrieves the key associated with the varbind type from the snmp.ObjectType object.
            if (varbinds.length === 0 || getKey(snmp.ObjectType, varbinds[0].type) === "NoSuchInstance") {
                throw new Error(`No varbinds returned from SNMP session (OID: ${monitor.snmpOid})`);

            // Varbinds succesfully returned
            } else {

                const value = varbinds[0].value;

                // Check if inputs are numeric. If not, re-parse as strings. This ensures comparisons are handled correctly.
                let snmpValue = isNaN(value) ? value.toString() : parseFloat(value);
                let snmpControlValue = isNaN(monitor.snmpControlValue) ? monitor.snmpControlValue.toString() : parseFloat(monitor.snmpControlValue);

                switch (monitor.snmpCondition) {
                    case ">":
                        heartbeat.status = snmpValue > snmpControlValue ? UP : DOWN;
                        break;
                    case ">=":
                        heartbeat.status = snmpValue >= snmpControlValue ? UP : DOWN;
                        break;
                    case "<":
                        heartbeat.status = snmpValue < snmpControlValue ? UP : DOWN;
                        break;

Review comment (bug):
we always print this log in the resolution case (=in the case where the await does not throw)
=> lets make this apparent

```suggestion
                        resolve(varbinds);
                    }
                });
            });
            log.debug("monitor", `SNMP: Received varbinds (Type=${getKey(snmp.ObjectType, varbinds[0].type)}): ${varbinds[0].value}`);
```

Code after:
                timeout: monitor.timeout * 1000,
                version: snmp.Version[monitor.snmpVersion],
            };
            session = snmp.createSession(monitor.hostname, monitor.radiusPassword, sessionOptions);

            // Handle errors during session creation
            session.on("error", (error) => {
                throw new Error(`Error creating SNMP session: ${error.message}`);
            });

            const varbinds = await new Promise((resolve, reject) => {
                session.get([ monitor.snmpOid ], (error, varbinds) => {
                    error ? reject(error) : resolve(varbinds);
                });
            });
            log.debug("monitor", `SNMP: Received varbinds (Type: ${snmp.ObjectType[varbinds[0].type]} Value: ${varbinds[0].value})`);

            if (varbinds.length === 0) {
                throw new Error(`No varbinds returned from SNMP session (OID: ${monitor.snmpOid})`);
            }

            if (varbinds[0].type === snmp.ObjectType.NoSuchInstance) {
                throw new Error(`The SNMP query returned that no instance exists for OID ${monitor.snmpOid}`);
            }

            // We restrict querying to one OID per monitor, therefore `varbinds[0]` will always contain the value we're interested in.
            const value = varbinds[0].value;

            const { status, response } = await evaluateJsonQuery(value, monitor.jsonPath, monitor.jsonPathOperator, monitor.expectedValue);

            if (status) {
                heartbeat.status = UP;
                heartbeat.msg = `JSON query passes (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})`;
            } else {
                throw new Error(`JSON query does not pass (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})`);
            }
        } finally {
            if (session) {
                session.close();
            }
        }
    }
}

module.exports = {
    SNMPMonitorType,
};