Skip to content

Commit cc8fd3e

Browse files
Merge pull request #200 from community-scripts/fix/197
fix/197: enforce numeric ssh_port end-to-end; harden UI input; coerce in API/DB
2 parents 2b58823 + 5a73a30 commit cc8fd3e

File tree

3 files changed

+36
-6
lines changed

3 files changed

+36
-6
lines changed

server.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,18 @@ import stripAnsi from 'strip-ansi';
88
import { spawn as ptySpawn } from 'node-pty';
99
import { getSSHExecutionService } from './src/server/ssh-execution-service.js';
1010
import { getDatabase } from './src/server/database-prisma.js';
11-
import { registerGlobalErrorHandlers } from './src/server/logging/globalHandlers.js';
11+
// Fallback minimal global error handlers for Node runtime (avoid TS import)
12+
function registerGlobalErrorHandlers() {
13+
if (registerGlobalErrorHandlers._registered) return;
14+
registerGlobalErrorHandlers._registered = true;
15+
process.on('uncaughtException', (err) => {
16+
console.error('uncaught_exception', err);
17+
});
18+
process.on('unhandledRejection', (reason) => {
19+
console.error('unhandled_rejection', reason);
20+
});
21+
}
22+
registerGlobalErrorHandlers._registered = false;
1223

1324
const dev = process.env.NODE_ENV !== 'production';
1425
const hostname = '0.0.0.0';

src/app/_components/ServerForm.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,21 @@ export function ServerForm({ onSubmit, initialData, isEditing = false, onCancel
122122
const handleChange = (field: keyof CreateServerData) => (
123123
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
124124
) => {
125-
setFormData(prev => ({ ...prev, [field]: e.target.value }));
125+
// Special handling for numeric ssh_port: keep it strictly numeric
126+
if (field === 'ssh_port') {
127+
const raw = (e.target as HTMLInputElement).value ?? '';
128+
const digitsOnly = raw.replace(/\D+/g, '');
129+
setFormData(prev => ({
130+
...prev,
131+
ssh_port: digitsOnly ? parseInt(digitsOnly, 10) : undefined,
132+
}));
133+
if (errors.ssh_port) {
134+
setErrors(prev => ({ ...prev, ssh_port: undefined }));
135+
}
136+
return;
137+
}
138+
139+
setFormData(prev => ({ ...prev, [field]: (e.target as HTMLInputElement).value }));
126140
// Clear error when user starts typing
127141
if (errors[field]) {
128142
setErrors(prev => ({ ...prev, [field]: undefined }));
@@ -246,14 +260,17 @@ export function ServerForm({ onSubmit, initialData, isEditing = false, onCancel
246260
<input
247261
type="number"
248262
id="ssh_port"
263+
inputMode="numeric"
264+
pattern="[0-9]*"
265+
autoComplete="off"
249266
value={formData.ssh_port ?? 22}
250267
onChange={handleChange('ssh_port')}
251268
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 ${
252269
errors.ssh_port ? 'border-destructive' : 'border-border'
253270
}`}
254271
placeholder="22"
255-
min="1"
256-
max="65535"
272+
min={1}
273+
max={65535}
257274
/>
258275
{errors.ssh_port && <p className="mt-1 text-sm text-destructive">{errors.ssh_port}</p>}
259276
</div>

src/server/database-prisma.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class DatabaseServicePrisma {
2020
// Server CRUD operations
2121
async createServer(serverData: CreateServerData) {
2222
const { name, ip, user, password, auth_type, ssh_key, ssh_key_passphrase, ssh_port, color, key_generated } = serverData;
23+
const normalizedPort = ssh_port !== undefined ? parseInt(String(ssh_port), 10) : 22;
2324

2425
let ssh_key_path = null;
2526

@@ -38,7 +39,7 @@ class DatabaseServicePrisma {
3839
auth_type: auth_type ?? 'password',
3940
ssh_key,
4041
ssh_key_passphrase,
41-
ssh_port: ssh_port ?? 22,
42+
ssh_port: Number.isNaN(normalizedPort) ? 22 : normalizedPort,
4243
ssh_key_path,
4344
key_generated: Boolean(key_generated),
4445
color,
@@ -60,6 +61,7 @@ class DatabaseServicePrisma {
6061

6162
async updateServer(id: number, serverData: CreateServerData) {
6263
const { name, ip, user, password, auth_type, ssh_key, ssh_key_passphrase, ssh_port, color, key_generated } = serverData;
64+
const normalizedPort = ssh_port !== undefined ? parseInt(String(ssh_port), 10) : undefined;
6365

6466
// Get existing server to check for key changes
6567
const existingServer = await this.getServerById(id);
@@ -109,7 +111,7 @@ class DatabaseServicePrisma {
109111
auth_type: auth_type ?? 'password',
110112
ssh_key,
111113
ssh_key_passphrase,
112-
ssh_port: ssh_port ?? 22,
114+
ssh_port: normalizedPort ?? 22,
113115
ssh_key_path,
114116
key_generated: key_generated !== undefined ? Boolean(key_generated) : (existingServer?.key_generated ?? false),
115117
color,

0 commit comments

Comments
 (0)