chore: refactor jsmasker calls

chore: implement constants
This commit is contained in:
Bill Church 2024-08-21 19:16:40 +00:00
parent 47910dd066
commit 2c3a89b5dc
No known key found for this signature in database
7 changed files with 91 additions and 53 deletions

View file

@ -5,25 +5,9 @@ const fs = require("fs")
const path = require("path") const path = require("path")
const { createNamespacedDebug } = require("./logger") const { createNamespacedDebug } = require("./logger")
const { HTTP, MESSAGES, DEFAULTS } = require("./constants") const { HTTP, MESSAGES, DEFAULTS } = require("./constants")
const { modifyHtml } = require("./utils")
const debug = createNamespacedDebug("connectionHandler") const debug = createNamespacedDebug("connectionHandler")
/**
* Modify the HTML content by replacing certain placeholders with dynamic values.
* @param {string} html - The original HTML content.
* @param {Object} config - The configuration object to inject into the HTML.
* @returns {string} - The modified HTML content.
*/
function modifyHtml(html, config) {
const modifiedHtml = html.replace(
/(src|href)="(?!http|\/\/)/g,
'$1="/ssh/assets/'
)
return modifiedHtml.replace(
"window.webssh2Config = null;",
`window.webssh2Config = ${JSON.stringify(config)};`
)
}
/** /**
* Handle reading the file and processing the response. * Handle reading the file and processing the response.

View file

@ -1,7 +1,6 @@
// server // server
// app/constants.js // app/constants.js
const crypto = require("crypto")
const path = require("path") const path = require("path")
/** /**
@ -14,7 +13,9 @@ const MESSAGES = {
CONFIG_ERROR: "CONFIG_ERROR", CONFIG_ERROR: "CONFIG_ERROR",
UNEXPECTED_ERROR: "An unexpected error occurred", UNEXPECTED_ERROR: "An unexpected error occurred",
EXPRESS_APP_CONFIG_ERROR: "Failed to configure Express app", EXPRESS_APP_CONFIG_ERROR: "Failed to configure Express app",
CLIENT_FILE_ERROR: "Error loading client file" CLIENT_FILE_ERROR: "Error loading client file",
FAILED_SESSION_SAVE: "Failed to save session",
CONFIG_VALIDATION_ERROR: "Config validation error"
} }
/** /**
@ -38,20 +39,6 @@ const DEFAULTS = {
CLIENT_FILE: "client.htm" CLIENT_FILE: "client.htm"
} }
/**
* Socket events
*/
const SOCKET_EVENTS = {
CONNECTION: "connection",
DISCONNECT: "disconnect",
AUTHENTICATE: "authenticate",
AUTHENTICATION: "authentication",
TERMINAL: "terminal",
DATA: "data",
RESIZE: "resize",
CONTROL: "control"
}
/** /**
* HTTP Related * HTTP Related
*/ */
@ -66,12 +53,11 @@ const HTTP = {
PATH: "/ssh/host/", PATH: "/ssh/host/",
SAMESITE: "Strict", SAMESITE: "Strict",
SESSION_SID: "webssh2_sid", SESSION_SID: "webssh2_sid",
CREDS_CLEARED: "Credentials cleared." CREDS_CLEARED: "Credentials cleared.",
} }
module.exports = { module.exports = {
MESSAGES, MESSAGES,
DEFAULTS, DEFAULTS,
SOCKET_EVENTS,
HTTP HTTP
} }

View file

@ -3,11 +3,11 @@
const express = require("express") const express = require("express")
const basicAuth = require("basic-auth") const basicAuth = require("basic-auth")
const maskObject = require("jsmasker")
const validator = require("validator") const validator = require("validator")
const { const {
getValidatedHost, getValidatedHost,
getValidatedPort, getValidatedPort,
maskSensitiveData,
validateSshTerm validateSshTerm
} = require("./utils") } = require("./utils")
const handleConnection = require("./connectionHandler") const handleConnection = require("./connectionHandler")
@ -61,7 +61,7 @@ router.get("/host/:host", auth, function(req, res) {
req.session.usedBasicAuth = true req.session.usedBasicAuth = true
// Sanitize and log the sshCredentials object // Sanitize and log the sshCredentials object
const sanitizedCredentials = maskObject( const sanitizedCredentials = maskSensitiveData(
JSON.parse(JSON.stringify(req.session.sshCredentials)) JSON.parse(JSON.stringify(req.session.sshCredentials))
) )
debug("/ssh/host/ Credentials: ", sanitizedCredentials) debug("/ssh/host/ Credentials: ", sanitizedCredentials)

View file

@ -16,7 +16,7 @@ function createServer(app) {
* @param {Error} err - The error object * @param {Error} err - The error object
*/ */
function handleServerError(err) { function handleServerError(err) {
console.error("WebSSH2 server.listen ERROR:", err.code) console.error("HTTP Server ERROR: %O", err)
} }
/** /**

View file

@ -1,14 +1,18 @@
// server // server
// app/socket.js // app/socket.js
const maskObject = require("jsmasker")
const validator = require("validator") const validator = require("validator")
const SSHConnection = require("./ssh") const SSHConnection = require("./ssh")
const { createNamespacedDebug } = require("./logger") const { createNamespacedDebug } = require("./logger")
const { SSHConnectionError, handleError } = require("./errors") const { SSHConnectionError, handleError } = require("./errors")
const debug = createNamespacedDebug("socket") const debug = createNamespacedDebug("socket")
const { validateSshTerm, isValidCredentials } = require("./utils") const {
isValidCredentials,
maskSensitiveData,
validateSshTerm
} = require("./utils")
const { MESSAGES } = require("./constants")
class WebSSH2Socket { class WebSSH2Socket {
constructor(socket, config) { constructor(socket, config) {
@ -38,7 +42,7 @@ class WebSSH2Socket {
const creds = this.socket.handshake.session.sshCredentials const creds = this.socket.handshake.session.sshCredentials
debug( debug(
`handleConnection: ${this.socket.id}, Host: ${creds.host}: HTTP Basic Credentials Exist, creds: %O`, `handleConnection: ${this.socket.id}, Host: ${creds.host}: HTTP Basic Credentials Exist, creds: %O`,
maskObject(creds) maskSensitiveData(creds)
) )
this.handleAuthenticate(creds) this.handleAuthenticate(creds)
} else if (!this.sessionState.authenticated) { } else if (!this.sessionState.authenticated) {
@ -67,7 +71,7 @@ class WebSSH2Socket {
} }
handleAuthenticate(creds) { handleAuthenticate(creds) {
debug(`handleAuthenticate: ${this.socket.id}, %O`, maskObject(creds)) debug(`handleAuthenticate: ${this.socket.id}, %O`, maskSensitiveData(creds))
if (isValidCredentials(creds)) { if (isValidCredentials(creds)) {
this.sessionState.term = validateSshTerm(creds.term) this.sessionState.term = validateSshTerm(creds.term)
@ -86,7 +90,7 @@ class WebSSH2Socket {
initializeConnection(creds) { initializeConnection(creds) {
debug( debug(
`initializeConnection: ${this.socket.id}, INITIALIZING SSH CONNECTION: Host: ${creds.host}, creds: %O`, `initializeConnection: ${this.socket.id}, INITIALIZING SSH CONNECTION: Host: ${creds.host}, creds: %O`,
maskObject(creds) maskSensitiveData(creds)
) )
this.ssh this.ssh
@ -278,7 +282,10 @@ class WebSSH2Socket {
this.socket.handshake.session.save(err => { this.socket.handshake.session.save(err => {
if (err) if (err)
console.error(`Failed to save session for ${this.socket.id}:`, err) console.error(
`clearSessionCredentials: ${MESSAGES.FAILED_SESSION_SAVE} ${this.socket.id}:`,
err
)
}) })
} }
} }

View file

@ -2,9 +2,9 @@
// app/ssh.js // app/ssh.js
const SSH = require("ssh2").Client const SSH = require("ssh2").Client
const maskObject = require("jsmasker")
const { createNamespacedDebug } = require("./logger") const { createNamespacedDebug } = require("./logger")
const { SSHConnectionError, handleError } = require("./errors") const { SSHConnectionError, handleError } = require("./errors")
const { maskSensitiveData } = require("./utils")
const debug = createNamespacedDebug("ssh") const debug = createNamespacedDebug("ssh")
@ -17,7 +17,7 @@ function SSHConnection(config) {
SSHConnection.prototype.connect = function(creds) { SSHConnection.prototype.connect = function(creds) {
const self = this const self = this
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
debug("connect: %O", maskObject(creds)) debug("connect: %O", maskSensitiveData(creds))
if (self.conn) { if (self.conn) {
self.conn.end() self.conn.end()
@ -51,12 +51,10 @@ SSHConnection.prototype.getSSHConfig = function(creds) {
username: creds.username, username: creds.username,
password: creds.password, password: creds.password,
tryKeyboard: true, tryKeyboard: true,
algorithms: creds.algorithms || this.config.ssh.algorithms, algorithms: this.config.ssh.algorithms,
readyTimeout: creds.readyTimeout || this.config.ssh.readyTimeout, readyTimeout: this.config.ssh.readyTimeout,
keepaliveInterval: keepaliveInterval: this.config.ssh.keepaliveInterval,
creds.keepaliveInterval || this.config.ssh.keepaliveInterval, keepaliveCountMax: this.config.ssh.keepaliveCountMax,
keepaliveCountMax:
creds.keepaliveCountMax || this.config.ssh.keepaliveCountMax,
debug: createNamespacedDebug("ssh2") debug: createNamespacedDebug("ssh2")
} }
} }

View file

@ -2,8 +2,11 @@
// /app/utils.js // /app/utils.js
const validator = require("validator") const validator = require("validator")
const crypto = require("crypto") const crypto = require("crypto")
const Ajv = require("ajv")
const maskObject = require("jsmasker")
const { createNamespacedDebug } = require("./logger") const { createNamespacedDebug } = require("./logger")
const { DEFAULTS } = require("./constants") const { DEFAULTS, MESSAGES } = require("./constants")
const { configSchema } = require("./config")
const debug = createNamespacedDebug("utils") const debug = createNamespacedDebug("utils")
@ -127,11 +130,71 @@ function validateSshTerm(term) {
return validatedSshTerm ? term : null return validatedSshTerm ? term : null
} }
/**
* Validates the given configuration object.
*
* @param {Object} config - The configuration object to validate.
* @throws {Error} If the configuration object fails validation.
* @returns {Object} The validated configuration object.
*/
function validateConfig(config) {
const ajv = new Ajv()
const validate = ajv.compile(configSchema)
const valid = validate(config)
if (!valid) {
throw new Error(
`${MESSAGES.CONFIG_VALIDATION_ERROR}: ${ajv.errorsText(validate.errors)}`
)
}
return config
}
/**
* Modify the HTML content by replacing certain placeholders with dynamic values.
* @param {string} html - The original HTML content.
* @param {Object} config - The configuration object to inject into the HTML.
* @returns {string} - The modified HTML content.
*/
function modifyHtml(html, config) {
debug("modifyHtml")
const modifiedHtml = html.replace(
/(src|href)="(?!http|\/\/)/g,
'$1="/ssh/assets/'
)
return modifiedHtml.replace(
"window.webssh2Config = null;",
`window.webssh2Config = ${JSON.stringify(config)};`
)
}
/**
* Masks sensitive information in an object
* @param {Object} obj - The object to mask
* @param {Object} [options] - Optional configuration for masking
* @returns {Object} The masked object
*/
function maskSensitiveData(obj, options) {
const defaultOptions = {
// Add any default masking options here
// For example:
// password: true,
// token: true
}
const maskingOptions = Object.assign({}, defaultOptions, options || {})
return maskObject(obj, maskingOptions)
}
module.exports = { module.exports = {
deepMerge, deepMerge,
generateSecureSecret, generateSecureSecret,
getValidatedHost, getValidatedHost,
getValidatedPort, getValidatedPort,
isValidCredentials, isValidCredentials,
maskSensitiveData,
modifyHtml,
validateConfig,
validateSshTerm validateSshTerm
} }