From 614e623e61cff2d6ecb8c339afd6bf115959f825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Sede=C3=B1o?= Date: Mon, 22 Jun 2026 18:16:49 +0200 Subject: [PATCH] Skip calls to non-existent methods in _applyCalls() Frontend calls to /cbwire/update could reference a method that no component defines. _applyCalls() invoked it unconditionally, so it fell through to onMissingMethod() and threw a CBWIREException, returning a 500. Embedded browsers/WebViews and extensions sometimes serialize the $wire object and inject a call to the standard JS toJSON method (and similar noise). Guard the invoke with structKeyExists() so unknown methods are silently skipped instead of crashing the update request. --- models/Component.cfc | 7 +++++ .../tests/specs/unit/ComponentSpec.cfc | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/models/Component.cfc b/models/Component.cfc index 96c7476..65cb943 100644 --- a/models/Component.cfc +++ b/models/Component.cfc @@ -1092,6 +1092,13 @@ component output="true" accessors="true" { } arguments.calls.each( function( call ) { try { + // Skip calls to methods the component does not expose. Embedded + // browsers/WebViews and extensions sometimes serialize the $wire + // object and inject noise calls (e.g. the standard JS "toJSON"), + // which would otherwise hit onMissingMethod() and throw a 500. + if( !structKeyExists( this, arguments.call.method ) ){ + return; + } if( _securedAnnotationAllows( arguments.call.method ) ){ local.result = invoke( this, arguments.call.method, arguments.call.params ); // Capture the return value in case it's needed by the front-end diff --git a/test-harness/tests/specs/unit/ComponentSpec.cfc b/test-harness/tests/specs/unit/ComponentSpec.cfc index 8d91e89..ff4012d 100644 --- a/test-harness/tests/specs/unit/ComponentSpec.cfc +++ b/test-harness/tests/specs/unit/ComponentSpec.cfc @@ -78,6 +78,37 @@ component extends="coldbox.system.testing.BaseTestCase" { }); + describe( "_applyCalls()", function() { + + it( "skips calls to methods the component does not expose without throwing", function() { + variables.wireComponent.$property( propertyName="_returnValues", mock=[] ); + + // "toJSON" is the standard JS method injected by some WebViews + // when they serialize the $wire object; no component defines it. + expect( function(){ + variables.wireComponent._applyCalls( [ + { "method": "toJSON", "params": [] } + ] ); + } ).notToThrow(); + + expect( variables.wireComponent.$getProperty( "_returnValues" ) ).toBeEmpty(); + }); + + it( "invokes existing methods and captures their return value", function() { + variables.wireComponent.$property( propertyName="_returnValues", mock=[] ); + variables.wireComponent.$( "increment", "done" ); + + variables.wireComponent._applyCalls( [ + { "method": "increment", "params": [] } + ] ); + + var returns = variables.wireComponent.$getProperty( "_returnValues" ); + expect( returns ).toHaveLength( 1 ); + expect( returns[ 1 ] ).toBe( "done" ); + }); + + }); + }); }