From 6cb70d263569f87888805675d32ab646b5377080 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Fri, 4 Sep 2026 13:31:57 +0100 Subject: [PATCH 1/2] fix(app): reject unsafe deepSet path segments --- packages/app/__tests__/common.test.ts | 22 ++++++++++++++++++++++ packages/app/lib/common/deeps.ts | 3 +++ 2 files changed, 25 insertions(+) diff --git a/packages/app/__tests__/common.test.ts b/packages/app/__tests__/common.test.ts index 511f657b01..2ddd03ad61 100644 --- a/packages/app/__tests__/common.test.ts +++ b/packages/app/__tests__/common.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from '@jest/globals'; import { Base64, getDataUrlParts } from '../lib/common'; +import { deepSet } from '../lib/common/deeps'; describe('common utilities', () => { describe('getDataUrlParts', () => { @@ -42,4 +43,25 @@ describe('common utilities', () => { }, ); }); + + describe('deepSet', () => { + it('rejects paths that mutate the target prototype', () => { + const target = {}; + + expect(deepSet(target, '__proto__.polluted', true)).toBe(false); + expect((target as Record).polluted).toBeUndefined(); + }); + + it('rejects paths that mutate Object.prototype', () => { + expect(deepSet({}, 'constructor.prototype.polluted', true, false)).toBe(false); + expect(({} as Record).polluted).toBeUndefined(); + }); + + it('still assigns values along an ordinary nested path', () => { + const target: Record = {}; + + expect(deepSet(target, 'a.b', 5)).toBe(true); + expect(target).toEqual({ a: { b: 5 } }); + }); + }); }); diff --git a/packages/app/lib/common/deeps.ts b/packages/app/lib/common/deeps.ts index 8ca859ac99..fda8723ad6 100644 --- a/packages/app/lib/common/deeps.ts +++ b/packages/app/lib/common/deeps.ts @@ -69,6 +69,9 @@ export function deepSet( return false; } const keys = path.split(joiner); + if (keys.some(key => ['__proto__', 'prototype', 'constructor'].includes(key))) { + return false; + } let i = 0; let _object: unknown = object; From ed3159f6bc4416dcb81a6bbdd3af4cf688855344 Mon Sep 17 00:00:00 2001 From: russellwheatley Date: Tue, 8 Sep 2026 13:17:41 +0100 Subject: [PATCH 2/2] fix(tests): exclude dist output from Jest coverage collection Packages cross-import each other's built dist output (e.g. database imports @react-native-firebase/app/dist/module/common/deeps), which resolves through the workspace symlink to packages/*/dist/**. Jest's default coveragePathIgnorePatterns only excludes node_modules/, so it was instrumenting both that build artifact and the lib/**.ts source it was compiled from. Any shared file with branches got a second, all-zero coverage entry alongside the real one, which is what made codecov/patch misreport fully-tested lines in deepSet's new guard as partial/miss. --- jest.config.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/jest.config.js b/jest.config.js index c109c65382..6a7cab682b 100644 --- a/jest.config.js +++ b/jest.config.js @@ -30,4 +30,12 @@ module.exports = { transformIgnorePatterns: [ 'node_modules/(?!((jest-)?react-native|@firebase|@react-native(-community)?))', ], + // Packages cross-import each other's built output (e.g. database imports + // `@react-native-firebase/app/dist/module/common/deeps`), which resolves + // through the workspace symlink to `packages/*/dist/**`. Without this, + // Jest instruments both that build artifact and the original `lib/**.ts` + // source it was compiled from, so any shared file with branches gets a + // second, all-zero coverage entry alongside the real one, and codecov's + // patch coverage misreports lines that are actually fully tested. + coveragePathIgnorePatterns: ['/node_modules/', '/dist/'], };