mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-12 01:08:13 +03:00
196 lines
5.7 KiB
Go
196 lines
5.7 KiB
Go
package lua
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"context"
|
|
"encoding/base64"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
glua "github.com/yuin/gopher-lua"
|
|
)
|
|
|
|
func TestProgramStatesAreIndependent(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "state.lua")
|
|
if err := os.WriteFile(path, []byte("value = (value or 0) + 1"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
program, err := Compile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if program.proto.SourceName != path {
|
|
t.Fatalf("program source = %q, want %q", program.proto.SourceName, path)
|
|
}
|
|
first, err := program.NewState(context.Background(), nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer first.Close()
|
|
first.SetGlobal("value", glua.LNumber(42))
|
|
second, err := program.NewState(context.Background(), nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer second.Close()
|
|
if got := second.GetGlobal("value"); got != glua.LNumber(1) {
|
|
t.Fatalf("second state value = %v, want 1", got)
|
|
}
|
|
}
|
|
|
|
func TestProgramInitializationObservesCancellation(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "loop.lua")
|
|
if err := os.WriteFile(path, []byte("while true do end"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
program, err := Compile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
state, err := program.NewState(ctx, nil, nil)
|
|
if err == nil || state != nil {
|
|
if state != nil {
|
|
state.Close()
|
|
}
|
|
t.Fatalf("NewState with canceled context = %v, %v; want nil state and error", state, err)
|
|
}
|
|
}
|
|
|
|
func TestNewStateClosesFailedValidation(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "state.lua")
|
|
if err := os.WriteFile(path, []byte("value = 1"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
program, err := Compile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wantErr := errors.New("invalid script")
|
|
var checked *glua.LState
|
|
L, err := program.NewState(context.Background(), nil, func(L *glua.LState) error {
|
|
checked = L
|
|
return wantErr
|
|
})
|
|
if L != nil || !errors.Is(err, wantErr) || checked == nil || !checked.IsClosed() {
|
|
t.Fatalf("state = %v, error = %v, checked state closed = %t", L, err, checked != nil && checked.IsClosed())
|
|
}
|
|
}
|
|
|
|
func compressSource(t *testing.T, code string) []byte {
|
|
t.Helper()
|
|
var data bytes.Buffer
|
|
writer := gzip.NewWriter(&data)
|
|
if _, err := writer.Write([]byte(code)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := writer.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return data.Bytes()
|
|
}
|
|
|
|
func TestCompileInline(t *testing.T) {
|
|
const code = `value = "你好"`
|
|
for _, tc := range []struct {
|
|
name, script string
|
|
value glua.LValue
|
|
}{
|
|
{"base64", "base64:" + base64.StdEncoding.EncodeToString([]byte(code)), glua.LString("你好")},
|
|
{"base64+gzip", "base64+gzip:" + base64.StdEncoding.EncodeToString(compressSource(t, code)), glua.LString("你好")},
|
|
{"empty base64", "base64:", glua.LNil},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
program, err := Compile(tc.script)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if program.proto.SourceName != "[inline]" {
|
|
t.Fatalf("program source = %q, want [inline]", program.proto.SourceName)
|
|
}
|
|
state, err := program.NewState(context.Background(), nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer state.Close()
|
|
if got := state.GetGlobal("value"); got != tc.value {
|
|
t.Fatalf("value = %v, want %v", got, tc.value)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCompileRejectsInvalidInline(t *testing.T) {
|
|
corrupt := compressSource(t, "value = 1")
|
|
corrupt[len(corrupt)-8] ^= 0xff
|
|
for _, tc := range []struct {
|
|
name, script string
|
|
wantErr error
|
|
wantMessage string
|
|
}{
|
|
{"base64", "base64:%", base64.CorruptInputError(0), "failed to decode base64"},
|
|
{"gzip base64", "base64+gzip:%", base64.CorruptInputError(0), "failed to decode base64"},
|
|
{"gzip header", "base64+gzip:" + base64.StdEncoding.EncodeToString([]byte("value = 'uncompressed'")), gzip.ErrHeader, "failed to decompress gzip"},
|
|
{"gzip checksum", "base64+gzip:" + base64.StdEncoding.EncodeToString(corrupt), gzip.ErrChecksum, "failed to decompress gzip"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
program, err := Compile(tc.script)
|
|
if program != nil || !errors.Is(err, tc.wantErr) || !strings.Contains(err.Error(), tc.wantMessage) {
|
|
t.Fatalf("Compile = %v, %v; want nil program and %q error wrapping %v", program, err, tc.wantMessage, tc.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCompileInlineDiagnostics(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
code string
|
|
compileError bool
|
|
}{
|
|
{"function broken(", true},
|
|
{`error("initialization failed")`, false},
|
|
} {
|
|
program, err := Compile("base64:" + base64.StdEncoding.EncodeToString([]byte(tc.code)))
|
|
if tc.compileError {
|
|
if program != nil || err == nil {
|
|
t.Fatalf("Compile = %v, %v; want nil program and syntax error", program, err)
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
state, initErr := program.NewState(context.Background(), nil, nil)
|
|
if state != nil {
|
|
state.Close()
|
|
t.Fatal("NewState returned a state after failed initialization")
|
|
}
|
|
err = initErr
|
|
}
|
|
if err == nil || !strings.Contains(err.Error(), "[inline]") {
|
|
t.Fatalf("Lua error = %v, want [inline] diagnostic", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCompileRejectsReadFailure(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "unreadable.lua")
|
|
if err := os.WriteFile(path, []byte("value = 42"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Chmod(path, 0); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { os.Chmod(path, 0o600) })
|
|
if _, err := os.ReadFile(path); !errors.Is(err, os.ErrPermission) {
|
|
t.Skip("file read permissions are not enforced")
|
|
}
|
|
if program, err := Compile(path); program != nil || !errors.Is(err, os.ErrPermission) {
|
|
t.Fatalf("Compile = %v, %v; want nil program and permission error", program, err)
|
|
}
|
|
}
|