bug处理

This commit is contained in:
truewhile
2026-10-08 11:13:10 +08:00
parent 8bfdd75d47
commit 2b2dc697ce
14 changed files with 952 additions and 9 deletions
+152
View File
@@ -0,0 +1,152 @@
package rule
import (
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"sync/atomic"
"testing"
"golang.org/x/text/encoding/simplifiedchinese"
)
// 本文件:java 文件接口(cacheFile / downloadFile / readTxtFile / deleteFile)
// 的语义回归。对应 legado JsExtensions:
// - cacheFile(url) → 返回文件**文本内容**(不是路径);
// - downloadFile(url) → 返回相对缓存根目录的路径;
// - readTxtFile(path[, charset]) → 返回文本;
// - 只允许访问缓存目录内的文件。
func newFileTestRunner(cacheDir, baseURL string, hits *int32) *JSRunner {
return NewJSRunner(JSConfig{
CacheDir: cacheDir,
BaseURL: baseURL,
Fetch: func(req *Request) (string, string, int, error) {
if hits != nil {
atomic.AddInt32(hits, 1)
}
resp, err := http.Get(req.URL) //nolint:gosec // 测试内固定 httptest 地址
if err != nil {
return "", "", 0, err
}
defer resp.Body.Close()
b, _ := io.ReadAll(resp.Body)
return string(b), req.URL, resp.StatusCode, nil
},
})
}
func TestCacheFileReturnsTextContent(t *testing.T) {
var hits int32
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("第一章 开始\n第二章 继续"))
}))
defer srv.Close()
r := newFileTestRunner(t.TempDir(), srv.URL, &hits)
ar := NewAnalyzeRule()
v, err := r.Run(ar, "java.cacheFile('"+srv.URL+"/vol.txt')", nil, "")
if err != nil {
t.Fatalf("cacheFile 失败: %v", err)
}
if got := anyToString(v); got != "第一章 开始\n第二章 继续" {
t.Fatalf("cacheFile 应返回文本内容,实际 %q", got)
}
// 第二次应命中缓存,不再发起请求。
if _, err := r.Run(ar, "java.cacheFile('"+srv.URL+"/vol.txt')", nil, ""); err != nil {
t.Fatal(err)
}
if n := atomic.LoadInt32(&hits); n != 1 {
t.Fatalf("重复调用应命中缓存(请求次数=%d)", n)
}
}
func TestCacheFileDecodesGBK(t *testing.T) {
gbk, err := simplifiedchinese.GBK.NewEncoder().Bytes([]byte("中文内容测试"))
if err != nil {
t.Fatal(err)
}
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write(gbk)
}))
defer srv.Close()
r := newFileTestRunner(t.TempDir(), srv.URL, nil)
v, err := r.Run(NewAnalyzeRule(), "java.cacheFile('"+srv.URL+"/gbk.txt')", nil, "")
if err != nil {
t.Fatal(err)
}
if got := anyToString(v); got != "中文内容测试" {
t.Fatalf("GBK 文本未正确解码: %q", got)
}
}
func TestDownloadFileThenReadTxtFile(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("正文内容"))
}))
defer srv.Close()
cacheDir := t.TempDir()
r := newFileTestRunner(cacheDir, srv.URL, nil)
ar := NewAnalyzeRule()
rawPath, err := r.Run(ar, "java.downloadFile('"+srv.URL+"/a.txt')", nil, "")
if err != nil {
t.Fatal(err)
}
rel := anyToString(rawPath)
if !strings.HasPrefix(rel, "/") || !strings.HasSuffix(rel, ".txt") {
t.Fatalf("downloadFile 应返回相对缓存路径,实际 %q", rel)
}
if _, err := os.Stat(filepath.Join(cacheDir, "reader", "files", strings.TrimPrefix(rel, "/"))); err != nil {
t.Fatalf("文件未落到缓存目录: %v", err)
}
v, err := r.Run(ar, "java.readTxtFile('"+rel+"')", nil, "")
if err != nil {
t.Fatal(err)
}
if got := anyToString(v); got != "正文内容" {
t.Fatalf("readTxtFile = %q", got)
}
del, err := r.Run(ar, "String(java.deleteFile('"+rel+"'))", nil, "")
if err != nil {
t.Fatal(err)
}
if anyToString(del) != "true" {
t.Fatalf("deleteFile 应返回 true,实际 %q", anyToString(del))
}
}
func TestFileOpsRejectPathTraversal(t *testing.T) {
cacheDir := t.TempDir()
secret := filepath.Join(cacheDir, "secret.txt")
if err := os.WriteFile(secret, []byte("不该被读到"), 0o600); err != nil {
t.Fatal(err)
}
r := newFileTestRunner(cacheDir, "", nil)
ar := NewAnalyzeRule()
for _, p := range []string{"/../../secret.txt", "../secret.txt", "/../reader/files/../../secret.txt"} {
v, err := r.Run(ar, "java.readTxtFile('"+p+"')", nil, "")
if err != nil {
t.Fatalf("%s 不应报错: %v", p, err)
}
if got := anyToString(v); strings.Contains(got, "不该被读到") {
t.Fatalf("越权读到了缓存目录外的文件(%s)", p)
}
}
}
func TestFileOpsWithoutCacheDirFail(t *testing.T) {
r := NewJSRunner(JSConfig{})
_, err := r.Run(NewAnalyzeRule(), "java.cacheFile('https://example.com/a.txt')", nil, "")
if err == nil || !strings.Contains(err.Error(), "缓存目录") {
t.Fatalf("未配置缓存目录时应明确报错,实际 %v", err)
}
}