fix: crash when a string is passed for the topics parameter of the mercure_publish() function (#2021)

This commit is contained in:
Kévin Dunglas
2025-11-23 17:03:43 +01:00
committed by GitHub
parent c6cadf3bf6
commit 911e6d156b
5 changed files with 50 additions and 7 deletions

View File

@@ -47,6 +47,7 @@ type testOptions struct {
realServer bool
logger *slog.Logger
initOpts []frankenphp.Option
requestOpts []frankenphp.RequestOption
phpIni map[string]string
}
@@ -82,8 +83,10 @@ func runTest(t *testing.T, test func(func(http.ResponseWriter, *http.Request), *
require.NoError(t, err)
defer frankenphp.Shutdown()
opts.requestOpts = append(opts.requestOpts, frankenphp.WithRequestDocumentRoot(testDataDir, false))
handler := func(w http.ResponseWriter, r *http.Request) {
req, err := frankenphp.NewRequestWithContext(r, frankenphp.WithRequestDocumentRoot(testDataDir, false))
req, err := frankenphp.NewRequestWithContext(r, opts.requestOpts...)
assert.NoError(t, err)
err = frankenphp.ServeHTTP(w, req)
@@ -1003,6 +1006,7 @@ func FuzzRequest(f *testing.F) {
if strings.Contains(req.URL.Path, "\x00") {
assert.Equal(t, 400, resp.StatusCode)
assert.Contains(t, body, "invalid request path")
return
}

View File

@@ -17,7 +17,7 @@ type mercureContext struct {
}
//export go_mercure_publish
func go_mercure_publish(threadIndex C.uintptr_t, topics *C.struct__zval_struct, data unsafe.Pointer, private bool, id, typ unsafe.Pointer, retry uint64) (generatedID *C.zend_string, error C.short) {
func go_mercure_publish(threadIndex C.uintptr_t, topics *C.struct__zval_struct, data *C.zend_string, private bool, id, typ *C.zend_string, retry uint64) (generatedID *C.zend_string, error C.short) {
thread := phpThreads[threadIndex]
ctx := thread.context()
fc := thread.frankenPHPContext()
@@ -32,10 +32,10 @@ func go_mercure_publish(threadIndex C.uintptr_t, topics *C.struct__zval_struct,
u := &mercure.Update{
Event: mercure.Event{
Data: GoString(data),
ID: GoString(id),
Data: GoString(unsafe.Pointer(data)),
ID: GoString(unsafe.Pointer(id)),
Retry: retry,
Type: GoString(typ),
Type: GoString(unsafe.Pointer(typ)),
},
Private: private,
}
@@ -43,7 +43,7 @@ func go_mercure_publish(threadIndex C.uintptr_t, topics *C.struct__zval_struct,
zvalType := C.zval_get_type(topics)
switch zvalType {
case C.IS_STRING:
u.Topics = []string{GoString(unsafe.Pointer(topics))}
u.Topics = []string{GoString(unsafe.Pointer(*(**C.zend_string)(unsafe.Pointer(&topics.value[0]))))}
case C.IS_ARRAY:
ts, err := GoPackedArray[string](unsafe.Pointer(topics))
if err != nil {

32
mercure_test.go Normal file
View File

@@ -0,0 +1,32 @@
//go:build !nomercure
package frankenphp_test
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/dunglas/frankenphp"
"github.com/dunglas/mercure"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMercurePublish_module(t *testing.T) { testMercurePublish(t, &testOptions{}) }
func TestMercurePublish_worker(t *testing.T) {
testMercurePublish(t, &testOptions{workerScript: "index.php"})
}
func testMercurePublish(t *testing.T, opts *testOptions) {
h, err := mercure.NewHub(t.Context(), mercure.WithTransport(mercure.NewLocalTransport(mercure.NewSubscriberList(0))))
require.NoError(t, err)
opts.requestOpts = []frankenphp.RequestOption{frankenphp.WithMercureHub(h)}
runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) {
body, _ := testGet(fmt.Sprintf("https://example.com/mercure-publish.php?i=%d", i), handler, t)
assert.Contains(t, body, "update 1: ")
assert.Contains(t, body, "update 2: ")
}, opts)
}

1
testdata/flush.php vendored
View File

@@ -1,6 +1,5 @@
<?php
require_once __DIR__.'/_executor.php';
return function () {

8
testdata/mercure-publish.php vendored Normal file
View File

@@ -0,0 +1,8 @@
<?php
require_once __DIR__.'/_executor.php';
return function () {
echo "update 1: " . mercure_publish('foo', 'bar', true, 'myid', 'mytype', 10) . PHP_EOL;
echo "update 2: " . mercure_publish(['baz', 'bar']) . PHP_EOL;
};