Files
ingress/pkg/plugins/middlewareyaegi_test.go
zeekay 6840de13c3 refactor(deps): require yaegi and grpc-web at their own module paths
Standing fork rule: a hanzoai fork declares its OWN module path and is
required directly -- never resolved via `replace` against the upstream
path. Both of these violated it, and that is precisely why the upstream
brand was still printed on every import line in pkg/plugins and
pkg/middlewares/grpcweb.

  github.com/traefik/yaegi    v0.16.1 -> github.com/hanzoai/yaegi    v0.16.2
  github.com/traefik/grpc-web v0.16.0 -> github.com/hanzoai/grpc-web v0.16.1

The forks now declare those module paths themselves (hanzoai/yaegi
c54ababa, hanzoai/grpc-web 7f097a6), so both `replace` directives are
deleted rather than repointed. Resolution is unchanged -- same source,
same code, one patch bump each to carry the new module line.

go.sum gains only the four new hashes; nothing else is rewritten, so the
pre-existing luxfi/zap checksum entry is untouched.

Verified: go build over the non-webui package set, plus ./pkg/plugins/...
and ./pkg/middlewares/grpcweb/... green.
2026-07-25 12:51:12 -07:00

149 lines
4.4 KiB
Go

package plugins
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/hanzoai/yaegi/interp"
)
// TestNewInterpreter_SyscallErrorCase - Tests the security gate logic
func TestNewInterpreter_SyscallErrorCase(t *testing.T) {
manifest := &Manifest{
Import: "does-not-matter-will-error-before-import",
UseUnsafe: true, // Plugin wants unsafe access
}
settings := Settings{
UseUnsafe: false, // But admin doesn't allow it
}
ctx := t.Context()
_, err := newInterpreter(ctx, "/tmp", manifest, settings)
// This proves our security gate logic works
require.Error(t, err)
assert.Contains(t, err.Error(), "restricted imports", "Our error message should be returned")
}
// TestNewYaegiMiddlewareBuilder_WithSyscallSupport - Tests the ACTUAL production code!
func TestNewYaegiMiddlewareBuilder_WithSyscallSupport(t *testing.T) {
tests := []struct {
name string
pluginType string
manifestUnsafe bool
settingsUnsafe bool
shouldSucceed bool
expectedError string
}{
{
name: "Should work with safe plugin when useUnsafe disabled",
pluginType: "safe",
manifestUnsafe: false,
settingsUnsafe: false,
shouldSucceed: true,
},
{
name: "Should work with unsafe-only plugin when useUnsafe enabled",
pluginType: "unsafe-only",
manifestUnsafe: true,
settingsUnsafe: true,
shouldSucceed: true,
},
{
name: "Should work with unsafe+syscall plugin when useUnsafe enabled",
pluginType: "unsafe+syscall",
manifestUnsafe: true,
settingsUnsafe: true,
shouldSucceed: true,
},
{
name: "Should fail when plugin needs unsafe but setting disabled",
pluginType: "unsafe-only",
manifestUnsafe: true,
settingsUnsafe: false,
shouldSucceed: false,
expectedError: "restricted imports",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
ctx := t.Context()
// Set GOPATH to include our fixtures directory
goPath := "fixtures"
// Create interpreter using our ACTUAL newInterpreter function
// This will automatically import the real test plugin!
interpreter, err := createInterpreterForTesting(ctx, goPath, tc.pluginType, tc.manifestUnsafe, tc.settingsUnsafe)
if tc.shouldSucceed {
require.NoError(t, err)
require.NotNil(t, interpreter)
// Test actual middleware building using newYaegiMiddlewareBuilder
// The plugin is already loaded by newInterpreter!
basePkg := getPluginPackage(tc.pluginType)
builder, err := newYaegiMiddlewareBuilder(interpreter, basePkg, basePkg)
require.NoError(t, err)
require.NotNil(t, builder)
// Verify that unsafe/syscall functions actually work if the plugin uses them
if tc.pluginType != "safe" {
verifyMiddlewareWorks(t, builder)
}
} else {
assert.Error(t, err)
assert.Contains(t, err.Error(), tc.expectedError)
}
})
}
}
// Helper that uses the ACTUAL newInterpreter function with real test plugins
func createInterpreterForTesting(ctx context.Context, goPath, pluginType string, manifestUnsafe, settingsUnsafe bool) (*interp.Interpreter, error) {
pluginImport := getPluginPackage(pluginType)
manifest := &Manifest{
Import: pluginImport,
UseUnsafe: manifestUnsafe,
}
settings := Settings{
UseUnsafe: settingsUnsafe,
}
// Call the ACTUAL production newInterpreter function - no workarounds needed!
return newInterpreter(ctx, goPath, manifest, settings)
}
// Helper to get the correct plugin package name based on type
func getPluginPackage(pluginType string) string {
switch pluginType {
case "safe":
return "testpluginsafe"
case "unsafe-only":
return "testpluginunsafe"
case "unsafe+syscall":
return "testpluginsyscall"
default:
return "testpluginsafe"
}
}
// Helper to verify that unsafe/syscall functions actually work by invoking the middleware
func verifyMiddlewareWorks(t *testing.T, builder *yaegiMiddlewareBuilder) {
t.Helper()
// Create a middleware instance - this will call the plugin's New() function
// which uses unsafe/syscall, proving they work
middleware, err := builder.newMiddleware(map[string]any{
"message": "test",
}, "test-middleware")
require.NoError(t, err, "Should be able to create middleware that uses unsafe/syscall")
require.NotNil(t, middleware, "Middleware should not be nil")
// The fact that we got here without crashing proves unsafe/syscall work!
}