diff --git a/tools/js/extractSlotComponents.js b/tools/js/extractSlotComponents.js index 0957c203a..a1c15b9b5 100644 --- a/tools/js/extractSlotComponents.js +++ b/tools/js/extractSlotComponents.js @@ -9,8 +9,21 @@ if (!version) { process.exit(1) } +if (!/^[a-zA-Z0-9._-]+$/.test(version)) { + console.error('Invalid version: must contain only alphanumeric characters, dots, underscores, or hyphens') + process.exit(1) +} + if (!fs.existsSync(sourceDir)) { - cp.execSync(`git clone -b client${version} https://github.com/extremeheat/extracted_minecraft_data.git ${sourceDir} --depth 1`, { stdio: 'inherit' }) + cp.execFileSync('git', [ + 'clone', + '-b', + `client${version}`, + 'https://github.com/extremeheat/extracted_minecraft_data.git', + sourceDir, + '--depth', + '1' + ], { stdio: 'inherit' }) } const componentsFile = fs.readFileSync(`./${sourceDir}/client/net/minecraft/core/component/DataComponents.java`, 'utf8') diff --git a/tools/js/test/extractSlotComponents.js b/tools/js/test/extractSlotComponents.js new file mode 100644 index 000000000..2587b2836 --- /dev/null +++ b/tools/js/test/extractSlotComponents.js @@ -0,0 +1,37 @@ +/* eslint-env mocha */ + +const assert = require('assert') +const cp = require('child_process') +const fs = require('fs') +const os = require('os') +const path = require('path') + +describe('extractSlotComponents.js command injection regression', function () { + const scriptPath = path.join(__dirname, '../extractSlotComponents.js') + + it('rejects a version containing shell metacharacters without executing them', function () { + const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'mcdata-extract-test-')) + const markerFile = path.join(cwd, 'pwned') + + const result = cp.spawnSync('node', [scriptPath, `1.21; touch ${markerFile}`], { cwd, encoding: 'utf8' }) + + assert.notStrictEqual(result.status, 0) + assert.match(result.stderr, /Invalid version/) + assert.strictEqual(fs.existsSync(markerFile), false) + + fs.rmSync(cwd, { recursive: true, force: true }) + }) + + it('accepts an ordinary dotted version label', function () { + const version = '1.21.4' + const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'mcdata-extract-test-')) + // Pre-create the expected source dir so the script skips the git clone entirely. + fs.mkdirSync(path.join(cwd, `mcsrc-${version}`)) + + const result = cp.spawnSync('node', [scriptPath, version], { cwd, encoding: 'utf8' }) + + assert.doesNotMatch(result.stderr, /Invalid version/) + + fs.rmSync(cwd, { recursive: true, force: true }) + }) +})