136 lines
3.5 KiB
JavaScript
136 lines
3.5 KiB
JavaScript
// server
|
|
// /app/utils.js
|
|
const validator = require("validator")
|
|
const createDebug = require("debug")
|
|
const crypto = require("crypto")
|
|
|
|
const debug = createDebug("webssh2:utils")
|
|
|
|
/**
|
|
* Deep merges two objects
|
|
* @param {Object} target - The target object to merge into
|
|
* @param {Object} source - The source object to merge from
|
|
* @returns {Object} The merged object
|
|
*/
|
|
function deepMerge(target, source) {
|
|
const output = Object.assign({}, target) // Avoid mutating target directly
|
|
Object.keys(source).forEach(key => {
|
|
if (Object.hasOwnProperty.call(source, key)) {
|
|
if (
|
|
source[key] instanceof Object &&
|
|
!Array.isArray(source[key]) &&
|
|
source[key] !== null
|
|
) {
|
|
output[key] = deepMerge(output[key] || {}, source[key])
|
|
} else {
|
|
output[key] = source[key]
|
|
}
|
|
}
|
|
})
|
|
return output
|
|
}
|
|
|
|
/**
|
|
* Generates a secure random session secret
|
|
* @returns {string} A random 32-byte hex string
|
|
*/
|
|
function generateSecureSecret() {
|
|
return crypto.randomBytes(32).toString("hex")
|
|
}
|
|
|
|
/**
|
|
* Determines if a given host is an IP address or a hostname.
|
|
* If it's a hostname, it escapes it for safety.
|
|
*
|
|
* @param {string} host - The host string to validate and escape.
|
|
* @returns {string} - The original IP or escaped hostname.
|
|
*/
|
|
function getValidatedHost(host) {
|
|
let validatedHost
|
|
|
|
if (validator.isIP(host)) {
|
|
validatedHost = host
|
|
} else {
|
|
validatedHost = validator.escape(host)
|
|
}
|
|
|
|
return validatedHost
|
|
}
|
|
|
|
/**
|
|
* Validates and sanitizes a port value.
|
|
* If no port is provided, defaults to port 22.
|
|
* If a port is provided, checks if it is a valid port number (1-65535).
|
|
* If the port is invalid, defaults to port 22.
|
|
*
|
|
* @param {string} [portInput] - The port string to validate and parse.
|
|
* @returns {number} - The validated port number.
|
|
*/
|
|
function getValidatedPort(portInput) {
|
|
const defaultPort = 22
|
|
const port = defaultPort
|
|
debug("getValidatedPort: input: %O", portInput)
|
|
|
|
if (portInput) {
|
|
if (validator.isInt(portInput, { min: 1, max: 65535 })) {
|
|
return parseInt(portInput, 10)
|
|
}
|
|
}
|
|
debug(
|
|
"getValidatedPort: port not specified or is invalid, setting port to: %O",
|
|
port
|
|
)
|
|
|
|
return port
|
|
}
|
|
|
|
/**
|
|
* Checks if the provided credentials object is valid.
|
|
*
|
|
* @param {Object} creds - The credentials object.
|
|
* @param {string} creds.username - The username.
|
|
* @param {string} creds.password - The password.
|
|
* @param {string} creds.host - The host.
|
|
* @param {number} creds.port - The port.
|
|
* @returns {boolean} - Returns true if the credentials are valid, otherwise false.
|
|
*/
|
|
function isValidCredentials(creds) {
|
|
return (
|
|
creds &&
|
|
typeof creds.username === "string" &&
|
|
typeof creds.password === "string" &&
|
|
typeof creds.host === "string" &&
|
|
typeof creds.port === "number"
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Validates and sanitizes the SSH terminal name using validator functions.
|
|
* Allows alphanumeric characters, hyphens, and periods.
|
|
* Returns null if the terminal name is invalid or not provided.
|
|
*
|
|
* @param {string} [term] - The terminal name to validate.
|
|
* @returns {string|null} - The sanitized terminal name if valid, null otherwise.
|
|
*/
|
|
function validateSshTerm(term) {
|
|
debug(`validateSshTerm: %O`, term)
|
|
|
|
if (!term) {
|
|
return null
|
|
}
|
|
|
|
const validatedSshTerm =
|
|
validator.isLength(term, { min: 1, max: 30 }) &&
|
|
validator.matches(term, /^[a-zA-Z0-9.-]+$/)
|
|
|
|
return validatedSshTerm ? term : null
|
|
}
|
|
|
|
module.exports = {
|
|
deepMerge,
|
|
generateSecureSecret,
|
|
getValidatedHost,
|
|
getValidatedPort,
|
|
isValidCredentials,
|
|
validateSshTerm
|
|
}
|