mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-22 00:13:45 -05:00
Validate file IDs for correctness (#7067)
* Validate file IDs for correctness * Add release notes
This commit is contained in:
committed by
GitHub
parent
a1e0b3f45d
commit
18072e1d8b
@@ -366,6 +366,19 @@ describe('/upload-user-file', () => {
|
||||
expect(res.text).toBe('fileId is required');
|
||||
});
|
||||
|
||||
it('returns 400 for invalid fileId format', async () => {
|
||||
const res = await request(app)
|
||||
.post('/upload-user-file')
|
||||
.set('Content-Type', 'application/encrypted-file')
|
||||
.set('x-actual-token', 'valid-token')
|
||||
.set('x-actual-name', 'test-file')
|
||||
.set('x-actual-file-id', 'budget@2026')
|
||||
.send(Buffer.from('file content'));
|
||||
|
||||
expect(res.statusCode).toEqual(400);
|
||||
expect(res.text).toBe('invalid fileId');
|
||||
});
|
||||
|
||||
it('uploads a new file successfully', async () => {
|
||||
const fileId = crypto.randomBytes(16).toString('hex');
|
||||
const fileName = 'test-file.txt';
|
||||
@@ -670,6 +683,16 @@ describe('/download-user-file', () => {
|
||||
expect(res.text).toBe('User or file not found');
|
||||
});
|
||||
|
||||
it('returns 400 for invalid fileId format', async () => {
|
||||
const res = await request(app)
|
||||
.get('/download-user-file')
|
||||
.set('x-actual-token', 'valid-token')
|
||||
.set('x-actual-file-id', 'budget@2026');
|
||||
|
||||
expect(res.statusCode).toEqual(400);
|
||||
expect(res.text).toBe('invalid fileId');
|
||||
});
|
||||
|
||||
it('returns 500 error if the file does not exist on the filesystem', async () => {
|
||||
getAccountDb().mutate(
|
||||
'INSERT INTO files (id, deleted) VALUES (?, FALSE)',
|
||||
|
||||
@@ -49,11 +49,16 @@ app.use(express.json({ limit: `${config.get('upload.fileSizeLimitMB')}mb` }));
|
||||
export { app as handlers };
|
||||
|
||||
const OK_RESPONSE = { status: 'ok' };
|
||||
const FILE_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
|
||||
|
||||
function boolToInt(deleted) {
|
||||
return deleted ? 1 : 0;
|
||||
}
|
||||
|
||||
function isValidFileId(fileId: unknown): fileId is string {
|
||||
return typeof fileId === 'string' && FILE_ID_PATTERN.test(fileId);
|
||||
}
|
||||
|
||||
const verifyFileExists = (fileId, filesService, res, errorObject) => {
|
||||
try {
|
||||
return filesService.get(fileId);
|
||||
@@ -256,6 +261,10 @@ app.post('/upload-user-file', async (req, res) => {
|
||||
res.status(400).send('fileId is required');
|
||||
return;
|
||||
}
|
||||
if (!isValidFileId(fileId)) {
|
||||
res.status(400).send('invalid fileId');
|
||||
return;
|
||||
}
|
||||
|
||||
let groupId = req.headers['x-actual-group-id'] || null;
|
||||
const encryptMeta = req.headers['x-actual-encrypt-meta'] || null;
|
||||
@@ -352,6 +361,10 @@ app.get('/download-user-file', async (req, res) => {
|
||||
res.status(400).send('Single file ID is required');
|
||||
return;
|
||||
}
|
||||
if (!isValidFileId(fileId)) {
|
||||
res.status(400).send('invalid fileId');
|
||||
return;
|
||||
}
|
||||
|
||||
const filesService = new FilesService(getAccountDb());
|
||||
const file = verifyFileExists(
|
||||
|
||||
Reference in New Issue
Block a user