, & inside string values so attacker- or // filter-controlled params can't break out of the quoted onload attribute. $json_params = wp_json_encode( $params, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP ); $json_params = sprintf( '{target:this, ...%s}', $json_params ); $onload = sprintf( 'new BeyondWords.Player(%s);', $json_params ); $onload = apply_filters( 'beyondwords_player_script_onload', $onload, $params ); return sprintf( // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript '', esc_attr( $context ), esc_url( \BeyondWords\Core\Urls::get_js_sdk_url() ), esc_attr( $onload ) ); } }