miguan v1
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
package honeypot
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
h := New(Options{Enabled: true})
|
||||
|
||||
hit := []string{
|
||||
"/wp-admin", "/wp-admin/install.php", "/wp-login.php", "/wp-content/plugins/",
|
||||
"/.ssh/id_rsa", "/.env", "/.git/config", "/.aws/credentials",
|
||||
"/administrator", "/phpmyadmin", "/actuator/env", "/swagger-ui.html",
|
||||
"/xmlrpc.php", "/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||||
"/wp-admin/../wp-content/x.php",
|
||||
}
|
||||
for _, p := range hit {
|
||||
if !h.Match(p) {
|
||||
t.Errorf("Match(%q) = false, want true", p)
|
||||
}
|
||||
}
|
||||
|
||||
// 业务路由必须一律不接管 —— 这是蜜罐的红线。
|
||||
miss := []string{
|
||||
"/", "/login", "/register", "/logout", "/_/alice", "/user", "/user/settings",
|
||||
"/api/v1/questions", "/static/css/base.css", "/favicon.ico", "/robots.txt",
|
||||
"/about", "/rules", "/sponsor", "/change-logs.html", "/vote/abc",
|
||||
"/explore", "/i18n/en",
|
||||
}
|
||||
for _, p := range miss {
|
||||
if h.Match(p) {
|
||||
t.Errorf("Match(%q) = true, want false(业务路由不得被蜜罐接管)", p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchDisabled(t *testing.T) {
|
||||
h := New(Options{Enabled: false})
|
||||
if h.Match("/wp-admin") {
|
||||
t.Error("蜜罐未启用时不应匹配任何路径")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchEncoded(t *testing.T) {
|
||||
h := New(Options{Enabled: true})
|
||||
// %2f 编码绕过
|
||||
if !h.Match("/wp-admin%2f..%2fetc") {
|
||||
t.Error("编码后的 wp-admin 路径应被匹配")
|
||||
}
|
||||
// 大小写
|
||||
if !h.Match("/WP-ADMIN") {
|
||||
t.Error("大小写不同的 WP-ADMIN 应被匹配")
|
||||
}
|
||||
// 查询串不应影响判定
|
||||
if !h.Match("/wp-login.php?redirect_to=/wp-admin") {
|
||||
t.Error("带查询串的 wp-login.php 应被匹配")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtraPaths(t *testing.T) {
|
||||
h := New(Options{Enabled: true, ExtraPaths: []string{"/secret-backdoor"}})
|
||||
if !h.Match("/secret-backdoor") {
|
||||
t.Error("自定义诱饵路径应被匹配")
|
||||
}
|
||||
if !h.Match("/secret-backdoor/x.php") {
|
||||
t.Error("自定义诱饵路径的子路径应被匹配")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractCredentialsForm(t *testing.T) {
|
||||
h := New(Options{Enabled: true})
|
||||
req := httptest.NewRequest(http.MethodPost, "/wp-login.php",
|
||||
strings.NewReader("log=admin&pwd=admin123&redirect_to=%2Fwp-admin%2F&wp-submit=Log+In"))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
creds := h.ExtractCredentials(req)
|
||||
if creds.Username != "admin" {
|
||||
t.Errorf("Username = %q, want admin", creds.Username)
|
||||
}
|
||||
if creds.Password != "admin123" {
|
||||
t.Errorf("Password = %q, want admin123", creds.Password)
|
||||
}
|
||||
if !creds.HadFields {
|
||||
t.Error("HadFields = false, want true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractCredentialsJSON(t *testing.T) {
|
||||
h := New(Options{Enabled: true})
|
||||
req := httptest.NewRequest(http.MethodPost, "/wp-login.php",
|
||||
strings.NewReader(`{"username":"root","password":"toor","extra":1}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
creds := h.ExtractCredentials(req)
|
||||
if creds.Username != "root" {
|
||||
t.Errorf("Username = %q, want root", creds.Username)
|
||||
}
|
||||
if creds.Password != "toor" {
|
||||
t.Errorf("Password = %q, want toor", creds.Password)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractCredentialsQuery(t *testing.T) {
|
||||
h := New(Options{Enabled: true})
|
||||
req := httptest.NewRequest(http.MethodGet, "/wp-login.php?log=admin&pwd=secret", nil)
|
||||
creds := h.ExtractCredentials(req)
|
||||
if creds.Username != "admin" || creds.Password != "secret" {
|
||||
t.Errorf("got %q/%q, want admin/secret", creds.Username, creds.Password)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractCredentialsBasicAuth(t *testing.T) {
|
||||
h := New(Options{Enabled: true})
|
||||
req := httptest.NewRequest(http.MethodGet, "/wp-admin", nil)
|
||||
req.SetBasicAuth("admin", "hunter2")
|
||||
creds := h.ExtractCredentials(req)
|
||||
if creds.Username != "admin" || creds.Password != "hunter2" {
|
||||
t.Errorf("got %q/%q, want admin/hunter2", creds.Username, creds.Password)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractCredentialsEmpty(t *testing.T) {
|
||||
h := New(Options{Enabled: true})
|
||||
req := httptest.NewRequest(http.MethodGet, "/wp-admin", nil)
|
||||
creds := h.ExtractCredentials(req)
|
||||
if creds.HadFields {
|
||||
t.Error("空请求不应标记为携带凭据")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractCredentialsMalformed(t *testing.T) {
|
||||
h := New(Options{Enabled: true})
|
||||
// 畸形 body 不应 panic
|
||||
req := httptest.NewRequest(http.MethodPost, "/wp-login.php",
|
||||
strings.NewReader("%zz&log=admin&&=="))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
creds := h.ExtractCredentials(req)
|
||||
if creds.Username != "admin" {
|
||||
t.Errorf("Username = %q, want admin(畸形 body 仍应尽量提取)", creds.Username)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user