diff --git a/files/routes/awards.py b/files/routes/awards.py index 7cfa8eb94..151c76555 100644 --- a/files/routes/awards.py +++ b/files/routes/awards.py @@ -126,8 +126,8 @@ def buy(v:User, award): @app.post("/award//") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def award_thing(v, thing_type, id): kind = request.values.get("kind", "").strip() diff --git a/files/routes/chat.py b/files/routes/chat.py index dc64ea15b..f61f61236 100644 --- a/files/routes/chat.py +++ b/files/routes/chat.py @@ -47,8 +47,8 @@ def chat(v): @socketio.on('speak') @limiter.limit("3/second;10/minute") +@limiter.limit("3/second;10/minute", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @admin_level_required(PERMS['CHAT']) -@limiter.limit("3/second;10/minute", key_func=lambda:f'{SITE}-{g.v.id}') def speak(data, v): limiter.check() if v.is_banned: return '', 403 diff --git a/files/routes/comments.py b/files/routes/comments.py index c2fb7c1d1..a8eb80546 100644 --- a/files/routes/comments.py +++ b/files/routes/comments.py @@ -78,9 +78,9 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None, sub=None): return render_template(template, v=v, p=post, sort=sort, comment_info=comment_info, render_replies=True, sub=post.subr) @app.post("/comment") -@limiter.limit("1/day") +@limiter.limit("1/second;20/minute;200/hour;1000/day") +@limiter.limit("1/second;20/minute;200/hour;1000/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit("1/day", key_func=lambda:f'{SITE}-{g.v.id}') def comment(v:User): if v.is_suspended: abort(403, "You can't perform this action while banned.") @@ -362,8 +362,8 @@ def comment(v:User): @app.post("/edit_comment/") @limiter.limit("1/second;10/minute;100/hour;200/day") +@limiter.limit("1/second;10/minute;100/hour;200/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit("1/second;10/minute;100/hour;200/day", key_func=lambda:f'{SITE}-{g.v.id}') def edit_comment(cid, v): c = get_comment(cid, v=v) @@ -437,8 +437,8 @@ def edit_comment(cid, v): @app.post("/delete/comment/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def delete_comment(cid, v): if v.id == 253: abort(403) c = get_comment(cid, v=v) @@ -459,8 +459,8 @@ def delete_comment(cid, v): @app.post("/undelete/comment/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def undelete_comment(cid, v): c = get_comment(cid, v=v) if c.deleted_utc: @@ -523,8 +523,8 @@ def unpin_comment(cid, v): @app.post("/save_comment/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def save_comment(cid, v): comment=get_comment(cid) @@ -540,8 +540,8 @@ def save_comment(cid, v): @app.post("/unsave_comment/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def unsave_comment(cid, v): comment=get_comment(cid) @@ -576,8 +576,8 @@ def diff_words(answer, guess): @app.post("/wordle/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def handle_wordle_action(cid, v): comment = get_comment(cid) diff --git a/files/routes/login.py b/files/routes/login.py index 78982eb70..d05ab03af 100644 --- a/files/routes/login.py +++ b/files/routes/login.py @@ -135,8 +135,8 @@ def me(v:User): @app.post("/logout") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def logout(v): loggedin = cache.get(f'{SITE}_loggedin') or {} if session.get("lo_user") in loggedin: del loggedin[session["lo_user"]] diff --git a/files/routes/mail.py b/files/routes/mail.py index 6047dd7ed..20131df5c 100644 --- a/files/routes/mail.py +++ b/files/routes/mail.py @@ -10,8 +10,8 @@ from files.__main__ import app, limiter @app.post("/verify_email") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def verify_email(v): send_verification_email(v) return {"message": "Email has been sent (ETA ~5 minutes)"} diff --git a/files/routes/notifications.py b/files/routes/notifications.py index 0756737a4..0b754bd21 100644 --- a/files/routes/notifications.py +++ b/files/routes/notifications.py @@ -10,8 +10,8 @@ from files.routes.wrappers import * from files.__main__ import app @app.post("/clear") +@limiter.limit(DEFAULT_RATELIMIT, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT, key_func=lambda:f'{SITE}-{g.v.id}') def clear(v): notifs = g.db.query(Notification).join(Notification.comment).filter(Notification.read == False, Notification.user_id == v.id).all() for n in notifs: @@ -24,8 +24,8 @@ def clear(v): @app.get("/unread") +@limiter.limit(DEFAULT_RATELIMIT, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT, key_func=lambda:f'{SITE}-{g.v.id}') def unread(v): listing = g.db.query(Notification, Comment).join(Notification.comment).filter( Notification.read == False, diff --git a/files/routes/oauth.py b/files/routes/oauth.py index f9074b6e4..f411128d8 100644 --- a/files/routes/oauth.py +++ b/files/routes/oauth.py @@ -17,8 +17,8 @@ def authorize_prompt(v:User): @app.post("/authorize") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def authorize(v): client_id = request.values.get("client_id") application = g.db.query(OauthApp).filter_by(client_id=client_id).one_or_none() @@ -37,8 +37,8 @@ def authorize(v): @app.post("/rescind/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def rescind(v, aid): auth = g.db.query(ClientAuth).filter_by(oauth_client = aid, user_id = v.id).one_or_none() @@ -49,8 +49,8 @@ def rescind(v, aid): @app.post("/api_keys") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def request_api_keys(v): new_app = OauthApp( app_name=request.values.get('name').replace('<','').replace('>',''), @@ -88,8 +88,8 @@ def request_api_keys(v): @app.post("/delete_app/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def delete_oauth_app(v, aid): try: aid = int(aid) @@ -111,8 +111,8 @@ def delete_oauth_app(v, aid): @app.post("/edit_app/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def edit_oauth_app(v, aid): try: aid = int(aid) @@ -278,8 +278,8 @@ def admin_apps_list(v): @app.post("/reroll/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def reroll_oauth_tokens(aid, v): aid = aid diff --git a/files/routes/posts.py b/files/routes/posts.py index 23714d5ec..13dc5aa1d 100644 --- a/files/routes/posts.py +++ b/files/routes/posts.py @@ -35,8 +35,8 @@ titleheaders = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWe @app.post("/publish/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def publish(pid, v): post = get_post(pid) if not post.private: return {"message": "Post published!"} @@ -260,8 +260,8 @@ def more_comments(v, cid): @app.post("/edit_post/") @limiter.limit("1/second;10/minute;100/hour;200/day") +@limiter.limit("1/second;10/minute;100/hour;200/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit("1/second;10/minute;100/hour;200/day", key_func=lambda:f'{SITE}-{g.v.id}') def edit_post(pid, v): p = get_post(pid) if not v.can_edit(p): abort(403) @@ -835,8 +835,8 @@ def submit_post(v:User, sub=None): @app.post("/delete_post/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def delete_post_pid(pid, v): post = get_post(pid) if post.author_id != v.id: abort(403) @@ -862,8 +862,8 @@ def delete_post_pid(pid, v): @app.post("/undelete_post/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def undelete_post_pid(pid, v): post = get_post(pid) if post.author_id != v.id: abort(403) @@ -952,8 +952,8 @@ def unmark_post_nsfw(pid, v): @app.post("/save_post/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def save_post(pid, v): post=get_post(pid) @@ -968,8 +968,8 @@ def save_post(pid, v): @app.post("/unsave_post/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def unsave_post(pid, v): post=get_post(pid) @@ -1040,8 +1040,8 @@ extensions = IMAGE_FORMATS + VIDEO_FORMATS + AUDIO_FORMATS @app.get("/submit/title") @limiter.limit("3/minute") +@limiter.limit("3/minute", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit("3/minute", key_func=lambda:f'{SITE}-{g.v.id}') def get_post_title(v): POST_TITLE_TIMEOUT = 5 url = request.values.get("url") diff --git a/files/routes/reporting.py b/files/routes/reporting.py index 3a68ed353..1a2890c83 100644 --- a/files/routes/reporting.py +++ b/files/routes/reporting.py @@ -13,8 +13,8 @@ from files.__main__ import app, limiter, cache @app.post("/report/post/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def flag_post(pid, v): post = get_post(pid) reason = request.values.get("reason", "").strip() @@ -69,8 +69,8 @@ def flag_post(pid, v): @app.post("/report/comment/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def flag_comment(cid, v): comment = get_comment(cid) diff --git a/files/routes/settings.py b/files/routes/settings.py index a8efd8d3e..f83f7ee63 100644 --- a/files/routes/settings.py +++ b/files/routes/settings.py @@ -36,8 +36,8 @@ def settings_personal(v:User): @app.delete('/settings/background') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def remove_background(v): if v.background: if v.background.startswith('/images/'): @@ -48,8 +48,8 @@ def remove_background(v): @app.post('/settings/custom_background') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def upload_custom_background(v): if g.is_tor: abort(403, "Image uploads are not allowed through TOR.") @@ -72,8 +72,8 @@ def upload_custom_background(v): @app.post('/settings/profile_background') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def upload_profile_background(v): if g.is_tor: abort(403, "Image uploads are not allowed through TOR.") @@ -93,8 +93,8 @@ def upload_profile_background(v): @app.delete('/settings/profile_background') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def delete_profile_background(v): if v.profile_background: os.remove(v.profile_background) @@ -103,8 +103,8 @@ def delete_profile_background(v): @app.post("/settings/personal") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def settings_personal_post(v): if v.id == 253 and request.values.get("private"): abort(403) @@ -383,22 +383,22 @@ def set_color(v:User, attr:str, color:Optional[str]): @app.post("/settings/namecolor") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def namecolor(v): return set_color(v, "namecolor", request.values.get("namecolor")) @app.post("/settings/themecolor") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def themecolor(v): return set_color(v, "themecolor", request.values.get("themecolor")) @app.post("/settings/gumroad") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def gumroad(v): if GUMROAD_TOKEN == DEFAULT_CONFIG_VALUE: abort(404) if not (v.email and v.is_activated): @@ -434,23 +434,23 @@ def gumroad(v): @app.post("/settings/titlecolor") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def titlecolor(v): return set_color(v, "titlecolor", request.values.get("titlecolor")) @app.post("/settings/verifiedcolor") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def verifiedcolor(v): if not v.verified: abort(403, "You don't have a checkmark") return set_color(v, "verifiedcolor", request.values.get("verifiedcolor")) @app.post("/settings/security") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def settings_security_post(v): if request.values.get("new_password"): if request.values.get("new_password") != request.values.get("cnf_password"): @@ -523,8 +523,8 @@ def settings_security_post(v): @app.post("/settings/log_out_all_others") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def settings_log_out_others(v): submitted_password = request.values.get("password", "").strip() if not v.verifyPass(submitted_password): @@ -538,8 +538,8 @@ def settings_log_out_others(v): @app.post("/settings/images/profile") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def settings_images_profile(v): if g.is_tor: abort(403, "Image uploads are not allowed through TOR.") @@ -573,8 +573,8 @@ def settings_images_profile(v): @app.post("/settings/images/banner") @feature_required('USERS_PROFILE_BANNER') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def settings_images_banner(v): if g.is_tor: abort(403, "Image uploads are not allowed through TOR.") @@ -599,8 +599,8 @@ def settings_css_get(v:User): @app.post("/settings/css") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def settings_css(v): if v.agendaposter: abort(400, "Agendapostered users can't edit CSS!") css = request.values.get("css", v.css).strip().replace('\\', '').strip()[:CSS_LENGTH_LIMIT] @@ -611,8 +611,8 @@ def settings_css(v): @app.post("/settings/profilecss") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def settings_profilecss(v): profilecss = request.values.get("profilecss", v.profilecss).strip().replace('\\', '').strip()[:CSS_LENGTH_LIMIT] valid, error = validate_css(profilecss) @@ -633,8 +633,8 @@ def settings_security(v:User): @app.post("/settings/block") @limiter.limit("1/second;20/day") +@limiter.limit("1/second;20/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit("1/second;20/day", key_func=lambda:f'{SITE}-{g.v.id}') def settings_block_user(v): user = get_user(request.values.get("username"), graceful=True) if not user: abort(404, "This user doesn't exist.") @@ -660,8 +660,8 @@ def settings_block_user(v): @app.post("/settings/unblock") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def settings_unblock_user(v): user = get_user(request.values.get("username")) x = v.has_blocked(user) @@ -684,8 +684,8 @@ def settings_advanced_get(v:User): @app.post("/settings/name_change") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def settings_name_change(v): new_name=request.values.get("name").strip() @@ -723,8 +723,8 @@ def settings_name_change(v): @app.post("/settings/song_change_mp3") @feature_required('USERS_PROFILE_SONG') @limiter.limit("1/second;10/day") +@limiter.limit("1/second;10/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit("1/second;10/day", key_func=lambda:f'{SITE}-{g.v.id}') def settings_song_change_mp3(v): file = request.files['file'] if file.content_type != 'audio/mpeg': @@ -751,8 +751,8 @@ def settings_song_change_mp3(v): @app.post("/settings/song_change") @feature_required('USERS_PROFILE_SONG') @limiter.limit("3/second;10/day") +@limiter.limit("3/second;10/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit("3/second;10/day", key_func=lambda:f'{SITE}-{g.v.id}') def settings_song_change(v): song=request.values.get("song").strip() @@ -829,8 +829,8 @@ def settings_song_change(v): @app.post("/settings/title_change") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def settings_title_change(v): if v.flairchanged: abort(403) @@ -858,8 +858,8 @@ def settings_title_change(v): @app.post("/settings/pronouns_change") @feature_required('PRONOUNS') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def settings_pronouns_change(v): pronouns = sanitize_settings_text(request.values.get("pronouns")) @@ -884,8 +884,8 @@ def settings_pronouns_change(v): @app.post("/settings/checkmark_text") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def settings_checkmark_text(v): if not v.verified: abort(403) new_name = sanitize_settings_text(request.values.get("checkmark-text"), 100) diff --git a/files/routes/static.py b/files/routes/static.py index f6587ca41..64bfcb4c2 100644 --- a/files/routes/static.py +++ b/files/routes/static.py @@ -201,8 +201,8 @@ def contact(v:Optional[User]): @app.post("/send_admin") @limiter.limit("1/second;1/2 minutes;10/day") +@limiter.limit("1/second;1/2 minutes;10/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit("1/second;1/2 minutes;10/day", key_func=lambda:f'{SITE}-{g.v.id}') def submit_contact(v): body = request.values.get("message") if not body: abort(400) diff --git a/files/routes/subs.py b/files/routes/subs.py index e69335dd5..c39665ca3 100644 --- a/files/routes/subs.py +++ b/files/routes/subs.py @@ -225,8 +225,8 @@ def sub_followers(v:User, sub): @app.post("/h//add_mod") @limiter.limit("1/second;30/day") +@limiter.limit("1/second;30/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit("1/second;30/day", key_func=lambda:f'{SITE}-{g.v.id}') def add_mod(v:User, sub): if SITE_NAME == 'WPD': abort(403) sub = get_sub_by_name(sub).name @@ -382,8 +382,8 @@ def sub_settings(v:User, sub): @app.post('/h//sidebar') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def post_sub_sidebar(v:User, sub): sub = get_sub_by_name(sub) if not v.mods(sub.name): abort(403) @@ -407,8 +407,8 @@ def post_sub_sidebar(v:User, sub): @app.post('/h//css') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def post_sub_css(v:User, sub): sub = get_sub_by_name(sub) css = request.values.get('css', '').strip() @@ -447,8 +447,8 @@ def get_sub_css(sub): @app.post("/h//settings/banners/") @limiter.limit("1/second;50/day") +@limiter.limit("1/second;50/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit("1/second;50/day", key_func=lambda:f'{SITE}-{g.v.id}') def upload_sub_banner(v:User, sub:str): if g.is_tor: abort(403, "Image uploads are not allowed through Tor") @@ -477,8 +477,8 @@ def upload_sub_banner(v:User, sub:str): @app.delete("/h//settings/banners/") @limiter.limit("1/2 second;30/day") +@limiter.limit("1/2 second;30/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit("1/2 second;30/day", key_func=lambda:f'{SITE}-{g.v.id}') def delete_sub_banner(v:User, sub:str, index:int): sub = get_sub_by_name(sub) if not v.mods(sub.name): abort(403) @@ -508,8 +508,8 @@ def delete_sub_banner(v:User, sub:str, index:int): @app.delete("/h//settings/banners/") @limiter.limit("1/10 second;30/day") +@limiter.limit("1/10 second;30/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit("1/10 second;30/day", key_func=lambda:f'{SITE}-{g.v.id}') def delete_all_sub_banners(v:User, sub:str): sub = get_sub_by_name(sub) if not v.mods(sub.name): abort(403) @@ -534,8 +534,8 @@ def delete_all_sub_banners(v:User, sub:str): @app.post("/h//sidebar_image") @limiter.limit("1/second;10/day") +@limiter.limit("1/second;10/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit("1/second;10/day", key_func=lambda:f'{SITE}-{g.v.id}') def sub_sidebar(v:User, sub): if g.is_tor: abort(403, "Image uploads are not allowed through TOR.") @@ -565,8 +565,8 @@ def sub_sidebar(v:User, sub): @app.post("/h//marsey_image") @limiter.limit("1/second;10/day") +@limiter.limit("1/second;10/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit("1/second;10/day", key_func=lambda:f'{SITE}-{g.v.id}') def sub_marsey(v:User, sub): if g.is_tor: abort(403, "Image uploads are not allowed through TOR.") diff --git a/files/routes/users.py b/files/routes/users.py index b4ab28ac9..57be547bf 100644 --- a/files/routes/users.py +++ b/files/routes/users.py @@ -285,8 +285,8 @@ def downvoting(v:User, username:str): @app.post("/@/suicide") @feature_required('USERS_SUICIDE') @limiter.limit("1/second;5/day") +@limiter.limit("1/second;5/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit("1/second;5/day", key_func=lambda:f'{SITE}-{g.v.id}') def suicide(v:User, username:str): user = get_user(username) suicide = f"Hi there,\n\nA [concerned user](/id/{v.id}) reached out to us about you.\n\nWhen you're in the middle of something painful, it may feel like you don't have a lot of options. But whatever you're going through, you deserve help and there are people who are here for you.\n\nThere are resources available in your area that are free, confidential, and available 24/7:\n\n- Call, Text, or Chat with Canada's [Crisis Services Canada](https://www.crisisservicescanada.ca/en/)\n- Call, Email, or Visit the UK's [Samaritans](https://www.samaritans.org/)\n- Text CHAT to America's [Crisis Text Line](https://www.crisistextline.org/) at 741741.\nIf you don't see a resource in your area above, the moderators keep a comprehensive list of resources and hotlines for people organized by location. Find Someone Now\n\nIf you think you may be depressed or struggling in another way, don't ignore it or brush it aside. Take yourself and your feelings seriously, and reach out to someone.\n\nIt may not feel like it, but you have options. There are people available to listen to you, and ways to move forward.\n\nYour fellow users care about you and there are people who want to help." @@ -343,16 +343,16 @@ def transfer_currency(v:User, username:str, currency_name:Literal['coins', 'mars @app.post("/@/transfer_coins") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def transfer_coins(v:User, username:str): return transfer_currency(v, username, 'coins', True) @app.post("/@/transfer_bux") @feature_required('MARSEYBUX') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def transfer_bux(v:User, username:str): return transfer_currency(v, username, 'marseybux', False) @@ -434,8 +434,8 @@ def usersong(username:str): @app.post("/subscribe/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def subscribe(v, post_id): existing = g.db.query(Subscription).filter_by(user_id=v.id, submission_id=post_id).one_or_none() if not existing: @@ -445,8 +445,8 @@ def subscribe(v, post_id): @app.post("/unsubscribe/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def unsubscribe(v, post_id): existing = g.db.query(Subscription).filter_by(user_id=v.id, submission_id=post_id).one_or_none() if existing: @@ -455,8 +455,8 @@ def unsubscribe(v, post_id): @app.post("/@/message") @limiter.limit("1/second;10/minute;20/hour;50/day") +@limiter.limit("1/second;10/minute;20/hour;50/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit("1/second;10/minute;20/hour;50/day", key_func=lambda:f'{SITE}-{g.v.id}') def message2(v:User, username:str): user = get_user(username, v=v, include_blocks=True, include_shadowbanned=False) @@ -516,8 +516,8 @@ def message2(v:User, username:str): @app.post("/reply") @limiter.limit("1/second;6/minute;50/hour;200/day") +@limiter.limit("1/second;6/minute;50/hour;200/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit("1/second;6/minute;50/hour;200/day", key_func=lambda:f'{SITE}-{g.v.id}') def messagereply(v:User): body = sanitize_raw_body(request.values.get("body"), False) if not body and not request.files.get("file"): abort(400, "Message is empty!") @@ -1019,8 +1019,8 @@ def u_user_id_info(id, v=None): @app.post("/follow/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def follow_user(username, v): target = get_user(username, v=v, include_shadowbanned=False) @@ -1046,8 +1046,8 @@ def follow_user(username, v): @app.post("/unfollow/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def unfollow_user(username, v): target = get_user(username) @@ -1074,8 +1074,8 @@ def unfollow_user(username, v): @app.post("/remove_follow/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{SITE}-{g.v.id}') def remove_follow(username, v): target = get_user(username) diff --git a/files/routes/votes.py b/files/routes/votes.py index b9a33d339..fb3de4893 100644 --- a/files/routes/votes.py +++ b/files/routes/votes.py @@ -182,16 +182,16 @@ def vote_post_comment(target_id, new, v, cls, vote_cls): @app.post("/vote/post//") @limiter.limit("5/second;60/minute;1000/hour;2000/day") +@limiter.limit("5/second;60/minute;1000/hour;2000/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit("5/second;60/minute;1000/hour;2000/day", key_func=lambda:f'{SITE}-{g.v.id}') @limiter.limit("1/second", key_func=lambda:f'{g.v.id}-{request.full_path}') def vote_post(post_id, new, v): return vote_post_comment(post_id, new, v, Submission, Vote) @app.post("/vote/comment//") @limiter.limit("5/second;60/minute;1000/hour;2000/day") +@limiter.limit("5/second;60/minute;1000/hour;2000/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @is_not_permabanned -@limiter.limit("5/second;60/minute;1000/hour;2000/day", key_func=lambda:f'{SITE}-{g.v.id}') @limiter.limit("1/second", key_func=lambda:f'{g.v.id}-{request.full_path}') def vote_comment(comment_id, new, v): return vote_post_comment(comment_id, new, v, Comment, CommentVote)