Merge pull request #488 from Ravinou/develop

fix: 🐛 prevent shell injection by replacing exec with exeFile
This commit is contained in:
Ravinou 2025-05-31 17:30:12 +02:00 committed by GitHub
commit 05ba852371
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 40 additions and 11 deletions

View file

@ -1,5 +1,7 @@
#!/usr/bin/env bash
### DEPRECATED ### NodeJS will handle this in the future.
# Shell created by Raven for BorgWarehouse.
# This shell takes 1 arg : [repositoryName] with 8 char. length only.
# This shell **delete the repository** in arg and **all his data** and the line associated in the authorized_keys file.

View file

@ -1,5 +1,7 @@
#!/usr/bin/env bash
### DEPRECATED ### NodeJS will handle this in the future.
# Shell created by Raven for BorgWarehouse.
# Get the timestamp of the last modification of the file integrity.* for of all repositories in a JSON output.
# stdout will be an array like :

View file

@ -1,5 +1,7 @@
#!/usr/bin/env bash
### DEPRECATED ### NodeJS will handle this in the future.
# Shell created by Raven for BorgWarehouse.
# Get the size of all repositories in a JSON output.
# stdout will be an array like :

View file

@ -1,5 +1,7 @@
#!/usr/bin/env bash
### DEPRECATED ### NodeJS will handle this in the future.
# Shell created by Raven for BorgWarehouse.
# This shell takes 4 args: [repositoryName] [new SSH pub key] [quota] [append-only mode (boolean)]
# This shell updates the SSH key and the quota for a repository.

View file

@ -1,15 +1,20 @@
import path from 'path';
import { promisify } from 'util';
import { exec as execCallback } from 'node:child_process';
import { execFile as execFileCallback } from 'node:child_process';
import { LastSaveDTO, StorageUsedDTO } from '~/types';
import repositoryNameCheck from '~/helpers/functions/repositoryNameCheck';
const exec = promisify(execCallback);
const execFile = promisify(execFileCallback);
const shellsDirectory = path.join(process.cwd(), '/helpers/shells');
// This is to prevent the cronjob from being executed multiple times
let isLastSaveListRunning = false;
let isStorageUsedRunning = false;
function isValidSshKey(key: string): boolean {
return /^ssh-(rsa|ed25519|ed25519-sk) [A-Za-z0-9+/=]+(\s.+)?$/.test(key.trim());
}
export const ShellService = {
getLastSaveList: async (): Promise<LastSaveDTO[]> => {
if (isLastSaveListRunning) {
@ -19,7 +24,7 @@ export const ShellService = {
}
try {
const { stdout } = await exec(`${shellsDirectory}/getLastSave.sh`);
const { stdout } = await execFile(`${shellsDirectory}/getLastSave.sh`);
return JSON.parse(stdout || '[]');
} finally {
isLastSaveListRunning = false;
@ -33,7 +38,7 @@ export const ShellService = {
isStorageUsedRunning = true;
}
try {
const { stdout } = await exec(`${shellsDirectory}/getStorageUsed.sh`);
const { stdout } = await execFile(`${shellsDirectory}/getStorageUsed.sh`);
return JSON.parse(stdout || '[]');
} finally {
isStorageUsedRunning = false;
@ -41,7 +46,7 @@ export const ShellService = {
},
deleteRepo: async (repositoryName: string) => {
const { stdout, stderr } = await exec(`${shellsDirectory}/deleteRepo.sh ${repositoryName}`);
const { stdout, stderr } = await execFile(`${shellsDirectory}/deleteRepo.sh`, [repositoryName]);
return { stdout, stderr };
},
@ -51,9 +56,19 @@ export const ShellService = {
storageSize: number,
appendOnlyMode: boolean
) => {
const { stdout, stderr } = await exec(
`${shellsDirectory}/updateRepo.sh ${repositoryName} "${sshPublicKey}" ${storageSize} ${appendOnlyMode}`
);
if (!isValidSshKey(sshPublicKey)) {
throw new Error('Invalid SSH key format');
}
if (!repositoryNameCheck(repositoryName)) {
throw new Error('Invalid repository name format');
}
const { stdout, stderr } = await execFile(`${shellsDirectory}/updateRepo.sh`, [
repositoryName,
sshPublicKey,
storageSize.toString(),
appendOnlyMode.toString(),
]);
return { stdout, stderr };
},
@ -62,9 +77,15 @@ export const ShellService = {
storageSize: number,
appendOnlyMode: boolean
): Promise<{ stdout?: string; stderr?: string }> => {
const { stdout, stderr } = await exec(
`${shellsDirectory}/createRepo.sh "${sshPublicKey}" ${storageSize} ${appendOnlyMode}`
);
if (!isValidSshKey(sshPublicKey)) {
throw new Error('Invalid SSH key format');
}
const { stdout, stderr } = await execFile(`${shellsDirectory}/createRepo.sh`, [
sshPublicKey,
storageSize.toString(),
appendOnlyMode.toString(),
]);
return { stdout, stderr };
},
};