chore: refactor jsmasker calls
chore: implement constants
This commit is contained in:
parent
47910dd066
commit
2c3a89b5dc
7 changed files with 91 additions and 53 deletions
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
app/ssh.js
14
app/ssh.js
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
65
app/utils.js
65
app/utils.js
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue