From 7aaeedea3657b3e47b553eafb7458894dd12996e Mon Sep 17 00:00:00 2001 From: Aevann Date: Sun, 26 Feb 2023 03:42:39 +0200 Subject: [PATCH] better ratelimiting --- files/helpers/config/const.py | 9 +- files/routes/admin.py | 161 +++++++++++++++++++----------- files/routes/asset_submissions.py | 12 ++- files/routes/awards.py | 6 +- files/routes/casino.py | 7 ++ files/routes/comments.py | 38 ++++--- files/routes/front.py | 2 +- files/routes/groups.py | 5 + files/routes/hats.py | 3 + files/routes/login.py | 18 ++-- files/routes/lottery.py | 11 +- files/routes/mail.py | 5 +- files/routes/notifications.py | 1 + files/routes/oauth.py | 45 +++++---- files/routes/polls.py | 2 + files/routes/posts.py | 47 +++++---- files/routes/push_notifs.py | 1 + files/routes/reporting.py | 20 ++-- files/routes/settings.py | 115 ++++++++++++--------- files/routes/static.py | 6 +- files/routes/subs.py | 47 ++++++--- files/routes/users.py | 57 +++++++---- files/routes/votes.py | 10 +- files/routes/wrappers.py | 3 + 24 files changed, 405 insertions(+), 226 deletions(-) diff --git a/files/helpers/config/const.py b/files/helpers/config/const.py index be4eb83d6..d0ae4324a 100644 --- a/files/helpers/config/const.py +++ b/files/helpers/config/const.py @@ -57,10 +57,9 @@ class Service(Enum): RDRAMA = auto() CHAT = auto() -POST_RATELIMIT = '1/second;10/hour;20/day' -DEFAULT_RATELIMIT_SLOWER = "1/second;30/minute;200/hour;1000/day" -DEFAULT_RATELIMIT = "3/second;30/minute;200/hour;1000/day" -CASINO_RATELIMIT = "1/second;100/minute;2000/hour;12000/day" +POST_RATELIMIT = '10/hour;20/day' +DEFAULT_RATELIMIT = "30/minute;200/hour;1000/day" +CASINO_RATELIMIT = "100/minute;2000/hour;12000/day" PUSH_NOTIF_LIMIT = 1000 @@ -797,7 +796,7 @@ if SITE == 'rdrama.net': 'justcool393': JUSTCOOL_ID } - POST_RATELIMIT = '1/second;5/hour;10/day' + POST_RATELIMIT = '5/hour;10/day' elif SITE == 'watchpeopledie.tv': EMAIL = "wpd@watchpeopledie.tv" TELEGRAM_ID = "wpdtv" diff --git a/files/routes/admin.py b/files/routes/admin.py index 337c97084..3f84f4d6f 100644 --- a/files/routes/admin.py +++ b/files/routes/admin.py @@ -72,8 +72,9 @@ def edit_rules_get(v): @app.post('/admin/edit_rules') -@limiter.limit("1/second;30/minute;200/hour;1000/day") -@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("30/minute;200/hour;1000/day") +@limiter.limit("30/minute;200/hour;1000/day", key_func=get_ID) @admin_level_required(PERMS['EDIT_RULES']) def edit_rules_post(v): rules = request.values.get('rules', '').strip() @@ -90,6 +91,7 @@ def edit_rules_post(v): return render_template('admin/edit_rules.html', v=v, rules=rules, msg='Rules edited successfully!') @app.post("/@/make_admin") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['ADMIN_ADD']) def make_admin(v:User, username): @@ -111,6 +113,7 @@ def make_admin(v:User, username): @app.post("/@/remove_admin") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['ADMIN_REMOVE']) def remove_admin(v:User, username): @@ -138,8 +141,9 @@ def remove_admin(v:User, username): return {"message": f"@{user.username} has been removed as admin!"} @app.post("/distribute/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_BETS_DISTRIBUTE']) def distribute(v:User, option_id): autojanny = get_account(AUTOJANNY_ID) @@ -196,8 +200,9 @@ def distribute(v:User, option_id): return {"message": f"Each winner has received {coinsperperson} coins!"} @app.post("/@/revert_actions") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['ADMIN_ACTIONS_REVERT']) def revert_actions(v:User, username): revertee = get_user(username) @@ -340,6 +345,7 @@ def admin_home(v): return render_template("admin/admin_home.html", v=v) @app.post("/admin/site_settings/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['SITE_SETTINGS']) def change_settings(v:User, setting): @@ -367,6 +373,7 @@ def change_settings(v:User, setting): return {'message': f"{setting.replace('_', ' ').title()} {word}d successfully!"} @app.post("/admin/clear_cloudflare_cache") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['SITE_CACHE_PURGE_CDN']) def clear_cloudflare_cache(v): @@ -401,9 +408,10 @@ def badge_grant_get(v): badge_types=badge_types, grant=grant) @app.post("/admin/badge_grant") +@limiter.limit('1/second', scope=path) @feature_required('BADGES') -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_BADGES']) def badge_grant_post(v): badges = admin_badges_grantable_list(v) @@ -462,9 +470,10 @@ def badge_grant_post(v): return render_template("admin/badge_admin.html", v=v, badge_types=badges, grant=True, msg=f"{new_badge.name} Badge granted to @{user.username} successfully!") @app.post("/admin/badge_remove") +@limiter.limit('1/second', scope=path) @feature_required('BADGES') -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_BADGES']) def badge_remove_post(v): badges = admin_badges_grantable_list(v) @@ -605,16 +614,17 @@ def alt_votes_get(v): @app.get("/admin/alts/") @app.get("/@/alts/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_LINK']) def admin_view_alts(v:User, username=None): u = get_user(username or request.values.get('username'), graceful=True) return render_template('admin/alts.html', v=v, u=u, alts=u.alts if u else None) @app.post('/@/alts/') -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_LINK']) def admin_add_alt(v:User, username): user1 = get_user(username) @@ -648,8 +658,9 @@ def admin_add_alt(v:User, username): return {"message": f"Linked @{user1.username} and @{user2.username} successfully!"} @app.post('/@/alts//deleted') -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_LINK']) def admin_delink_relink_alt(v:User, username, other): user1 = get_user(username) @@ -712,6 +723,7 @@ def admin_removed_comments(v): ) @app.post("/unagendaposter/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_AGENDAPOSTER']) def unagendaposter(id, v): @@ -751,8 +763,9 @@ def unagendaposter(id, v): @app.post("/shadowban/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_SHADOWBAN']) def shadowban(user_id, v): user = get_account(user_id) @@ -786,8 +799,9 @@ def shadowban(user_id, v): return {"message": f"@{user.username} has been shadowbanned!"} @app.post("/unshadowban/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_SHADOWBAN']) def unshadowban(user_id, v): user = get_account(user_id) @@ -813,8 +827,9 @@ def unshadowban(user_id, v): @app.post("/admin/title_change/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_TITLE_CHANGE']) def admin_title_change(user_id, v): @@ -857,8 +872,9 @@ def admin_title_change(user_id, v): return {"message": f"@{user.username}'s flair has been changed!"} @app.post("/ban_user/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_BAN']) def ban_user(id, v): @@ -949,6 +965,7 @@ def ban_user(id, v): @app.post("/agendaposter/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_AGENDAPOSTER']) def agendaposter(id, v): @@ -1043,8 +1060,9 @@ def agendaposter(id, v): @app.post("/unban_user/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_BAN']) def unban_user(id, v): @@ -1088,8 +1106,9 @@ def unban_user(id, v): return {"message": f"@{user.username} has been unbanned!"} @app.post("/mute_user/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_BAN']) def mute_user(v:User, user_id): user = get_account(user_id) @@ -1109,8 +1128,9 @@ def mute_user(v:User, user_id): @app.post("/unmute_user/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_BAN']) def unmute_user(v:User, user_id): user = get_account(user_id) @@ -1128,8 +1148,9 @@ def unmute_user(v:User, user_id): return {"message": f"@{user.username} has been unmuted!"} @app.post("/admin/progstack/post/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['PROGSTACK']) def progstack_post(post_id, v): post = get_post(post_id) @@ -1148,8 +1169,9 @@ def progstack_post(post_id, v): return {"message": "Progressive stack applied on post!"} @app.post("/admin/unprogstack/post/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['PROGSTACK']) def unprogstack_post(post_id, v): post = get_post(post_id) @@ -1166,8 +1188,9 @@ def unprogstack_post(post_id, v): return {"message": "Progressive stack removed from post!"} @app.post("/admin/progstack/comment/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['PROGSTACK']) def progstack_comment(comment_id, v): comment = get_comment(comment_id) @@ -1186,8 +1209,9 @@ def progstack_comment(comment_id, v): return {"message": "Progressive stack applied on comment!"} @app.post("/admin/unprogstack/comment/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['PROGSTACK']) def unprogstack_comment(comment_id, v): comment = get_comment(comment_id) @@ -1204,8 +1228,9 @@ def unprogstack_comment(comment_id, v): return {"message": "Progressive stack removed from comment!"} @app.post("/remove_post/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def remove_post(post_id, v): post = get_post(post_id) @@ -1233,8 +1258,9 @@ def remove_post(post_id, v): @app.post("/approve_post/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def approve_post(post_id, v): post = get_post(post_id) @@ -1265,6 +1291,7 @@ def approve_post(post_id, v): @app.post("/distinguish/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_DISTINGUISH']) def distinguish_post(post_id, v): @@ -1292,6 +1319,7 @@ def distinguish_post(post_id, v): @app.post("/sticky/") +@limiter.limit('1/second', scope=path) @feature_required('PINS') @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) @@ -1337,6 +1365,7 @@ def sticky_post(post_id, v): @app.post("/unsticky/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def unsticky_post(post_id, v): @@ -1366,6 +1395,7 @@ def unsticky_post(post_id, v): return {"message": "Post unpinned!"} @app.post("/sticky_comment/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def sticky_comment(cid, v): @@ -1402,6 +1432,7 @@ def sticky_comment(cid, v): @app.post("/unsticky_comment/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def unsticky_comment(cid, v): @@ -1434,8 +1465,9 @@ def unsticky_comment(cid, v): @app.post("/remove_comment/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def remove_comment(c_id, v): comment = get_comment(c_id) @@ -1455,8 +1487,9 @@ def remove_comment(c_id, v): @app.post("/approve_comment/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def approve_comment(c_id, v): comment = get_comment(c_id) @@ -1482,6 +1515,7 @@ def approve_comment(c_id, v): @app.post("/distinguish_comment/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_DISTINGUISH']) def admin_distinguish_comment(c_id, v): @@ -1517,8 +1551,9 @@ def admin_banned_domains(v): banned_domains=banned_domains) @app.post("/admin/ban_domain") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['DOMAINS_BAN']) def ban_domain(v): @@ -1549,8 +1584,9 @@ def ban_domain(v): @app.post("/admin/unban_domain/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['DOMAINS_BAN']) def unban_domain(v:User, domain): existing = g.db.get(BannedDomain, domain) @@ -1569,8 +1605,9 @@ def unban_domain(v:User, domain): @app.post("/admin/nuke_user") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def admin_nuke_user(v): @@ -1603,8 +1640,9 @@ def admin_nuke_user(v): @app.post("/admin/unnuke_user") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def admin_nunuke_user(v): @@ -1638,8 +1676,9 @@ def admin_nunuke_user(v): return {"message": f"@{user.username}'s content has been approved!"} @app.post("/blacklist/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_BLACKLIST']) def blacklist_user(user_id, v): user = get_account(user_id) @@ -1659,8 +1698,9 @@ def blacklist_user(user_id, v): return {"message": f"@{user.username} has been blacklisted from restricted holes!"} @app.post("/unblacklist/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_BLACKLIST']) def unblacklist_user(user_id, v): user = get_account(user_id) @@ -1687,8 +1727,9 @@ def delete_media_get(v): return render_template("admin/delete_media.html", v=v) @app.post("/admin/delete_media") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['DELETE_MEDIA']) def delete_media_post(v): diff --git a/files/routes/asset_submissions.py b/files/routes/asset_submissions.py index fbc5e4a3c..0877b1f0e 100644 --- a/files/routes/asset_submissions.py +++ b/files/routes/asset_submissions.py @@ -33,6 +33,7 @@ def submit_marseys(v:User): @app.post("/submit/marseys") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def submit_marsey(v:User): @@ -106,6 +107,7 @@ def verify_permissions_and_get_asset(cls, asset_type:str, v:User, name:str, make return asset @app.post("/admin/approve/marsey/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['MODERATE_PENDING_SUBMITTED_ASSETS']) def approve_marsey(v, name): @@ -204,6 +206,7 @@ def remove_asset(cls, type_name:str, v:User, name:str) -> dict[str, str]: return {"message": f"'{name}' removed!"} @app.post("/remove/marsey/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def remove_marsey(v:User, name): @@ -220,6 +223,7 @@ def submit_hats(v:User): @app.post("/submit/hats") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def submit_hat(v:User): @@ -282,8 +286,9 @@ def submit_hat(v:User): @app.post("/admin/approve/hat/") -@limiter.limit("3/second;120/minute;200/hour;1000/day") -@limiter.limit("3/second;120/minute;200/hour;1000/day", key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("120/minute;200/hour;1000/day") +@limiter.limit("120/minute;200/hour;1000/day", key_func=get_ID) @admin_level_required(PERMS['MODERATE_PENDING_SUBMITTED_ASSETS']) def approve_hat(v, name): hat = verify_permissions_and_get_asset(HatDef, "hat", v, name, False) @@ -353,6 +358,7 @@ def approve_hat(v, name): return {"message": f"'{hat.name}' approved!"} @app.post("/remove/hat/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def remove_hat(v:User, name): @@ -377,6 +383,7 @@ def update_marseys(v): @app.post("/admin/update/marseys") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['UPDATE_ASSETS']) def update_marsey(v): @@ -435,6 +442,7 @@ def update_hats(v): @app.post("/admin/update/hats") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['UPDATE_ASSETS']) def update_hat(v): diff --git a/files/routes/awards.py b/files/routes/awards.py index 0c7b2c9c9..9f2a7dfcf 100644 --- a/files/routes/awards.py +++ b/files/routes/awards.py @@ -46,6 +46,7 @@ def shop(v:User): @app.post("/buy/") +@limiter.limit('1/second', scope=path) @limiter.limit("100/minute;200/hour;1000/day") @limiter.limit("100/minute;200/hour;1000/day", key_func=get_ID) @auth_required @@ -127,8 +128,9 @@ def buy(v:User, award): return {"message": f"{award_title} award bought!"} @app.post("/award//") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def award_thing(v, thing_type, id): kind = request.values.get("kind", "").strip() diff --git a/files/routes/casino.py b/files/routes/casino.py index 298ae6d36..caab91224 100644 --- a/files/routes/casino.py +++ b/files/routes/casino.py @@ -80,6 +80,7 @@ def lottershe(v:User): # Slots @app.post("/casino/slots") +@limiter.limit('1/second', scope=path) @limiter.limit(CASINO_RATELIMIT) @limiter.limit(CASINO_RATELIMIT, key_func=get_ID) @auth_required @@ -112,6 +113,7 @@ def pull_slots(v:User): # 21 @app.post("/casino/twentyone/deal") +@limiter.limit('1/second', scope=path) @limiter.limit(CASINO_RATELIMIT) @limiter.limit(CASINO_RATELIMIT, key_func=get_ID) @auth_required @@ -132,6 +134,7 @@ def blackjack_deal_to_player(v:User): @app.post("/casino/twentyone/hit") +@limiter.limit('1/second', scope=path) @limiter.limit(CASINO_RATELIMIT) @limiter.limit(CASINO_RATELIMIT, key_func=get_ID) @auth_required @@ -148,6 +151,7 @@ def blackjack_player_hit(v:User): @app.post("/casino/twentyone/stay") +@limiter.limit('1/second', scope=path) @limiter.limit(CASINO_RATELIMIT) @limiter.limit(CASINO_RATELIMIT, key_func=get_ID) @auth_required @@ -164,6 +168,7 @@ def blackjack_player_stay(v:User): @app.post("/casino/twentyone/double-down") +@limiter.limit('1/second', scope=path) @limiter.limit(CASINO_RATELIMIT) @limiter.limit(CASINO_RATELIMIT, key_func=get_ID) @auth_required @@ -180,6 +185,7 @@ def blackjack_player_doubled_down(v:User): @app.post("/casino/twentyone/buy-insurance") +@limiter.limit('1/second', scope=path) @limiter.limit(CASINO_RATELIMIT) @limiter.limit(CASINO_RATELIMIT, key_func=get_ID) @auth_required @@ -209,6 +215,7 @@ def roulette_get_bets(v:User): @app.post("/casino/roulette/place-bet") +@limiter.limit('1/second', scope=path) @limiter.limit(CASINO_RATELIMIT) @limiter.limit(CASINO_RATELIMIT, key_func=get_ID) @auth_required diff --git a/files/routes/comments.py b/files/routes/comments.py index 4efbfa5b6..f6545cbc6 100644 --- a/files/routes/comments.py +++ b/files/routes/comments.py @@ -82,8 +82,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/second;20/minute;200/hour;1000/day") -@limiter.limit("1/second;20/minute;200/hour;1000/day", key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("20/minute;200/hour;1000/day") +@limiter.limit("20/minute;200/hour;1000/day", key_func=get_ID) @auth_required def comment(v:User): if v.is_suspended: abort(403, "You can't perform this action while banned!") @@ -372,8 +373,9 @@ def comment(v:User): return {"comment": render_template("comments.html", v=v, comments=[c])} @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=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("10/minute;100/hour;200/day") +@limiter.limit("10/minute;100/hour;200/day", key_func=get_ID) @is_not_permabanned def edit_comment(cid, v): c = get_comment(cid, v=v) @@ -449,8 +451,9 @@ def edit_comment(cid, v): @app.post("/delete/comment/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def delete_comment(cid, v): if v.id == 253: abort(403) @@ -471,8 +474,9 @@ def delete_comment(cid, v): return {"message": "Comment deleted!"} @app.post("/undelete/comment/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def undelete_comment(cid, v): c = get_comment(cid, v=v) @@ -491,6 +495,7 @@ def undelete_comment(cid, v): return {"message": "Comment undeleted!"} @app.post("/pin_comment/") +@limiter.limit('1/second', scope=path) @feature_required('PINS') @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required @@ -515,6 +520,7 @@ def pin_comment(cid, v): @app.post("/unpin_comment/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unpin_comment(cid, v): @@ -537,8 +543,9 @@ def unpin_comment(cid, v): @app.post("/save_comment/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def save_comment(cid, v): @@ -554,8 +561,9 @@ def save_comment(cid, v): return {"message": "Comment saved!"} @app.post("/unsave_comment/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unsave_comment(cid, v): @@ -590,8 +598,9 @@ def diff_words(answer, guess): @app.post("/wordle/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def handle_wordle_action(cid, v): comment = get_comment(cid) @@ -622,6 +631,7 @@ def handle_wordle_action(cid, v): @app.post("/toggle_comment_nsfw/") +@limiter.limit('1/second', scope=path) @feature_required('NSFW_MARKING') @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required diff --git a/files/routes/front.py b/files/routes/front.py index b869387fd..e20f59270 100644 --- a/files/routes/front.py +++ b/files/routes/front.py @@ -13,7 +13,7 @@ from files.__main__ import app, cache, limiter @app.get("/") @app.get("/h/") @app.get("/s/") -@limiter.limit("3/second;30/minute;5000/hour;10000/day") +@limiter.limit("30/minute;5000/hour;10000/day") @auth_desired_with_logingate def front_all(v, sub=None, subdomain=None): if sub: diff --git a/files/routes/groups.py b/files/routes/groups.py index b53e60ab3..4ecf857c9 100644 --- a/files/routes/groups.py +++ b/files/routes/groups.py @@ -15,6 +15,7 @@ def ping_groups(v:User): return render_template('groups.html', v=v, groups=groups, cost=GROUP_COST, msg=get_msg(), error=get_error()) @app.post("/create_group") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def create_group(v): @@ -52,6 +53,7 @@ def create_group(v): return redirect(f'/ping_groups?msg=!{group} created successfully!') @app.post("/!/apply") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def join_group(v:User, group_name): @@ -66,6 +68,7 @@ def join_group(v:User, group_name): return {"message": f"Application submitted to !{group}'s owner (@{group.owner.username}) successfully!"} @app.post("/!/leave") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def leave_group(v:User, group_name): @@ -108,6 +111,7 @@ def memberships(v:User, group_name): return render_template('group_memberships.html', v=v, group=group, members=members, applications=applications) @app.post("/!//approve") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def group_approve(v:User, group_name, user_id): @@ -129,6 +133,7 @@ def group_approve(v:User, group_name, user_id): return {"message": f'You have approved @{application.user.username} successfully!'} @app.post("/!//reject") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def group_reject(v:User, group_name, user_id): diff --git a/files/routes/hats.py b/files/routes/hats.py index 32fdbceb6..e93f70729 100644 --- a/files/routes/hats.py +++ b/files/routes/hats.py @@ -28,6 +28,7 @@ def hats(v:User): return render_template("hats.html", owned_hat_ids=owned_hat_ids, hats=hats, v=v, sales=sales, num_of_hats=num_of_hats) @app.post("/buy_hat/") +@limiter.limit('1/second', scope=path) @limiter.limit('100/minute;1000/3 days') @limiter.limit('100/minute;1000/3 days', key_func=get_ID) @auth_required @@ -80,6 +81,7 @@ def buy_hat(v:User, hat_id): @app.post("/equip_hat/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def equip_hat(v:User, hat_id): @@ -95,6 +97,7 @@ def equip_hat(v:User, hat_id): return {"message": f"'{hat.name}' equipped!"} @app.post("/unequip_hat/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unequip_hat(v:User, hat_id): diff --git a/files/routes/login.py b/files/routes/login.py index e73ba325f..609621b95 100644 --- a/files/routes/login.py +++ b/files/routes/login.py @@ -38,6 +38,7 @@ def login_deduct_when(resp): return g.login_failed @app.post("/login") +@limiter.limit('1/second', scope=path) @auth_desired @limiter.limit("6/minute;10/day", deduct_when=login_deduct_when) def login_post(v:Optional[User]): @@ -135,8 +136,9 @@ def me(v:User): @app.post("/logout") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def logout(v): loggedin = cache.get(f'{SITE}_loggedin') or {} @@ -193,7 +195,8 @@ def sign_up_get(v:Optional[User]): @app.post("/signup") -@limiter.limit("1/second;10/day") +@limiter.limit('1/second', scope=path) +@limiter.limit("10/day") @auth_desired def sign_up_post(v:Optional[User]): if not get_setting('signups'): @@ -365,7 +368,8 @@ def get_forgot(): @app.post("/forgot") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) def post_forgot(): username = request.values.get("username") @@ -429,7 +433,8 @@ def get_reset(): @app.post("/reset") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) @auth_desired def post_reset(v:Optional[User]): if v: return redirect('/') @@ -474,7 +479,8 @@ def lost_2fa(v:Optional[User]): return render_template("login/lost_2fa.html", v=v) @app.post("/lost_2fa") -@limiter.limit("1/second;6/minute;200/hour;1000/day") +@limiter.limit('1/second', scope=path) +@limiter.limit("6/minute;200/hour;1000/day") def lost_2fa_post(): username=request.values.get("username") user=get_user(username, graceful=True) diff --git a/files/routes/lottery.py b/files/routes/lottery.py index ba8790867..807a2ec62 100644 --- a/files/routes/lottery.py +++ b/files/routes/lottery.py @@ -7,6 +7,7 @@ from files.routes.wrappers import * from files.__main__ import app, limiter @app.post("/lottery/end") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['LOTTERY_ADMIN']) def lottery_end(v): @@ -15,6 +16,7 @@ def lottery_end(v): @app.post("/lottery/start") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['LOTTERY_ADMIN']) def lottery_start(v): @@ -23,8 +25,9 @@ def lottery_start(v): @app.post("/lottery/buy") -@limiter.limit("3/second;100/minute;500/hour;1000/day") -@limiter.limit("3/second;100/minute;500/hour;1000/day", key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("100/minute;500/hour;1000/day") +@limiter.limit("100/minute;500/hour;1000/day", key_func=get_ID) @auth_required def lottery_buy(v:User): try: quantity = int(request.values.get("quantity")) @@ -41,8 +44,8 @@ def lottery_buy(v:User): @app.get("/lottery/active") -@limiter.limit("3/second;100/minute;500/hour;1000/day") -@limiter.limit("3/second;100/minute;500/hour;1000/day", key_func=get_ID) +@limiter.limit("100/minute;500/hour;1000/day") +@limiter.limit("100/minute;500/hour;1000/day", key_func=get_ID) @auth_required def lottery_active(v:User): lottery, participants = get_active_lottery_stats() diff --git a/files/routes/mail.py b/files/routes/mail.py index 2db1225d5..091858020 100644 --- a/files/routes/mail.py +++ b/files/routes/mail.py @@ -10,8 +10,9 @@ from files.routes.users import claim_rewards from files.__main__ import app, limiter @app.post("/verify_email") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def verify_email(v): send_verification_email(v) diff --git a/files/routes/notifications.py b/files/routes/notifications.py index ee37027f3..0e18393a1 100644 --- a/files/routes/notifications.py +++ b/files/routes/notifications.py @@ -11,6 +11,7 @@ from files.routes.wrappers import * from files.__main__ import app @app.post("/clear") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def clear(v): diff --git a/files/routes/oauth.py b/files/routes/oauth.py index 5d3542bd4..f85dd55c1 100644 --- a/files/routes/oauth.py +++ b/files/routes/oauth.py @@ -17,8 +17,9 @@ def authorize_prompt(v:User): return render_template("oauth.html", v=v, application=application) @app.post("/authorize") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def authorize(v): client_id = request.values.get("client_id") @@ -37,8 +38,9 @@ def authorize(v): return redirect(f"{application.redirect_uri}?token={access_token}") @app.post("/rescind/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def rescind(v, aid): @@ -49,8 +51,9 @@ def rescind(v, aid): @app.post("/api_keys") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def request_api_keys(v): new_app = OauthApp( @@ -90,8 +93,9 @@ def request_api_keys(v): @app.post("/delete_app/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def delete_oauth_app(v, aid): try: @@ -113,8 +117,9 @@ def delete_oauth_app(v, aid): @app.post("/edit_app/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def edit_oauth_app(v, aid): try: @@ -137,8 +142,9 @@ def edit_oauth_app(v, aid): @app.post("/admin/app/approve/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['APPS_MODERATION']) def admin_app_approve(v, aid): @@ -174,8 +180,9 @@ def admin_app_approve(v, aid): @app.post("/admin/app/revoke/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['APPS_MODERATION']) def admin_app_revoke(v, aid): @@ -200,8 +207,9 @@ def admin_app_revoke(v, aid): @app.post("/admin/app/reject/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['APPS_MODERATION']) def admin_app_reject(v, aid): @@ -286,8 +294,9 @@ def admin_apps_list(v): @app.post("/reroll/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def reroll_oauth_tokens(aid, v): diff --git a/files/routes/polls.py b/files/routes/polls.py index 78cd3e8c5..50e6a938e 100644 --- a/files/routes/polls.py +++ b/files/routes/polls.py @@ -6,6 +6,7 @@ from files.__main__ import app @app.post("/vote/post/option/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def vote_option(option_id, v): @@ -85,6 +86,7 @@ def option_votes(option_id, v): @app.post("/vote/comment/option/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def vote_option_comment(option_id, v): diff --git a/files/routes/posts.py b/files/routes/posts.py index 59b343b25..9d1ea5924 100644 --- a/files/routes/posts.py +++ b/files/routes/posts.py @@ -33,8 +33,9 @@ from files.__main__ import app, limiter titleheaders = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36"} @app.post("/publish/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def publish(pid, v): post = get_post(pid) @@ -174,7 +175,7 @@ def post_id(pid, anything=None, v=None, sub=None): fart=get_setting('fart_mode')) @app.get("/view_more///") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT) @auth_desired_with_logingate def view_more(v, pid, sort, offset): post = get_post(pid, v=v) @@ -229,7 +230,7 @@ def view_more(v, pid, sort, offset): @app.get("/more_comments/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT) @auth_desired_with_logingate def more_comments(v, cid): try: cid = int(cid) @@ -253,8 +254,9 @@ def more_comments(v, cid): return render_template("comments.html", v=v, comments=comments, p=p, render_replies=True) @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=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("10/minute;100/hour;200/day") +@limiter.limit("10/minute;100/hour;200/day", key_func=get_ID) @is_not_permabanned def edit_post(pid, v): p = get_post(pid) @@ -474,6 +476,7 @@ def thumbnail_thread(pid:int, vid:int): @app.post("/is_repost") +@limiter.limit('1/second', scope=path) def is_repost(): not_a_repost = {'permalink': ''} if not FEATURES['REPOST_DETECTION']: @@ -518,6 +521,7 @@ def is_repost(): @app.post("/submit") @app.post("/h//submit") +@limiter.limit('1/second', scope=path) @limiter.limit(POST_RATELIMIT) @limiter.limit(POST_RATELIMIT, key_func=get_ID) @auth_required @@ -799,8 +803,9 @@ def submit_post(v:User, sub=None): return {"post_id": post.id} @app.post("/delete_post/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def delete_post_pid(pid, v): post = get_post(pid) @@ -826,8 +831,9 @@ def delete_post_pid(pid, v): return {"message": "Post deleted!"} @app.post("/undelete_post/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def undelete_post_pid(pid, v): post = get_post(pid) @@ -848,6 +854,7 @@ def undelete_post_pid(pid, v): @app.post("/mark_post_nsfw/") +@limiter.limit('1/second', scope=path) @feature_required('NSFW_MARKING') @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required @@ -884,6 +891,7 @@ def mark_post_nsfw(pid, v): return {"message": "Post has been marked as +18!"} @app.post("/unmark_post_nsfw/") +@limiter.limit('1/second', scope=path) @feature_required('NSFW_MARKING') @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required @@ -920,8 +928,9 @@ def unmark_post_nsfw(pid, v): return {"message": "Post has been unmarked as +18!"} @app.post("/save_post/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def save_post(pid, v): @@ -936,8 +945,9 @@ def save_post(pid, v): return {"message": "Post saved!"} @app.post("/unsave_post/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unsave_post(pid, v): @@ -951,6 +961,7 @@ def unsave_post(pid, v): return {"message": "Post unsaved!"} @app.post("/pin/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def pin_post(post_id, v): @@ -965,8 +976,8 @@ def pin_post(post_id, v): return abort(404, "Post not found!") @app.put("/post//new") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def set_new_sort(post_id:int, v:User): post = get_post(post_id) @@ -987,8 +998,8 @@ def set_new_sort(post_id:int, v:User): @app.delete("/post//new") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unset_new_sort(post_id:int, v:User): post = get_post(post_id) diff --git a/files/routes/push_notifs.py b/files/routes/push_notifs.py index f248d05cf..d9b5da329 100644 --- a/files/routes/push_notifs.py +++ b/files/routes/push_notifs.py @@ -4,6 +4,7 @@ from flask import request, g from files.classes.push_subscriptions import PushSubscription @app.post("/push_subscribe") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def push_subscribe(v): diff --git a/files/routes/reporting.py b/files/routes/reporting.py index 293f7edfd..f1fb0db8b 100644 --- a/files/routes/reporting.py +++ b/files/routes/reporting.py @@ -12,8 +12,9 @@ from files.routes.wrappers import * from files.__main__ import app, limiter, cache @app.post("/report/post/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def flag_post(pid, v): post = get_post(pid) @@ -68,8 +69,9 @@ def flag_post(pid, v): @app.post("/report/comment/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def flag_comment(cid, v): @@ -94,8 +96,9 @@ def flag_comment(cid, v): @app.post('/del_report/post//') -@limiter.limit("4/second;100/minute;300/hour;2000/day") -@limiter.limit("4/second;100/minute;300/hour;2000/day", key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("100/minute;300/hour;2000/day") +@limiter.limit("100/minute;300/hour;2000/day", key_func=get_ID) @admin_level_required(PERMS['FLAGS_REMOVE']) def remove_report_post(v, pid, uid): try: @@ -118,8 +121,9 @@ def remove_report_post(v, pid, uid): @app.post('/del_report/comment//') -@limiter.limit("4/second;100/minute;300/hour;2000/day") -@limiter.limit("4/second;100/minute;300/hour;2000/day", key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("100/minute;300/hour;2000/day") +@limiter.limit("100/minute;300/hour;2000/day", key_func=get_ID) @admin_level_required(PERMS['FLAGS_REMOVE']) def remove_report_comment(v, cid, uid): try: diff --git a/files/routes/settings.py b/files/routes/settings.py index 1523e3c7e..dfc09526d 100644 --- a/files/routes/settings.py +++ b/files/routes/settings.py @@ -37,8 +37,8 @@ def settings_personal(v:User): return render_template("settings/personal.html", v=v, error=get_error(), msg=get_msg()) @app.delete('/settings/background') -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def remove_background(v): if v.background: @@ -49,8 +49,9 @@ def remove_background(v): return {"message": "Background removed!"} @app.post('/settings/custom_background') -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def upload_custom_background(v): if g.is_tor: abort(403, "Image uploads are not allowed through TOR!") @@ -73,8 +74,9 @@ def upload_custom_background(v): return redirect('/settings/personal') @app.post('/settings/profile_background') -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def upload_profile_background(v): if g.is_tor: abort(403, "Image uploads are not allowed through TOR!") @@ -94,8 +96,8 @@ def upload_profile_background(v): return redirect(f'/@{v.username}') @app.delete('/settings/profile_background') -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def delete_profile_background(v): if v.profile_background: @@ -104,8 +106,9 @@ def delete_profile_background(v): return {"message": "Profile background removed!"} @app.post("/settings/personal") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_personal_post(v): if v.id == 253 and request.values.get("private"): @@ -361,6 +364,7 @@ def settings_personal_post(v): @app.post("/settings/filters") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def filters(v:User): @@ -388,37 +392,42 @@ 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=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required 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=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def themecolor(v): return set_color(v, "themecolor", request.values.get("themecolor")) @app.post("/settings/titlecolor") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required 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=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required 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=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_security_post(v): if request.values.get("new_password"): @@ -491,8 +500,9 @@ def settings_security_post(v): return render_template("settings/security.html", v=v, msg="Two-factor authentication disabled!") @app.post("/settings/log_out_all_others") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_log_out_others(v): submitted_password = request.values.get("password", "").strip() @@ -507,8 +517,9 @@ def settings_log_out_others(v): @app.post("/settings/images/profile") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_images_profile(v): if g.is_tor: abort(403, "Image uploads are not allowed through TOR!") @@ -545,9 +556,10 @@ def settings_images_profile(v): @app.post("/settings/images/banner") +@limiter.limit('1/second', scope=path) @feature_required('USERS_PROFILE_BANNER') -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_images_banner(v): if g.is_tor: abort(403, "Image uploads are not allowed through TOR!") @@ -573,8 +585,9 @@ def settings_css_get(v:User): return render_template("settings/css.html", v=v, msg=get_msg(), profilecss=v.profilecss) @app.post("/settings/css") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_css(v): if v.agendaposter: abort(400, "Agendapostered users can't edit CSS!") @@ -585,8 +598,9 @@ def settings_css(v): return render_template("settings/css.html", v=v, msg="Custom CSS successfully updated!", profilecss=v.profilecss) @app.post("/settings/profilecss") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_profilecss(v): profilecss = request.values.get("profilecss", v.profilecss).strip().replace('\\', '').strip()[:CSS_LENGTH_LIMIT] @@ -610,8 +624,9 @@ def settings_security(v:User): ) @app.post("/settings/block") -@limiter.limit("1/second;20/day") -@limiter.limit("1/second;20/day", key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("20/day") +@limiter.limit("20/day", key_func=get_ID) @auth_required def settings_block_user(v): user = get_user(request.values.get("username"), graceful=True) @@ -637,8 +652,9 @@ def settings_block_user(v): @app.post("/settings/unblock") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_unblock_user(v): user = get_user(request.values.get("username")) @@ -663,8 +679,9 @@ def settings_advanced_get(v:User): return render_template("settings/advanced.html", v=v, msg=get_msg(), error=get_error()) @app.post("/settings/name_change") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def settings_name_change(v): new_name=request.values.get("name").strip() @@ -701,9 +718,10 @@ def settings_name_change(v): return redirect("/settings/personal?msg=Name successfully changed!") @app.post("/settings/song_change_mp3") +@limiter.limit('1/second', scope=path) @feature_required('USERS_PROFILE_SONG') -@limiter.limit("1/second;10/day") -@limiter.limit("1/second;10/day", key_func=get_ID) +@limiter.limit("10/day") +@limiter.limit("10/day", key_func=get_ID) @auth_required def settings_song_change_mp3(v): file = request.files['file'] @@ -765,9 +783,10 @@ def _change_song_youtube(vid, id): @app.post("/settings/song_change") +@limiter.limit('1/second', scope=path) @feature_required('USERS_PROFILE_SONG') -@limiter.limit("3/second;10/day") -@limiter.limit("3/second;10/day", key_func=get_ID) +@limiter.limit("10/day") +@limiter.limit("10/day", key_func=get_ID) @auth_required def settings_song_change(v): song=request.values.get("song").strip() @@ -817,8 +836,9 @@ def settings_song_change(v): return redirect("/settings/personal?msg=Profile Anthem successfully updated. Wait 5 minutes for the change to take effect.") @app.post("/settings/title_change") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_title_change(v): if v.flairchanged: abort(403) @@ -845,9 +865,10 @@ def settings_title_change(v): @app.post("/settings/pronouns_change") +@limiter.limit('1/second', scope=path) @feature_required('PRONOUNS') -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_pronouns_change(v): pronouns = sanitize_settings_text(request.values.get("pronouns")) @@ -872,8 +893,9 @@ def settings_pronouns_change(v): @app.post("/settings/checkmark_text") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_checkmark_text(v): if not v.verified: abort(403) @@ -886,6 +908,7 @@ def settings_checkmark_text(v): if IS_FISTMAS(): @app.post("/events/fistmas2022/darkmode") + @limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def event_darkmode(v): diff --git a/files/routes/static.py b/files/routes/static.py index 3761fae6c..16ede5f02 100644 --- a/files/routes/static.py +++ b/files/routes/static.py @@ -227,8 +227,9 @@ def contact(v:Optional[User]): return render_template("contact.html", v=v, msg=get_msg()) @app.post("/contact") -@limiter.limit("1/second;1/2 minutes;10/day") -@limiter.limit("1/second;1/2 minutes;10/day", key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("1/2 minutes;10/day") +@limiter.limit("1/2 minutes;10/day", key_func=get_ID) @auth_required def submit_contact(v): body = request.values.get("message") @@ -321,6 +322,7 @@ def mobile_app(v:Optional[User]): return render_template("app.html", v=v) @app.post("/dismiss_mobile_tip") +@limiter.limit('1/second', scope=path) def dismiss_mobile_tip(): session["tooltip_last_dismissed"] = int(time.time()) return "", 204 diff --git a/files/routes/subs.py b/files/routes/subs.py index 51ba492ec..309ec66a9 100644 --- a/files/routes/subs.py +++ b/files/routes/subs.py @@ -8,6 +8,7 @@ from .front import frontlist from files.__main__ import app, cache, limiter @app.post("/exile/post/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def exile_post(v:User, pid): @@ -40,6 +41,7 @@ def exile_post(v:User, pid): return {"message": f"@{u.username} has been exiled from /h/{sub} successfully!"} @app.post("/exile/comment/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def exile_comment(v:User, cid): @@ -72,6 +74,7 @@ def exile_comment(v:User, cid): return {"message": f"@{u.username} has been exiled from /h/{sub} successfully!"} @app.post("/h//unexile/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def unexile(v:User, sub, uid): @@ -101,6 +104,7 @@ def unexile(v:User, sub, uid): return redirect(f'/h/{sub}/exilees') @app.post("/h//block") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def block_sub(v:User, sub): @@ -113,6 +117,7 @@ def block_sub(v:User, sub): return {"message": f"/h/{sub} blocked successfully!"} @app.post("/h//unblock") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unblock_sub(v:User, sub): @@ -129,6 +134,7 @@ def unblock_sub(v:User, sub): return {"message": f"/h/{sub.name} unblocked successfully!"} @app.post("/h//subscribe") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def subscribe_sub(v:User, sub): @@ -141,6 +147,7 @@ def subscribe_sub(v:User, sub): return {"message": f"/h/{sub} unblocked successfully!"} @app.post("/h//unsubscribe") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unsubscribe_sub(v:User, sub): @@ -152,6 +159,7 @@ def unsubscribe_sub(v:User, sub): return {"message": f"/h/{sub} blocked successfully!"} @app.post("/h//follow") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def follow_sub(v:User, sub): @@ -167,6 +175,7 @@ def follow_sub(v:User, sub): return {"message": f"/h/{sub} followed successfully!"} @app.post("/h//unfollow") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unfollow_sub(v:User, sub): @@ -235,8 +244,9 @@ 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=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("30/day") +@limiter.limit("30/day", key_func=get_ID) @is_not_permabanned def add_mod(v:User, sub): if SITE_NAME == 'WPD': abort(403) @@ -273,6 +283,7 @@ def add_mod(v:User, sub): return redirect(f'/h/{sub}/mods') @app.post("/h//remove_mod") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def remove_mod(v:User, sub): @@ -322,6 +333,7 @@ def create_sub(v): return render_template("sub/create_hole.html", v=v, cost=HOLE_COST, error=get_error()) @app.post("/create_hole") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def create_sub2(v): @@ -356,6 +368,7 @@ def create_sub2(v): return redirect(f'/h/{sub}') @app.post("/kick/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def kick(v:User, pid): @@ -397,8 +410,9 @@ def sub_settings(v:User, sub): @app.post('/h//sidebar') -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def post_sub_sidebar(v:User, sub): sub = get_sub_by_name(sub) @@ -422,8 +436,9 @@ def post_sub_sidebar(v:User, sub): @app.post('/h//css') -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def post_sub_css(v:User, sub): sub = get_sub_by_name(sub) @@ -462,8 +477,9 @@ def get_sub_css(sub): return resp @app.post("/h//settings/banners/") -@limiter.limit("1/second;50/day") -@limiter.limit("1/second;50/day", key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("50/day") +@limiter.limit("50/day", key_func=get_ID) @is_not_permabanned def upload_sub_banner(v:User, sub:str): if g.is_tor: abort(403, "Image uploads are not allowed through Tor") @@ -549,8 +565,9 @@ def delete_all_sub_banners(v:User, sub:str): return {"message": f"Deleted all banners from /h/{sub} successfully"} @app.post("/h//sidebar_image") -@limiter.limit("1/second;10/day") -@limiter.limit("1/second;10/day", key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("10/day") +@limiter.limit("10/day", key_func=get_ID) @is_not_permabanned def sub_sidebar(v:User, sub): if g.is_tor: abort(403, "Image uploads are not allowed through TOR!") @@ -580,8 +597,9 @@ def sub_sidebar(v:User, sub): return redirect(f'/h/{sub}/settings') @app.post("/h//marsey_image") -@limiter.limit("1/second;10/day") -@limiter.limit("1/second;10/day", key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("10/day") +@limiter.limit("10/day", key_func=get_ID) @is_not_permabanned def sub_marsey(v:User, sub): if g.is_tor: abort(403, "Image uploads are not allowed through TOR!") @@ -620,6 +638,7 @@ def subs(v:User): return render_template('sub/subs.html', v=v, subs=subs, total_users=total_users) @app.post("/hole_pin/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def hole_pin(v:User, pid): @@ -649,6 +668,7 @@ def hole_pin(v:User, pid): return {"message": f"Post pinned to /h/{p.sub} successfully!"} @app.post("/hole_unpin/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def hole_unpin(v:User, pid): @@ -679,6 +699,7 @@ def hole_unpin(v:User, pid): @app.post('/h//stealth') +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def sub_stealth(v:User, sub): @@ -711,6 +732,7 @@ def sub_stealth(v:User, sub): @app.post("/mod_pin/") +@limiter.limit('1/second', scope=path) @feature_required('PINS') @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned @@ -740,6 +762,7 @@ def mod_pin(cid, v): return {"message": "Comment pinned!"} @app.post("/unmod_pin/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def mod_unpin(cid, v): diff --git a/files/routes/users.py b/files/routes/users.py index 867644b68..33554f9ea 100644 --- a/files/routes/users.py +++ b/files/routes/users.py @@ -297,9 +297,10 @@ def downvoting(v:User, username:str): return all_upvoters_downvoters(v, username, -1, True) @app.post("/@/suicide") +@limiter.limit('1/second', scope=path) @feature_required('USERS_SUICIDE') -@limiter.limit("1/second;5/day") -@limiter.limit("1/second;5/day", key_func=get_ID) +@limiter.limit("5/day") +@limiter.limit("5/day", key_func=get_ID) @auth_required def suicide(v:User, username:str): user = get_user(username) @@ -357,16 +358,18 @@ def transfer_currency(v:User, username:str, currency_name:Literal['coins', 'mars return {"message": f"{amount - tax} {currency_name} have been transferred to @{receiver.username}"} @app.post("/@/transfer_coins") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def transfer_coins(v:User, username:str): return transfer_currency(v, username, 'coins', True) @app.post("/@/transfer_bux") +@limiter.limit('1/second', scope=path) @feature_required('MARSEYBUX') -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def transfer_bux(v:User, username:str): return transfer_currency(v, username, 'marseybux', False) @@ -452,8 +455,9 @@ def usersong(username:str): else: abort(404) @app.post("/subscribe/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def subscribe(v, post_id): existing = g.db.query(Subscription).filter_by(user_id=v.id, submission_id=post_id).one_or_none() @@ -463,8 +467,9 @@ def subscribe(v, post_id): return {"message": "Subscribed to post successfully!"} @app.post("/unsubscribe/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unsubscribe(v, post_id): existing = g.db.query(Subscription).filter_by(user_id=v.id, submission_id=post_id).one_or_none() @@ -473,8 +478,9 @@ def unsubscribe(v, post_id): return {"message": "Unsubscribed from post successfully!"} @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=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("10/minute;20/hour;50/day") +@limiter.limit("10/minute;20/hour;50/day", key_func=get_ID) @is_not_permabanned def message2(v:User, username:str): user = get_user(username, v=v, include_blocks=True, include_shadowbanned=False) @@ -535,8 +541,9 @@ 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=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("6/minute;50/hour;200/day") +@limiter.limit("6/minute;50/hour;200/day", key_func=get_ID) @auth_required def messagereply(v:User): body = sanitize_raw_body(request.values.get("body"), False) @@ -1045,8 +1052,9 @@ def u_user_id_info(id, v=None): return user.json @app.post("/follow/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def follow_user(username, v): @@ -1072,8 +1080,9 @@ def follow_user(username, v): return {"message": f"@{target.username} has been followed!"} @app.post("/unfollow/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unfollow_user(username, v): @@ -1100,8 +1109,9 @@ def unfollow_user(username, v): return {"message": f"@{target.username} has been unfollowed!"} @app.post("/remove_follow/") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def remove_follow(username, v): target = get_user(username) @@ -1192,6 +1202,7 @@ def subscribed_posts(v:User, username): return get_saves_and_subscribes(v, "userpage/submissions.html", Subscription, page, False) @app.post("/fp/") +@limiter.limit('1/second', scope=path) @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def fp(v:User, fp): @@ -1348,6 +1359,7 @@ if KOFI_TOKEN: return '' @app.post("/gumroad") +@limiter.limit('1/second', scope=path) def gumroad(): data = request.values ip = request.headers.get('CF-Connecting-IP') @@ -1379,8 +1391,9 @@ def gumroad(): @app.post("/settings/claim_rewards") -@limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit(DEFAULT_RATELIMIT) +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_claim_rewards(v:User): if not (v.email and v.is_activated): diff --git a/files/routes/votes.py b/files/routes/votes.py index bc4ef1f0c..1e23cdd4b 100644 --- a/files/routes/votes.py +++ b/files/routes/votes.py @@ -194,16 +194,18 @@ 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=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("60/minute;1000/hour;2000/day") +@limiter.limit("60/minute;1000/hour;2000/day", key_func=get_ID) @is_not_permabanned @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=get_ID) +@limiter.limit('1/second', scope=path) +@limiter.limit("60/minute;1000/hour;2000/day") +@limiter.limit("60/minute;1000/hour;2000/day", key_func=get_ID) @is_not_permabanned @limiter.limit("1/second", key_func=lambda:f'{g.v.id}-{request.full_path}') def vote_comment(comment_id, new, v): diff --git a/files/routes/wrappers.py b/files/routes/wrappers.py index bde1204c9..c514f2063 100644 --- a/files/routes/wrappers.py +++ b/files/routes/wrappers.py @@ -10,6 +10,9 @@ from files.helpers.settings import get_setting from files.routes.routehelpers import validate_formkey from files.__main__ import app, db_session, limiter +def path(n): + return request.path + def get_ID(): if request.headers.get("Authorization"): x = request.headers.get("Authorization")