From c2ef4bfd0375428ba748c67cae4d84de0ea92fec Mon Sep 17 00:00:00 2001 From: JPeer264 Date: Thu, 30 Apr 2026 11:01:21 +0200 Subject: [PATCH] fix(js): Prevent argument injection via type coercion in serializeOptions Add runtime type validation for string/number options to match existing validation for array and boolean types. Wrap arguments in array literal to prevent concat() from flattening array inputs. Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 1 + lib/__tests__/helper.test.js | 32 ++++++++++++++++++++++++++++++++ lib/helper.ts | 6 +++++- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9bfd983d5..3de2336b65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixes - (snapshots) Stop sending Sentry auth token to Objectstore ([#3286](https://github.com/getsentry/sentry-cli/pull/3286)) +- (js) Fix argument injection in JavaScript API's `serializeOptions`. String/number options now validate input types and prevent `Array.prototype.concat()` from flattening array values into separate CLI arguments. ([#3287](https://github.com/getsentry/sentry-cli/pull/3287)) ## 3.4.1 diff --git a/lib/__tests__/helper.test.js b/lib/__tests__/helper.test.js index b5ebe92660..ef8329b6d5 100644 --- a/lib/__tests__/helper.test.js +++ b/lib/__tests__/helper.test.js @@ -203,6 +203,38 @@ describe('SentryCli helper', () => { '--ignore-file', '/js.ignore', ]); + + // Should throw when array is passed to string option (argument injection prevention) + expect(() => { + helper.prepareCommand(command, SOURCEMAPS_OPTIONS, { + urlPrefix: ['~/', '--auth-token', 'malicious-token'], + }); + }).toThrow('urlPrefix should be a string or number'); + + expect(() => { + helper.prepareCommand(command, SOURCEMAPS_OPTIONS, { + urlSuffix: ['?hash=1', '--url', 'https://evil.com'], + }); + }).toThrow('urlSuffix should be a string or number'); + + expect(() => { + helper.prepareCommand(command, SOURCEMAPS_OPTIONS, { ignoreFile: { path: '/evil' } }); + }).toThrow('ignoreFile should be a string or number'); + }); + + test('call prepare command with number option', () => { + const command = ['sourcemaps', 'upload', '--release', 'release', '/dev/null']; + + // Numbers should be converted to strings + expect(helper.prepareCommand(command, SOURCEMAPS_OPTIONS, { urlPrefix: 123 })).toEqual([ + 'sourcemaps', + 'upload', + '--release', + 'release', + '/dev/null', + '--url-prefix', + '123', + ]); }); }); }); diff --git a/lib/helper.ts b/lib/helper.ts index 068c886e33..dd83538347 100644 --- a/lib/helper.ts +++ b/lib/helper.ts @@ -234,7 +234,11 @@ function serializeOptions(schema: OptionsSchema, options: Record