🐐 Several security fixes

This commit is contained in:
2026-07-29 16:30:14 +02:00
parent b42abe5f5e
commit c56498239f
21 changed files with 660 additions and 204 deletions
+14 -3
View File
@@ -52,7 +52,8 @@ class BuySpecificCardRequest(BaseModel):
@router.post("/shards/shatter")
def shatter_cards(req: ShatterRequest, user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)):
@limiter.limit("30/minute")
def shatter_cards(request: Request, req: ShatterRequest, user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)):
if not req.card_ids:
raise HTTPException(status_code=400, detail="No cards selected")
try:
@@ -79,7 +80,9 @@ def shatter_cards(req: ShatterRequest, user: UserModel = Depends(get_current_use
@router.post("/store/stripe/checkout")
def create_stripe_checkout(req: StripeCheckoutRequest, user: UserModel = Depends(get_current_user)):
# Each call creates a Stripe session, so this is spend against our Stripe account.
@limiter.limit("10/minute")
def create_stripe_checkout(request: Request, req: StripeCheckoutRequest, user: UserModel = Depends(get_current_user)):
package = SHARD_PACKAGES.get(req.package_id)
if not package:
raise HTTPException(status_code=400, detail="Invalid package")
@@ -122,6 +125,13 @@ async def stripe_webhook(request: Request, db: Session = Depends(get_db)):
if event["type"] == "checkout.session.completed":
data = event["data"]["object"]
# checkout.session.completed also fires for asynchronous payment methods, where
# payment_status is "unpaid" until the funds actually settle. Sessions are created
# card-only today so that can't happen yet, but crediting shards for money that
# may never arrive is not something to leave resting on a Stripe dashboard toggle.
if data.get("payment_status") != "paid":
db.commit()
return {"ok": True}
user_id = data.get("metadata", {}).get("user_id")
shards = data.get("metadata", {}).get("shards")
if user_id and shards:
@@ -177,7 +187,8 @@ async def buy_specific_card(request: Request, req: BuySpecificCardRequest, user:
@router.post("/store/buy")
def store_buy(req: StoreBuyRequest, user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)):
@limiter.limit("30/minute")
def store_buy(request: Request, req: StoreBuyRequest, user: UserModel = Depends(get_current_user), db: Session = Depends(get_db)):
cost = STORE_PACKAGES.get(req.quantity)
if cost is None:
raise HTTPException(status_code=400, detail="Invalid package")