安全审计 #21/#22(security_todo.md P2): - #21 DoSend 配额补偿收敛为 defer + persistedBytes 统一对账: 预扣总量与实际落库量的差额在任何退出路径退还,覆盖外发入队 失败/本地投递失败/Sent 副本保存失败三条提前 return 的泄漏路径; 移除文件读取与附件保存失败的分散回退 - #22 OAuth2 仅接受已验证邮箱:GitHub /user/emails 过滤 verified==true(主邮箱优先,其次任一已验证);Google email_verified 为 false 或缺失时拒绝(fail closed);解析逻辑 抽为 parseGitHubEmail/parseGoogleEmail 纯函数,不依赖 IdP 当前只返回已验证邮箱的实现细节 - 新增 8 项单测(DoSend 回退/不误退、GitHub/Google 邮箱解析矩阵) - security_todo.md 勾选 #21/#22
73 lines
2.3 KiB
Go
73 lines
2.3 KiB
Go
package auth
|
|
|
|
// OAuth2 邮箱解析回归测试(#22):仅接受已验证邮箱——未验证邮箱可能
|
|
// 不属于该账号所有者,放行会允许冒充既有用户登录(账号接管)。
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestParseGitHubEmailPrefersVerifiedPrimary(t *testing.T) {
|
|
body := []byte(`[{"email":"p@example.com","primary":true,"verified":true}]`)
|
|
email, err := parseGitHubEmail(body)
|
|
if err != nil {
|
|
t.Fatalf("parseGitHubEmail: %v", err)
|
|
}
|
|
if email != "p@example.com" {
|
|
t.Fatalf("email = %q, want p@example.com", email)
|
|
}
|
|
}
|
|
|
|
func TestParseGitHubEmailSkipsUnverifiedPrimary(t *testing.T) {
|
|
// 主邮箱未验证:不能用它登录,回退到任一已验证邮箱
|
|
body := []byte(`[
|
|
{"email":"unverified@example.com","primary":true,"verified":false},
|
|
{"email":"verified@example.com","primary":false,"verified":true}
|
|
]`)
|
|
email, err := parseGitHubEmail(body)
|
|
if err != nil {
|
|
t.Fatalf("parseGitHubEmail: %v", err)
|
|
}
|
|
if email != "verified@example.com" {
|
|
t.Fatalf("email = %q, want verified@example.com", email)
|
|
}
|
|
}
|
|
|
|
func TestParseGitHubEmailRejectsAllUnverified(t *testing.T) {
|
|
body := []byte(`[
|
|
{"email":"a@example.com","primary":true,"verified":false},
|
|
{"email":"b@example.com","primary":false,"verified":false}
|
|
]`)
|
|
_, err := parseGitHubEmail(body)
|
|
if err == nil || !strings.Contains(err.Error(), "已验证") {
|
|
t.Fatalf("expected unverified-email rejection, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestParseGitHubEmailMalformedBody(t *testing.T) {
|
|
if _, err := parseGitHubEmail([]byte("not json")); err == nil {
|
|
t.Fatal("expected error for malformed body")
|
|
}
|
|
}
|
|
|
|
func TestParseGoogleEmailAcceptsVerified(t *testing.T) {
|
|
email, err := parseGoogleEmail([]byte(`{"email":"a@example.com","verified_email":true}`))
|
|
if err != nil {
|
|
t.Fatalf("parseGoogleEmail: %v", err)
|
|
}
|
|
if email != "a@example.com" {
|
|
t.Fatalf("email = %q, want a@example.com", email)
|
|
}
|
|
}
|
|
|
|
func TestParseGoogleEmailRejectsUnverified(t *testing.T) {
|
|
if _, err := parseGoogleEmail([]byte(`{"email":"a@example.com","verified_email":false}`)); err == nil {
|
|
t.Fatal("expected rejection for verified_email=false")
|
|
}
|
|
// verified_email 缺失按未验证处理(fail closed)
|
|
if _, err := parseGoogleEmail([]byte(`{"email":"a@example.com"}`)); err == nil {
|
|
t.Fatal("expected rejection when verified_email is missing")
|
|
}
|
|
}
|