fix(server): enforce numeric ssh_port end-to-end; harden UI input; coerce in API/DB; fix runtime handler import
This commit is contained in:
13
server.js
13
server.js
@@ -8,7 +8,18 @@ import stripAnsi from 'strip-ansi';
|
||||
import { spawn as ptySpawn } from 'node-pty';
|
||||
import { getSSHExecutionService } from './src/server/ssh-execution-service.js';
|
||||
import { getDatabase } from './src/server/database-prisma.js';
|
||||
import { registerGlobalErrorHandlers } from './src/server/logging/globalHandlers.js';
|
||||
// Fallback minimal global error handlers for Node runtime (avoid TS import)
|
||||
function registerGlobalErrorHandlers() {
|
||||
if (registerGlobalErrorHandlers._registered) return;
|
||||
registerGlobalErrorHandlers._registered = true;
|
||||
process.on('uncaughtException', (err) => {
|
||||
console.error('uncaught_exception', err);
|
||||
});
|
||||
process.on('unhandledRejection', (reason) => {
|
||||
console.error('unhandled_rejection', reason);
|
||||
});
|
||||
}
|
||||
registerGlobalErrorHandlers._registered = false;
|
||||
|
||||
const dev = process.env.NODE_ENV !== 'production';
|
||||
const hostname = '0.0.0.0';
|
||||
|
||||
@@ -122,7 +122,21 @@ export function ServerForm({ onSubmit, initialData, isEditing = false, onCancel
|
||||
const handleChange = (field: keyof CreateServerData) => (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
|
||||
) => {
|
||||
setFormData(prev => ({ ...prev, [field]: e.target.value }));
|
||||
// Special handling for numeric ssh_port: keep it strictly numeric
|
||||
if (field === 'ssh_port') {
|
||||
const raw = (e.target as HTMLInputElement).value ?? '';
|
||||
const digitsOnly = raw.replace(/\D+/g, '');
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
ssh_port: digitsOnly ? parseInt(digitsOnly, 10) : undefined,
|
||||
}));
|
||||
if (errors.ssh_port) {
|
||||
setErrors(prev => ({ ...prev, ssh_port: undefined }));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setFormData(prev => ({ ...prev, [field]: (e.target as HTMLInputElement).value }));
|
||||
// Clear error when user starts typing
|
||||
if (errors[field]) {
|
||||
setErrors(prev => ({ ...prev, [field]: undefined }));
|
||||
@@ -246,14 +260,17 @@ export function ServerForm({ onSubmit, initialData, isEditing = false, onCancel
|
||||
<input
|
||||
type="number"
|
||||
id="ssh_port"
|
||||
inputMode="numeric"
|
||||
pattern="[0-9]*"
|
||||
autoComplete="off"
|
||||
value={formData.ssh_port ?? 22}
|
||||
onChange={handleChange('ssh_port')}
|
||||
className={`w-full px-3 py-2 border rounded-md shadow-sm bg-card text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:border-ring ${
|
||||
errors.ssh_port ? 'border-destructive' : 'border-border'
|
||||
}`}
|
||||
placeholder="22"
|
||||
min="1"
|
||||
max="65535"
|
||||
min={1}
|
||||
max={65535}
|
||||
/>
|
||||
{errors.ssh_port && <p className="mt-1 text-sm text-destructive">{errors.ssh_port}</p>}
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@ class DatabaseServicePrisma {
|
||||
// Server CRUD operations
|
||||
async createServer(serverData: CreateServerData) {
|
||||
const { name, ip, user, password, auth_type, ssh_key, ssh_key_passphrase, ssh_port, color, key_generated } = serverData;
|
||||
const normalizedPort = ssh_port !== undefined ? parseInt(String(ssh_port), 10) : 22;
|
||||
|
||||
let ssh_key_path = null;
|
||||
|
||||
@@ -38,7 +39,7 @@ class DatabaseServicePrisma {
|
||||
auth_type: auth_type ?? 'password',
|
||||
ssh_key,
|
||||
ssh_key_passphrase,
|
||||
ssh_port: ssh_port ?? 22,
|
||||
ssh_port: Number.isNaN(normalizedPort) ? 22 : normalizedPort,
|
||||
ssh_key_path,
|
||||
key_generated: Boolean(key_generated),
|
||||
color,
|
||||
@@ -60,6 +61,7 @@ class DatabaseServicePrisma {
|
||||
|
||||
async updateServer(id: number, serverData: CreateServerData) {
|
||||
const { name, ip, user, password, auth_type, ssh_key, ssh_key_passphrase, ssh_port, color, key_generated } = serverData;
|
||||
const normalizedPort = ssh_port !== undefined ? parseInt(String(ssh_port), 10) : undefined;
|
||||
|
||||
// Get existing server to check for key changes
|
||||
const existingServer = await this.getServerById(id);
|
||||
@@ -109,7 +111,7 @@ class DatabaseServicePrisma {
|
||||
auth_type: auth_type ?? 'password',
|
||||
ssh_key,
|
||||
ssh_key_passphrase,
|
||||
ssh_port: ssh_port ?? 22,
|
||||
ssh_port: normalizedPort ?? 22,
|
||||
ssh_key_path,
|
||||
key_generated: key_generated !== undefined ? Boolean(key_generated) : (existingServer?.key_generated ?? false),
|
||||
color,
|
||||
|
||||
Reference in New Issue
Block a user