diff --git a/test/review/address_findings_test.go b/test/review/address_findings_test.go new file mode 100644 index 00000000..e304e118 --- /dev/null +++ b/test/review/address_findings_test.go @@ -0,0 +1,19 @@ +package address + +import "testing" + +// TestAuditEmailAddressValidationMustRejectMalformedAddresses 验证转发地址校验拒绝明显非法输入。 +func TestAuditEmailAddressValidationMustRejectMalformedAddresses(t *testing.T) { + invalid := []string{ + "@example.com", + "user@", + "user..name@example.com", + "user@example..com", + "user name@example.com", + } + for _, address := range invalid { + if IsValidEmailAddress(address) { + t.Errorf("非法邮箱地址不应通过校验: %q", address) + } + } +} diff --git a/test/review/async_findings_test.go b/test/review/async_findings_test.go new file mode 100644 index 00000000..e4699d5e --- /dev/null +++ b/test/review/async_findings_test.go @@ -0,0 +1,36 @@ +package async + +import ( + "sync" + "testing" + "time" +) + +// TestAuditAsyncLastErrorMustBeSafeForConcurrentAccess 验证异步错误状态的并发访问安全性。 +func TestAuditAsyncLastErrorMustBeSafeForConcurrentAccess(t *testing.T) { + process := New(nil) + const workers = 32 + const rounds = 200 + + var started sync.WaitGroup + started.Add(workers) + for i := 0; i < workers; i++ { + go func() { + started.Done() + for round := 0; round < rounds; round++ { + process.Process(func(any) { panic("audit panic") }, nil) + _ = process.LastError() + } + }() + } + started.Wait() + + deadline := time.Now().Add(2 * time.Second) + for time.Now().Before(deadline) { + if process.LastError() != nil { + return + } + time.Sleep(time.Millisecond) + } + t.Fatal("异步任务未记录 panic 错误") +} diff --git a/test/review/run.mjs b/test/review/run.mjs index 88c45fa9..ccf2a2f3 100644 --- a/test/review/run.mjs +++ b/test/review/run.mjs @@ -9,6 +9,8 @@ const directory = path.join(root, 'test-log', `audit-${stamp}-${process.pid}`) await mkdir(directory, { recursive: true }) const replacements = { 'backend/hooks/framework/audit_findings_test.go': 'test/review/plugin_framework_findings_test.go', + 'backend/utils/async/audit_findings_test.go': 'test/review/async_findings_test.go', + 'backend/utils/address/audit_findings_test.go': 'test/review/address_findings_test.go', 'backend/services/totp/audit_findings_test.go': 'test/review/totp_findings_test.go', 'backend/services/setup/ssl/audit_findings_test.go': 'test/review/ssl_findings_test.go', 'backend/services/setup/audit_findings_test.go': 'test/review/setup_findings_test.go', @@ -44,14 +46,16 @@ const setupOnly = process.argv.includes('--setup-only') const sslOnly = process.argv.includes('--ssl-only') const totpOnly = process.argv.includes('--totp-only') const frameworkOnly = process.argv.includes('--framework-only') -const packages = frameworkOnly ? ['./hooks/framework'] : totpOnly ? ['./services/totp'] : sslOnly ? ['./services/setup/ssl'] : setupOnly ? ['./services/setup'] : httpOnly ? ['./listen/http_server'] : controllersOnly ? ['./controllers'] : configOnly ? ['./config'] : migrationOnly ? ['./services/dbmigration'] : pop3Only ? ['./internal/gopop', './listen/pop3_server'] : messageOnly ? [ +const asyncOnly = process.argv.includes('--async-only') +const addressOnly = process.argv.includes('--address-only') +const packages = frameworkOnly ? ['./hooks/framework'] : asyncOnly ? ['./utils/async'] : addressOnly ? ['./utils/address'] : totpOnly ? ['./services/totp'] : sslOnly ? ['./services/setup/ssl'] : setupOnly ? ['./services/setup'] : httpOnly ? ['./listen/http_server'] : controllersOnly ? ['./controllers'] : configOnly ? ['./config'] : migrationOnly ? ['./services/dbmigration'] : pop3Only ? ['./internal/gopop', './listen/pop3_server'] : messageOnly ? [ './dto/parsemail', './listen/smtp_server' ] : [ './services/dbmigration', './listen/http_server', './services/list', './services/recovery', './listen/imap_server', './controllers', './internal/gopop', './listen/pop3_server', './dto/parsemail', './listen/smtp_server', './config', './services/setup', './services/setup/ssl', './services/totp', './hooks/framework' ] -const extra = process.argv.slice(2).filter(argument => !['--pop3-only', '--message-only', '--migration-only', '--config-only', '--controllers-only', '--http-only', '--setup-only', '--ssl-only', '--totp-only', '--framework-only'].includes(argument)) +const extra = process.argv.slice(2).filter(argument => !['--pop3-only', '--message-only', '--migration-only', '--config-only', '--controllers-only', '--http-only', '--setup-only', '--ssl-only', '--totp-only', '--framework-only', '--async-only', '--address-only'].includes(argument)) const selection = extra.some(argument => argument === '-run' || argument.startsWith('-run=')) ? [] : ['-run=^TestAudit'] const child = spawn(process.execPath, [ path.join(root, 'test/run.mjs'), 'backend', `-overlay=${overlay}`,