package console import "testing" // 分发桶是公共读、直接挂在 dl.ymaikj.com 上的,key 拼错就等于把它变成公开网盘, // 所以每条边界都钉在测试里。 func TestBuildDistObjectKey(t *testing.T) { ok := map[string][3]string{ "正常": {"EAIMAR", "android", "eaimar-release-1.0.2-20260923.apk"}, "目录带斜杠": {"EAIMAR", "/android/", "a.apk"}, "文件名夹带目录": {"EAIMAR", "android", "../../etc/passwd"}, "另一个应用": {"deepGlass", "android", "a.apk"}, "应用名带斜杠": {"a/../b", "android", "a.apk"}, } want := map[string]string{ "正常": "android/EAIMAR/eaimar-release-1.0.2-20260923.apk", "目录带斜杠": "android/EAIMAR/a.apk", "文件名夹带目录": "android/EAIMAR/passwd", "另一个应用": "android/deepGlass/a.apk", "应用名带斜杠": "android/a____b/a.apk", } for name, in := range ok { got, err := buildDistObjectKey(in[0], in[1], in[2]) if err != nil { t.Errorf("%s: 本该通过,却报错 %v", name, err) continue } if got != want[name] { t.Errorf("%s: 期望 %q,得到 %q", name, want[name], got) } } bad := map[string][3]string{ "没选应用": {"", "android", "a.apk"}, "应用名全是非法字符": {"//", "android", "a.apk"}, "目录为空": {"EAIMAR", "", "a.apk"}, "目录不在白名单": {"EAIMAR", "anything", "a.apk"}, "用 .. 跳出去": {"EAIMAR", "android/../secret", "a.apk"}, "绝对路径跳根": {"EAIMAR", "/../../", "a.apk"}, "文件名为空": {"EAIMAR", "android", " "}, } for name, in := range bad { if got, err := buildDistObjectKey(in[0], in[1], in[2]); err == nil { t.Errorf("%s: 本该被拒,却得到 %q", name, got) } } } // apk 的 Content-Type 必须由服务端强制:它计入预签名,签错了要等真机装包才发现。 func TestBuildObjectKey(t *testing.T) { ok := map[string][3]string{ "固件": {"deepGlass", "product/47632/", "fw.bin"}, "产品图": {"deepGlass", "product/47632/images/", "a.png"}, "未保存的产品图": {"EAIMAR", "product/images/", "a.png"}, "目录带斜杠": {"deepGlass", "/product/47632/", "fw.bin"}, "文件名夹带目录": {"deepGlass", "product/47632/", "../../etc/passwd"}, "应用名带斜杠": {"a/../b", "product/1/", "fw.bin"}, } want := map[string]string{ "固件": "deepGlass/product/47632/fw.bin", "产品图": "deepGlass/product/47632/images/a.png", "未保存的产品图": "EAIMAR/product/images/a.png", "目录带斜杠": "deepGlass/product/47632/fw.bin", "文件名夹带目录": "deepGlass/product/47632/passwd", "应用名带斜杠": "a____b/product/1/fw.bin", } for name, in := range ok { got, err := buildObjectKey(in[0], in[1], in[2]) if err != nil { t.Errorf("%s: 本该通过,却报错 %v", name, err) continue } if got != want[name] { t.Errorf("%s: 期望 %q,得到 %q", name, want[name], got) } } if _, err := buildObjectKey("", "product/1/", "a.bin"); err == nil { t.Error("没指定应用本该被拒") } // .. 会被 path.Clean 吃掉,文件仍留在应用目录下,不会逃到桶根。 got, err := buildObjectKey("deepGlass", "../secret", "a.bin") if err != nil || got != "deepGlass/secret/a.bin" { t.Fatalf("目录里的 .. 应被折回应用目录下,得到 %q err=%v", got, err) } } func TestParseMainUploadDir(t *testing.T) { dir, id, err := parseMainUploadDir("product/47632/") if err != nil || dir != "product/47632" || id != 47632 { t.Fatalf("固件目录: dir=%q id=%d err=%v", dir, id, err) } dir, id, err = parseMainUploadDir("product/47632/images/") if err != nil || dir != "product/47632/images" || id != 47632 { t.Fatalf("产品图目录: dir=%q id=%d err=%v", dir, id, err) } dir, id, err = parseMainUploadDir("product/images/") if err != nil || dir != "product/images" || id != 0 { t.Fatalf("未保存产品图: dir=%q id=%d err=%v", dir, id, err) } if _, _, err = parseMainUploadDir("android"); err == nil { t.Fatal("非 product 目录本该被拒") } if _, _, err = parseMainUploadDir("product/0/"); err == nil { t.Fatal("产品 id 为 0 本该被拒") } } func TestPickBoundApp(t *testing.T) { got, err := pickBoundApp([]string{"deepGlass"}, "") if err != nil || got != "deepGlass" { t.Fatalf("只绑一个: got=%q err=%v", got, err) } got, err = pickBoundApp([]string{"EAIMAR", "deepGlass"}, "deepGlass") if err != nil || got != "deepGlass" { t.Fatalf("点名已绑定的应用: got=%q err=%v", got, err) } if _, err = pickBoundApp([]string{"deepGlass"}, "EAIMAR"); err == nil { t.Fatal("点名未绑定的应用本该被拒") } if _, err = pickBoundApp(nil, ""); err == nil { t.Fatal("未绑定本该被拒") } if _, err = pickBoundApp([]string{"EAIMAR", "deepGlass"}, ""); err == nil { t.Fatal("绑了多个却不指定本该被拒") } } func TestDistContentTypeForced(t *testing.T) { if distContentTypes[".apk"] != "application/vnd.android.package-archive" { t.Fatalf("apk 的 Content-Type 不对: %q", distContentTypes[".apk"]) } }