93 lines
3.2 KiB
Go
93 lines
3.2 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"linkport/internal/cryptotool"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestNavicatDecryption(t *testing.T) {
|
|
f := newFixture(t)
|
|
path := "/api/v1/tools/navicat/decrypt"
|
|
body := map[string]any{"version": 12, "ciphertext": "f03a1418eb2928a948cc8c71d92edf1c"}
|
|
requireStatus(t, f.request("POST", path, body, false), 401)
|
|
f.login()
|
|
requireStatus(t, f.request("POST", path, body, false), 403)
|
|
w := f.request("POST", path, body, true)
|
|
requireStatus(t, w, 200)
|
|
if w.Header().Get("Cache-Control") != "no-store" {
|
|
t.Fatal("password response must not be cached")
|
|
}
|
|
var result struct{ Password string }
|
|
if err := json.Unmarshal(w.Body.Bytes(), &result); err != nil || result.Password != "密码🔑 test" {
|
|
t.Fatalf("unexpected decryption result: %s, %v", w.Body.String(), err)
|
|
}
|
|
w = f.request("GET", path, nil, false)
|
|
requireStatus(t, w, 405)
|
|
if w.Header().Get("Allow") != "POST" {
|
|
t.Fatal("missing allowed method")
|
|
}
|
|
for _, invalid := range []map[string]any{
|
|
{"version": 12, "ciphertext": "zz"},
|
|
{"version": 13, "ciphertext": "45"},
|
|
{"version": 12, "ciphertext": strings.Repeat("0", 70000)},
|
|
} {
|
|
requireStatus(t, f.request("POST", path, invalid, true), 400)
|
|
}
|
|
events, err := f.store.Events()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
raw, _ := json.Marshal(events)
|
|
if strings.Contains(string(raw), result.Password) || strings.Contains(string(raw), body["ciphertext"].(string)) {
|
|
t.Fatal("decryption data leaked to audit events")
|
|
}
|
|
}
|
|
|
|
func TestNavicatDecryptionRejectsBearer(t *testing.T) {
|
|
r := httptest.NewRequest("POST", "/api/v1/tools/navicat/decrypt", strings.NewReader(`{"version":11,"ciphertext":"45"}`))
|
|
r.Header.Set("Authorization", "Bearer test-token")
|
|
w := httptest.NewRecorder()
|
|
(&Server{}).decryptNavicat(w, r)
|
|
requireStatus(t, w, 403)
|
|
}
|
|
|
|
func TestCustomDecryption(t *testing.T) {
|
|
f := newFixture(t)
|
|
path := "/api/v1/tools/decrypt"
|
|
body := cryptotool.Options{Algorithm: "AES", Mode: "CBC", Padding: "PKCS7", Key: "libcckeylibcckey", KeyEncoding: "UTF8",
|
|
IV: "libcciv libcciv ", IVEncoding: "UTF8", InputEncoding: "Hex", OutputEncoding: "UTF8", Ciphertext: "f03a1418eb2928a948cc8c71d92edf1c"}
|
|
requireStatus(t, f.request("POST", path, body, false), 401)
|
|
f.login()
|
|
requireStatus(t, f.request("POST", path, body, false), 403)
|
|
w := f.request("POST", path, body, true)
|
|
requireStatus(t, w, 200)
|
|
var result map[string]string
|
|
if err := json.Unmarshal(w.Body.Bytes(), &result); err != nil || result["plaintext"] != "密码🔑 test" {
|
|
t.Fatalf("wrong result: %s, %v", w.Body.String(), err)
|
|
}
|
|
if w.Header().Get("Cache-Control") != "no-store" {
|
|
t.Fatal("decrypted text must not be cached")
|
|
}
|
|
requireStatus(t, f.request("GET", path, nil, false), 405)
|
|
body.IV = "libcciv libcciv"
|
|
requireStatus(t, f.request("POST", path, body, true), 400)
|
|
events, err := f.store.Events()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
raw, _ := json.Marshal(events)
|
|
for _, secret := range []string{body.Key, body.IV, body.Ciphertext, result["plaintext"]} {
|
|
if strings.Contains(string(raw), secret) {
|
|
t.Fatal("decryption material leaked to audit")
|
|
}
|
|
}
|
|
r := httptest.NewRequest("POST", path, nil)
|
|
r.Header.Set("Authorization", "Bearer test-token")
|
|
w = httptest.NewRecorder()
|
|
f.server.decryptCustom(w, r)
|
|
requireStatus(t, w, 403)
|
|
}
|