// Package lua 提供 Lua 脚本嵌入能力 package lua import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" glua "github.com/yuin/gopher-lua" ) // TestSandboxBlocksCoroutineCreate 验证协程创建被阻止 func TestSandboxBlocksCoroutineCreate(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() // 测试直接调用被阻止 coro1, err := engine.NewCoroutine(nil) require.NoError(t, err) err = coro1.SetupSandbox() require.NoError(t, err) err = coro1.Execute(`coroutine.create(function() end)`) assert.Error(t, err) assert.Contains(t, err.Error(), "blocked") coro1.Close() // 测试通过 _G 访问被阻止(需要新协程,因为前一个已 dead) coro2, err := engine.NewCoroutine(nil) require.NoError(t, err) err = coro2.SetupSandbox() require.NoError(t, err) err = coro2.Execute(`_G.coroutine.create(function() end)`) assert.Error(t, err) assert.Contains(t, err.Error(), "blocked") coro2.Close() } // TestGlobalCoroutineBypassAttempt 验证 _G.coroutine 绕过尝试失败 func TestGlobalCoroutineBypassAttempt(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() // 测试 _G.coroutine.create 绕过 coro1, err := engine.NewCoroutine(nil) require.NoError(t, err) err = coro1.SetupSandbox() require.NoError(t, err) err = coro1.Execute(`_G.coroutine.create(function() end)`) assert.Error(t, err) assert.Contains(t, err.Error(), "blocked") coro1.Close() // 测试字符串索引绕过 coro2, err := engine.NewCoroutine(nil) require.NoError(t, err) err = coro2.SetupSandbox() require.NoError(t, err) err = coro2.Execute(`local c = _G["coroutine"]; c.create(function() end)`) assert.Error(t, err) assert.Contains(t, err.Error(), "blocked") coro2.Close() } // TestSandboxBlocksCoroutineWrap 验证 coroutine.wrap 被阻止 func TestSandboxBlocksCoroutineWrap(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() coro, err := engine.NewCoroutine(nil) require.NoError(t, err) defer coro.Close() err = coro.SetupSandbox() require.NoError(t, err) err = coro.Execute(`coroutine.wrap(function() end)`) assert.Error(t, err) assert.Contains(t, err.Error(), "blocked") } // TestSandboxPreservesYield 验证 yield 正常工作 func TestSandboxPreservesYield(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() coro, err := engine.NewCoroutine(nil) require.NoError(t, err) defer coro.Close() err = coro.SetupSandbox() require.NoError(t, err) // 注意:使用 LState Pool 后,脚本直接执行而非通过 yield/resume 模式 // coroutine.yield 在主脚本中不能使用(只能在真正的协程中使用) // 测试 coroutine.yield 函数存在 err = coro.Execute(`local y = coroutine.yield; return type(y)`) assert.NoError(t, err) } // TestSandboxPreservesStatus 验证 status 可用 func TestSandboxPreservesStatus(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() coro, err := engine.NewCoroutine(nil) require.NoError(t, err) defer coro.Close() err = coro.SetupSandbox() require.NoError(t, err) // status 应该可用 err = coro.Execute(`local s = coroutine.status; return s`) assert.NoError(t, err) } // TestDebugLibraryNotLoaded 验证 debug 库未加载 func TestDebugLibraryNotLoaded(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() coro, err := engine.NewCoroutine(nil) require.NoError(t, err) defer coro.Close() // debug 库应该不存在 debug := engine.GetLStateForTest().GetGlobal("debug") assert.Equal(t, glua.LNil, debug, "debug library should not be loaded") // 尝试访问 debug.getregistry 应该失败 err = coro.Execute(`return debug.getregistry()`) assert.Error(t, err) } // TestCoroutineRunningBlocked 验证 coroutine.running 被阻止(防止信息泄露) func TestCoroutineRunningBlocked(t *testing.T) { engine, err := NewEngine(DefaultConfig()) require.NoError(t, err) defer engine.Close() coro, err := engine.NewCoroutine(nil) require.NoError(t, err) defer coro.Close() err = coro.SetupSandbox() require.NoError(t, err) err = coro.Execute(`coroutine.running()`) assert.Error(t, err) assert.Contains(t, err.Error(), "blocked") }