fix(core): guard material-ref parsers against non-string values (Sentry MONOREPO-EDITOR-EM) (#486)

* fix(core): guard material-ref parsers against non-string values (Sentry MONOREPO-EDITOR-EM)

getLibraryMaterialIdFromRef and getSceneMaterialIdFromRef only guarded
against null/undefined, then called .startsWith(). When a non-string
material ref reaches them (legacy/malformed wall material slot ref),
.startsWith is undefined -> TypeError: e.startsWith is not a function.

Narrow with typeof !== 'string' -> return null, so a bad ref degrades to
'no library/scene material' instead of throwing during wall material
resolution (packages/viewer wall-materials.ts -> parseMaterialRef).

* test(core): cover malformed material refs

---------

Co-authored-by: Aymeric Rabot <aymeric.rabot@gmail.com>
This commit is contained in:
Anton
2026-07-19 17:41:54 +02:00
committed by GitHub
co-authored by Aymeric Rabot
parent 5c7f58f295
commit f67787077e
2 changed files with 16 additions and 2 deletions
@@ -0,0 +1,13 @@
import { describe, expect, test } from 'bun:test'
import { getLibraryMaterialIdFromRef, getSceneMaterialIdFromRef } from './material-library'
describe('material references', () => {
test('rejects malformed runtime values instead of calling string methods', () => {
const malformedRefs: unknown[] = [42, true, {}, []]
for (const ref of malformedRefs) {
expect(getLibraryMaterialIdFromRef(ref as string)).toBeNull()
expect(getSceneMaterialIdFromRef(ref as string)).toBeNull()
}
})
})
+3 -2
View File
@@ -4170,13 +4170,14 @@ export function toSceneMaterialRef(id: string) {
} }
export function getLibraryMaterialIdFromRef(materialRef?: string | null) { export function getLibraryMaterialIdFromRef(materialRef?: string | null) {
if (!materialRef) return null if (typeof materialRef !== 'string') return null
if (!materialRef.startsWith(LIBRARY_MATERIAL_REF_PREFIX)) return null if (!materialRef.startsWith(LIBRARY_MATERIAL_REF_PREFIX)) return null
return materialRef.slice(LIBRARY_MATERIAL_REF_PREFIX.length) return materialRef.slice(LIBRARY_MATERIAL_REF_PREFIX.length)
} }
export function getSceneMaterialIdFromRef(materialRef?: string | null): string | null { export function getSceneMaterialIdFromRef(materialRef?: string | null): string | null {
if (!materialRef?.startsWith(SCENE_MATERIAL_REF_PREFIX)) return null if (typeof materialRef !== 'string') return null
if (!materialRef.startsWith(SCENE_MATERIAL_REF_PREFIX)) return null
return materialRef.slice(SCENE_MATERIAL_REF_PREFIX.length) return materialRef.slice(SCENE_MATERIAL_REF_PREFIX.length)
} }