mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-13 21:43:55 -06:00
fix: refuse a path that could escape the command
device_setup_command built a shell command from the agent's values: a typographic quote ends a PowerShell string, and a path starting with - reads as an option. Such values are refused, the options are written attached to their values, and platform must be a setup name.
This commit is contained in:
1 parent
24d7db0ab3
commit
d10c037a86
2 files changed
+27
-5
No files matched your search
@@ -84,6 +84,15 @@ class TheToolsReadOnly(unittest.TestCase):
|
||||
self.assertIn("const games = everything ? [] :", self.source)
|
||||
self.assertIn("'--required-only'", self.source)
|
||||
|
||||
def test_a_value_that_could_escape_the_command_is_refused(self) -> None:
|
||||
"""The command is pasted into a shell: PowerShell also ends a single-quoted
|
||||
string on the typographic quotes, and a path starting with "-" reads as
|
||||
an option."""
|
||||
self.assertIn("const unsafe = /[\\u0000-\\u001f\\u007f'\"`‘’‚‛“”„]/;", self.source)
|
||||
self.assertIn("value.startsWith('-')", self.source)
|
||||
self.assertIn("/^[a-z0-9_-]+$/.test(platform)", self.source)
|
||||
self.assertIn("`--dest=${quote(bios_folder as string)}`", self.source)
|
||||
|
||||
def test_they_reach_no_other_site(self) -> None:
|
||||
code = re.sub(r"^\s*//.*$", "", self.source, flags=re.M)
|
||||
self.assertEqual(set(re.findall(r"https://[\w.-]+", code)), {"https://abdess.github.io"})
|
||||
|
||||
@@ -141,12 +141,25 @@ const setupCommand: Tool = {
|
||||
},
|
||||
annotations: { readOnlyHint: true },
|
||||
async execute({ shell, everything, bios_folder, games_folder, platform }) {
|
||||
const quote = (value: unknown) => `'${String(value).replace(/'/g, shell === 'powershell' ? "''" : "'\\''")}'`;
|
||||
const run = shell === 'powershell' ? PS : SH;
|
||||
const powershell = shell === 'powershell';
|
||||
// The command is pasted into a shell: a value that could close its
|
||||
// quotes (PowerShell also ends a string on the typographic ones) or
|
||||
// start another option is refused, never escaped.
|
||||
const unsafe = /[\u0000-\u001f\u007f'"`‘’‚‛“”„]/;
|
||||
for (const [field, value] of [['bios_folder', bios_folder], ['games_folder', games_folder]] as const) {
|
||||
if (value !== undefined && (typeof value !== 'string' || !value || unsafe.test(value) || value.startsWith('-'))) {
|
||||
return `${field} must be a folder path without quotes or control characters.`;
|
||||
}
|
||||
}
|
||||
if (platform !== undefined && (typeof platform !== 'string' || !/^[a-z0-9_-]+$/.test(platform))) {
|
||||
return 'platform must be a setup name such as retroarch or batocera.';
|
||||
}
|
||||
const run = powershell ? PS : SH;
|
||||
const quote = (value: string) => `'${value}'`;
|
||||
const place: string[] = [];
|
||||
if (platform) place.push('--platform', quote(platform));
|
||||
if (bios_folder) place.push('--dest', quote(bios_folder));
|
||||
const games = everything ? [] : [...(games_folder ? ['--for-games', quote(games_folder)] : ['--for-games']), '--required-only'];
|
||||
if (platform) place.push(`--platform=${platform}`);
|
||||
if (bios_folder) place.push(`--dest=${quote(bios_folder as string)}`);
|
||||
const games = everything ? [] : [games_folder ? `--for-games=${quote(games_folder as string)}` : '--for-games', '--required-only'];
|
||||
const command = (...more: string[]) => [run, ...place, ...more].join(' ');
|
||||
return JSON.stringify({
|
||||
preview: command(...games, '--dry-run'),
|
||||
|
||||
Reference in new issue
Block a user