generated from VLADIMIR/template
up
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
package cudy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func clientRow(id, mac, band, signal string) string {
|
||||
return fmt.Sprintf(`<tr id="cbi-table-%s"><td><div id="cbi-table-%s-hostname"><p>PRIVATE NAME<br><span>%s</span></p></div></td><td><div id="cbi-table-%s-ipmac"><p>192.168.10.9<br>%s</p><p>192.168.10.9<br>%s</p></div></td><td><div id="cbi-table-%s-signal"><p>%s</p><p>%s</p></div></td></tr>`, id, id, band, id, mac, mac, id, signal, signal)
|
||||
}
|
||||
func clientTable(rows string) string {
|
||||
return `<table><thead><tr><th>IP / MAC Address</th></tr></thead><tbody>` + rows + `</tbody></table>`
|
||||
}
|
||||
|
||||
func TestParseClients(t *testing.T) {
|
||||
rows := clientRow("1", "AA:BB:CC:DD:EE:02", "2.4G WiFi", "-60 dBm") + clientRow("2", "AA:BB:CC:DD:EE:01", "5G WiFi", "—") + clientRow("3", "AA:BB:CC:DD:EE:03", "Ethernet", "—")
|
||||
clients, err := parseClients([]byte(clientTable(rows)))
|
||||
if err != nil || len(clients) != 2 {
|
||||
t.Fatalf("reading: %v, count %d", err, len(clients))
|
||||
}
|
||||
if clients[0].MAC != "AA:BB:CC:DD:EE:01" || clients[0].Signal != nil || clients[1].Signal == nil || *clients[1].Signal != -60 {
|
||||
t.Fatalf("unexpected snapshot: %+v", clients)
|
||||
}
|
||||
data, _ := json.Marshal(clients)
|
||||
if strings.Contains(string(data), "PRIVATE") {
|
||||
t.Fatal("hostname leaked")
|
||||
}
|
||||
for _, text := range []string{`<form>Login</form>`, clientTable(clientRow("1", "bad", "5G WiFi", "-60 dBm")), clientTable(strings.ReplaceAll(clientRow("1", "AA:BB:CC:DD:EE:01", "5G WiFi", "-60 dBm"), "cbi-table-", "changed-")), clientTable(clientRow("1", "AA:BB:CC:DD:EE:01", "5 GHz", "-60 dBm")), clientTable(clientRow("1", "AA:BB:CC:DD:EE:01", "", "-60 dBm")), clientTable(rows + clientRow("4", "AA:BB:CC:DD:EE:01", "5G WiFi", "-70 dBm"))} {
|
||||
if _, err := parseClients([]byte(text)); !errors.Is(err, ErrRead) {
|
||||
t.Fatalf("invalid DOM accepted: %v", err)
|
||||
}
|
||||
}
|
||||
clients, err = parseClients([]byte(clientTable("")))
|
||||
if err != nil || len(clients) != 0 {
|
||||
t.Fatal("empty table rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReaderSessionAndSafeErrors(t *testing.T) {
|
||||
sessions := 0
|
||||
expire := false
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/cgi-bin/luci/", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "POST" {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
first := sha256.Sum256([]byte("private-password" + "salt"))
|
||||
want := sha256.Sum256([]byte(hex.EncodeToString(first[:]) + "token"))
|
||||
if r.Form.Get("luci_password") != hex.EncodeToString(want[:]) || r.Form.Get("_csrf") != "csrf" || r.Form.Get("luci_language") != "en" {
|
||||
t.Error("incorrect login")
|
||||
}
|
||||
sessions++
|
||||
http.SetCookie(w, &http.Cookie{Name: "sysauth", Value: "session", Path: "/"})
|
||||
fmt.Fprint(w, "authenticated")
|
||||
return
|
||||
}
|
||||
w.WriteHeader(403)
|
||||
fmt.Fprint(w, `<form><input type="hidden" name="salt" value="salt"><input type="hidden" name="_csrf" value="csrf"></form>`)
|
||||
})
|
||||
mux.HandleFunc("/cgi-bin/luci/admin/get_token", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
t.Error("token method")
|
||||
}
|
||||
fmt.Fprint(w, "token")
|
||||
})
|
||||
mux.HandleFunc("/cgi-bin/luci/admin/network/devices/devlist", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" || r.URL.RawQuery != "detail=1" {
|
||||
t.Error("not a read-only request")
|
||||
}
|
||||
if _, err := r.Cookie("sysauth"); err != nil {
|
||||
t.Error("session cookie missing")
|
||||
}
|
||||
if expire {
|
||||
expire = false
|
||||
w.WriteHeader(403)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(w, clientTable(clientRow("1", "AA:BB:CC:DD:EE:01", "5G WiFi", "-55 dBm")))
|
||||
})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
reader, err := NewReader(server.URL, "private-password")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
if _, err := reader.Read(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if sessions != 1 {
|
||||
t.Fatal("session not reused")
|
||||
}
|
||||
expire = true
|
||||
if _, err := reader.Read(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if sessions != 2 {
|
||||
t.Fatal("expired session not refreshed once")
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
if _, err := reader.Read(ctx); !errors.Is(err, context.Canceled) {
|
||||
t.Fatal("cancellation lost")
|
||||
}
|
||||
for _, base := range []string{"https://example.com", "http://user:secret@192.168.10.1", "http://192.168.10.1/path", "http://8.8.8.8"} {
|
||||
if _, err := NewReader(base, "secret"); !errors.Is(err, ErrRead) {
|
||||
t.Fatal("untrusted URL accepted")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user