Skip to content

Commit 95f220c

Browse files
committed
feat: improve public key validation in /:username/ssh-keys
1 parent 42b2b6e commit 95f220c

File tree

1 file changed

+22
-9
lines changed

1 file changed

+22
-9
lines changed

src/service/routes/users.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import express, { Request, Response } from 'express';
2-
const router = express.Router();
2+
import { utils } from 'ssh2';
33

44
import * as db from '../../db';
55
import { toPublicUser } from './publicApi';
66

7+
const router = express.Router();
8+
const parseKey = utils.parseKey;
9+
710
router.get('/', async (req: Request, res: Response) => {
811
console.log('fetching users');
912
const users = await db.getUsers();
@@ -24,30 +27,40 @@ router.get('/:id', async (req: Request, res: Response) => {
2427
// Add SSH public key
2528
router.post('/:username/ssh-keys', async (req: Request, res: Response) => {
2629
if (!req.user) {
27-
res.status(401).json({ error: 'Authentication required' });
30+
res.status(401).json({ error: 'Login required' });
2831
return;
2932
}
3033

3134
const { username, admin } = req.user as { username: string; admin: boolean };
3235
const targetUsername = req.params.username.toLowerCase();
3336

34-
// Only allow users to add keys to their own account, or admins to add to any account
37+
// Admins can add to any account, users can only add to their own
3538
if (username !== targetUsername && !admin) {
3639
res.status(403).json({ error: 'Not authorized to add keys for this user' });
3740
return;
3841
}
3942

4043
const { publicKey } = req.body;
41-
if (!publicKey) {
44+
if (!publicKey || typeof publicKey !== 'string') {
4245
res.status(400).json({ error: 'Public key is required' });
4346
return;
4447
}
4548

46-
// Strip the comment from the key (everything after the last space)
47-
const keyWithoutComment = publicKey.split(' ').slice(0, 2).join(' ');
48-
49-
console.log('Adding SSH key', { targetUsername, keyWithoutComment });
5049
try {
50+
const parsedKey = parseKey(publicKey.trim());
51+
52+
if (parsedKey instanceof Error) {
53+
res.status(400).json({ error: `Invalid SSH key: ${parsedKey.message}` });
54+
return;
55+
}
56+
57+
if (parsedKey.isPrivateKey()) {
58+
res.status(400).json({ error: 'Invalid SSH key: Must be a public key' });
59+
return;
60+
}
61+
62+
const keyWithoutComment = parsedKey.getPublicSSH().toString('utf8');
63+
console.log('Adding SSH key', { targetUsername, keyWithoutComment });
5164
await db.addPublicKey(targetUsername, keyWithoutComment);
5265
res.status(201).json({ message: 'SSH key added successfully' });
5366
} catch (error) {
@@ -59,7 +72,7 @@ router.post('/:username/ssh-keys', async (req: Request, res: Response) => {
5972
// Remove SSH public key
6073
router.delete('/:username/ssh-keys', async (req: Request, res: Response) => {
6174
if (!req.user) {
62-
res.status(401).json({ error: 'Authentication required' });
75+
res.status(401).json({ error: 'Login required' });
6376
return;
6477
}
6578

0 commit comments

Comments
 (0)