diff --git a/packages/animations/browser/src/render/transition_animation_engine.ts b/packages/animations/browser/src/render/transition_animation_engine.ts index 3fb49af3c05..976ea967a9e 100644 --- a/packages/animations/browser/src/render/transition_animation_engine.ts +++ b/packages/animations/browser/src/render/transition_animation_engine.ts @@ -112,7 +112,7 @@ class StateValue { input: any, public namespaceId: string = '', ) { - const isObj = input && input.hasOwnProperty('value'); + const isObj = input && Object.hasOwn(input, 'value'); const value = isObj ? input['value'] : input; this.value = normalizeTriggerValue(value); if (isObj) { @@ -246,7 +246,7 @@ class AnimationTransitionNamespace { let fromState = triggersWithStates.get(triggerName); const toState = new StateValue(value, this.id); - const isObj = value && value.hasOwnProperty('value'); + const isObj = value && Object.hasOwn(value, 'value'); if (!isObj && fromState) { toState.absorbOptions(fromState.options); } diff --git a/packages/animations/browser/test/render/transition_animation_engine_spec.ts b/packages/animations/browser/test/render/transition_animation_engine_spec.ts index 6a63b6bf1a5..7ace8eefe5f 100644 --- a/packages/animations/browser/test/render/transition_animation_engine_spec.ts +++ b/packages/animations/browser/test/render/transition_animation_engine_spec.ts @@ -118,6 +118,25 @@ const DEFAULT_NAMESPACE_ID = 'id'; expect(engine.players.length).toEqual(1); }); + it('should read the value from a trigger object whose own key shadows hasOwnProperty', () => { + const engine = makeEngine(); + + const trig = trigger('myTrigger', [ + transition('* => *', [style({height: '0px'}), animate(1000, style({height: '100px'}))]), + ]); + + registerTrigger(element, engine, trig); + + // A bound trigger value in the `{value, params}` object form can come from + // untrusted data (e.g. a parsed JSON payload) and carry an own `hasOwnProperty` + // key that shadows the method. Detecting the object form must not depend on it. + const value = JSON.parse('{"value": "matched", "hasOwnProperty": "x"}'); + expect(() => setProperty(element, engine, 'myTrigger', value)).not.toThrow(); + + engine.flush(); + expect(engine.players.length).toEqual(1); + }); + it('should throw an error if an animation property without a matching trigger is changed', () => { const engine = makeEngine(); expect(() => {