fix: security hardening, bug fixes, and PostgreSQL compatibility#843
Open
onesyue wants to merge 1 commit intocedar2025:masterfrom
Open
fix: security hardening, bug fixes, and PostgreSQL compatibility#843onesyue wants to merge 1 commit intocedar2025:masterfrom
onesyue wants to merge 1 commit intocedar2025:masterfrom
Conversation
Security:
- Validate filter/sort field names in admin controllers to prevent
SQL injection via user-controlled column names in orderBy/where
- Add ZipSlip protection in theme upload (reject paths with "..")
- Use hash_equals() for timing-safe server token comparison
- Remove hardcoded APP_KEY from .env.example
Bug fixes:
- Fix wrong constant: Order::STATUS_PROCESSING → TYPE_NEW_PURCHASE
in OrderService event dispatch (values happen to match, but
semantically incorrect)
- Fix where('plan_id', NULL) → whereNull('plan_id') — SQL
"col = NULL" never matches, must use "col IS NULL"
- Wrap CouponService::use() in DB::transaction() so lockForUpdate()
actually holds the lock (prevents coupon race condition)
- Wrap UserService::addBalance() in DB::transaction() for the same
reason (prevents balance race condition)
- Fix duplicate index creation in migration by checking existence
before adding
PostgreSQL compatibility:
- Replace where('banned', 0) with where('banned', false) and
where('show', 1) with where('show', true) across all files —
PostgreSQL boolean columns reject integer comparisons
af72f5f to
b98c06e
Compare
Author
|
@cedar2025 友情提醒:此 PR 已重新 rebase 至最新 master,并新增了今天发现的一处遗漏。 本次更新内容新增 - fn($q) => $q->where('is_admin', 1)
- ->when($isStaff, fn($q) => $q->orWhere('is_staff', 1))
+ fn($q) => $q->where('is_admin', true)
+ ->when($isStaff, fn($q) => $q->orWhere('is_staff', true))
本 PR 总共修复:
恳请审查 🙏 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A batch of security, correctness, and PostgreSQL compatibility fixes found during code audit.
Security Fixes
1. SQL injection via admin filter/sort field names
Admin table endpoints (
UserController,OrderController,CouponController,TicketController) accept user-controlledfilter[].id/sort[].idas column names.orderBy()column names are NOT parameterized by Laravel — an attacker with admin access could inject SQL expressions. AddedisValidFieldName()regex validation (/^[a-zA-Z_][a-zA-Z0-9_.]*$/) toQueryOperatorstrait.2. ZipSlip in theme upload (
ThemeService.php)$zip->extractTo()without validating entry paths allows path traversal via filenames like../../etc/cron.d/evil. Added..detection before extraction.3. Timing-safe token comparison (
Middleware/Server.php)Changed
$value !== admin_setting('server_token')tohash_equals()to prevent timing attacks on the server communication token.4. Hardcoded APP_KEY in
.env.exampleRemoved the pre-set
APP_KEYvalue. Deployments that skipphp artisan key:generatewould share a known encryption key.Bug Fixes
5. Wrong constant in order event dispatch (
OrderService.php:134)Both constants happen to equal
1, so no behavioral change — but semantically wrong and fragile.6.
where('plan_id', NULL)never matches (UserService.php)SQL
WHERE plan_id = NULLis always false — must useIS NULL. Changed towhereNull('plan_id'). Same fix forexpired_at.7. Coupon race condition (
CouponService.php)lockForUpdate()was called in the constructor (outside any transaction), so the lock was immediately released. Two concurrent requests could both readlimit_use = 1, both succeed, and over-consume the coupon. Moved the lock inside aDB::transaction()in theuse()method.8. Balance race condition (
UserService::addBalance())Same pattern —
lockForUpdate()without a wrapping transaction. Wrapped inDB::transaction().9. Duplicate index migration fails (
2025_01_15_000002)v2_stat_server.server_idandrecord_atindexes already exist from the initial migration. Running this migration on an existing database throws "duplicate index" errors. AddedSchema::hasIndex()check before each$table->index().PostgreSQL Compatibility
10. Boolean column comparisons (8 files)
PostgreSQL boolean columns reject integer comparisons (
WHERE banned = 0→ error). Changed allwhere('banned', 0)towhere('banned', false)andwhere('show', 1)towhere('show', true). These changes are backward-compatible with MySQL.Files Changed (20 files, +124 -73)
QueryOperators.php,UserController,OrderController,CouponController,TicketController,Server.phpmiddleware,ThemeService.php,.env.exampleOrderService.php,UserService.php,CouponService.php, migration fileKnowledgeController,OrderController,CheckTrafficExceeded,ResetTraffic,PlanObserver,ServerRouteObserver,ServerService,TrafficResetServiceTest Plan
column_name,relation.column, rejects; DROP TABLE,column--,1 OR 1=1true/falseworks on MySQL)🤖 Generated with Claude Code