def POST_message(self, level, logs): # Whitelist tags to keep the frontend from creating too many keys in statsd valid_frontend_log_tags = { 'unknown', 'jquery-migrate-bad-html', } # prevent simple CSRF by requiring a custom header if not request.headers.get('X-Loggit'): abort(403) uid = c.user._id if c.user_is_loggedin else '-' # only accept a maximum of 3 entries per request for log in logs[:3]: if 'msg' not in log or 'url' not in log: continue tag = 'unknown' if log.get('tag') in valid_frontend_log_tags: tag = log['tag'] g.stats.simple_event('frontend.error.' + tag) g.log.warning('[web frontend] %s: %s | U: %s FP: %s UA: %s', level, log['msg'], uid, log['url'], request.user_agent) VRatelimit.ratelimit(rate_user=False, rate_ip=True, prefix="rate_weblog_", seconds=10)
def POST_create(self, form, jquery, title, description, resources, nsfw): """Create a new live thread. Once created, the initial settings can be modified with [/api/live/*thread*/edit](#POST_api_live_{thread}_edit) and new updates can be posted with [/api/live/*thread*/update](#POST_api_live_{thread}_update). """ if not is_event_configuration_valid(form): return if form.has_errors("ratelimit", errors.RATELIMIT): return VRatelimit.ratelimit( rate_user=True, prefix="liveupdate_create_", seconds=60) event = LiveUpdateEvent.new( id=None, title=title, description=description, resources=resources, banned=c.user._spam, nsfw=nsfw, ) event.add_contributor(c.user, ContributorPermissionSet.SUPERUSER) queries.create_event(event) form.redirect("/live/" + event._id) form._send_data(id=event._id)
def POST_create(self, form, jquery, title, description, resources, nsfw): """Create a new live thread. Once created, the initial settings can be modified with [/api/live/*thread*/edit](#POST_api_live_{thread}_edit) and new updates can be posted with [/api/live/*thread*/update](#POST_api_live_{thread}_update). """ if not is_event_configuration_valid(form): return if form.has_errors("ratelimit", errors.RATELIMIT): return VRatelimit.ratelimit( rate_user=True, prefix="liveupdate_create_", seconds=60) event = LiveUpdateEvent.new( id=None, title=title, description=description, resources=resources, banned=c.user._spam, nsfw=nsfw, ) event.add_contributor(c.user, ContributorPermissionSet.SUPERUSER) queries.create_event(event) form.redirect("/live/" + event._id) form._send_data(id=event._id)
def POST_message(self, level, logs): # Whitelist tags to keep the frontend from creating too many keys in statsd valid_frontend_log_tags = { 'unknown', 'jquery-migrate-bad-html', } # prevent simple CSRF by requiring a custom header if not request.headers.get('X-Loggit'): abort(403) uid = c.user._id if c.user_is_loggedin else '-' # only accept a maximum of 3 entries per request for log in logs[:3]: if 'msg' not in log or 'url' not in log: continue tag = 'unknown' if log.get('tag') in valid_frontend_log_tags: tag = log['tag'] g.stats.simple_event('frontend.error.' + tag) g.log.warning('[web frontend] %s: %s | U: %s FP: %s UA: %s', level, log['msg'], uid, log['url'], request.user_agent) VRatelimit.ratelimit(rate_user=False, rate_ip=True, prefix="rate_weblog_", seconds=10)
def POST_message(self, level, logs): # Whitelist tags to keep the frontend from creating too many keys in statsd valid_frontend_log_tags = {"unknown", "jquery-migrate-bad-html"} # prevent simple CSRF by requiring a custom header if not request.headers.get("X-Loggit"): abort(403) uid = c.user._id if c.user_is_loggedin else "-" # only accept a maximum of 3 entries per request for log in logs[:3]: if "msg" not in log or "url" not in log: continue tag = "unknown" if log.get("tag") in valid_frontend_log_tags: tag = log["tag"] g.stats.simple_event("frontend.error." + tag) g.log.warning( "[web frontend] %s: %s | U: %s FP: %s UA: %s", level, log["msg"], uid, log["url"], request.user_agent ) VRatelimit.ratelimit(rate_user=False, rate_ip=True, prefix="rate_weblog_", seconds=10)
def POST_message(self, level, logs): # prevent simple CSRF by requiring a custom header if not request.headers.get('X-Loggit'): abort(403) uid = c.user._id if c.user_is_loggedin else '-' # only accept a maximum of 3 entries per request for log in logs[:3]: g.log.warning('[web frontend] %s: %s | U: %s FP: %s UA: %s', level, log['msg'], uid, log['url'], request.user_agent) VRatelimit.ratelimit(rate_user=False, rate_ip=True, prefix="rate_weblog_", seconds=10)
def POST_create(self, form, jquery, title, description, resources, nsfw): """Create a new live thread. Once created, the initial settings can be modified with [/api/live/*thread*/edit](#POST_api_live_{thread}_edit) and new updates can be posted with [/api/live/*thread*/update](#POST_api_live_{thread}_update). """ if not is_event_configuration_valid(form): return # for simplicity, set the live-thread creation threshold at the # subreddit creation threshold if not c.user_is_admin and not c.user.can_create_subreddit: form.set_error(errors.CANT_CREATE_SR, "") c.errors.add(errors.CANT_CREATE_SR, field="") return if form.has_errors("ratelimit", errors.RATELIMIT): return VRatelimit.ratelimit(rate_user=True, prefix="liveupdate_create_", seconds=60) event = LiveUpdateEvent.new( id=None, title=title, description=description, resources=resources, banned=c.user._spam, nsfw=nsfw, ) event.add_contributor(c.user, ContributorPermissionSet.SUPERUSER) queries.create_event(event) amqp.add_item( "new_liveupdate_event", json.dumps({ "event_fullname": event._fullname, "creator_fullname": c.user._fullname, })) form.redirect("/live/" + event._id) form._send_data(id=event._id) liveupdate_events.create_event(event, context=c, request=request)
def POST_create(self, form, jquery, title, description): if not is_event_configuration_valid(form): return if form.has_errors("ratelimit", errors.RATELIMIT): return VRatelimit.ratelimit( rate_user=True, prefix="liveupdate_create_", seconds=60) event = LiveUpdateEvent.new( id=None, title=title, banned=c.user._spam, ) event.add_contributor(c.user, ContributorPermissionSet.SUPERUSER) queries.create_event(event) form.redirect("/live/" + event._id)
def POST_create(self, form, jquery, title, description, resources, nsfw): """Create a new live thread. Once created, the initial settings can be modified with [/api/live/*thread*/edit](#POST_api_live_{thread}_edit) and new updates can be posted with [/api/live/*thread*/update](#POST_api_live_{thread}_update). """ if not is_event_configuration_valid(form): return # for simplicity, set the live-thread creation threshold at the # subreddit creation threshold if not c.user_is_admin and not c.user.can_create_subreddit: form.set_error(errors.CANT_CREATE_SR, "") c.errors.add(errors.CANT_CREATE_SR, field="") return if form.has_errors("ratelimit", errors.RATELIMIT): return VRatelimit.ratelimit( rate_user=True, prefix="liveupdate_create_", seconds=60) event = LiveUpdateEvent.new( id=None, title=title, description=description, resources=resources, banned=c.user._spam, nsfw=nsfw, ) event.add_contributor(c.user, ContributorPermissionSet.SUPERUSER) queries.create_event(event) form.redirect("/live/" + event._id) form._send_data(id=event._id) liveupdate_events.create_event(event, context=c, request=request)
def POST_nominate(self, form, jquery, organization): if not feature.is_enabled('reddit_donate'): return self.abort404() if form.has_errors("organization", errors.DONATE_UNKNOWN_ORGANIZATION): return if form.has_errors("eligible", errors.DONATE_ACCOUNT_NOT_ELIGIBLE): return if form.has_errors("ratelimit", errors.RATELIMIT): return else: VRatelimit.ratelimit( rate_user=True, prefix="donate_nominate_", seconds=NOMINATION_COOLDOWN, ) DonationNominationsByAccount.nominate( c.user, organization, )
def POST_nominate(self, form, jquery, organization): if not feature.is_enabled('reddit_donate'): return self.abort404() if form.has_errors("organization", errors.DONATE_UNKNOWN_ORGANIZATION): return if form.has_errors("eligible", errors.DONATE_ACCOUNT_NOT_ELIGIBLE): return if form.has_errors("ratelimit", errors.RATELIMIT): return else: VRatelimit.ratelimit( rate_user=True, prefix="donate_nominate_", seconds=NOMINATION_COOLDOWN, ) DonationNominationsByAccount.nominate( c.user, organization, )
def POST_report_cache_poisoning( self, report_mac, poisoner_name, poisoner_id, poisoner_canary, victim_canary, render_time, route_name, url, source, cache_policy, resp_headers, ): """Report an instance of cache poisoning and its details""" self.OPTIONS_report_cache_poisoning() if c.errors: abort(400) # prevent simple CSRF by requiring a custom header if not request.headers.get('X-Loggit'): abort(403) # Eh? Why are you reporting this if the canaries are the same? if poisoner_canary == victim_canary: abort(400) expected_mac = make_poisoning_report_mac( poisoner_canary=poisoner_canary, poisoner_name=poisoner_name, poisoner_id=poisoner_id, cache_policy=cache_policy, source=source, route_name=route_name, ) if not constant_time_compare(report_mac, expected_mac): abort(403) if resp_headers: try: resp_headers = json.loads(resp_headers) # Verify this is a JSON map of `header_name => [value, ...]` if not isinstance(resp_headers, dict): abort(400) for hdr_name, hdr_vals in resp_headers.iteritems(): if not isinstance(hdr_name, basestring): abort(400) if not all(isinstance(h, basestring) for h in hdr_vals): abort(400) except ValueError: abort(400) if not resp_headers: resp_headers = {} poison_info = dict( poisoner_name=poisoner_name, poisoner_id=str(poisoner_id), # Convert the JS timestamp to a standard one render_time=render_time * 1000, route_name=route_name, url=url, source=source, cache_policy=cache_policy, resp_headers=resp_headers, ) # For immediate feedback when tracking the effects of caching changes g.stats.simple_event("cache.poisoning.%s.%s" % (source, cache_policy)) # For longer-term diagnosing of caching issues g.events.cache_poisoning_event(poison_info, request=request, context=c) VRatelimit.ratelimit(rate_ip=True, prefix="rate_poison_", seconds=10) return self.api_wrapper({})
def POST_report_cache_poisoning( self, report_mac, poisoner_name, poisoner_id, poisoner_canary, victim_canary, render_time, route_name, url, source, cache_policy, resp_headers, ): """Report an instance of cache poisoning and its details""" self.OPTIONS_report_cache_poisoning() if c.errors: abort(400) # prevent simple CSRF by requiring a custom header if not request.headers.get('X-Loggit'): abort(403) # Eh? Why are you reporting this if the canaries are the same? if poisoner_canary == victim_canary: abort(400) expected_mac = make_poisoning_report_mac( poisoner_canary=poisoner_canary, poisoner_name=poisoner_name, poisoner_id=poisoner_id, cache_policy=cache_policy, source=source, route_name=route_name, ) if not constant_time_compare(report_mac, expected_mac): abort(403) if resp_headers: try: resp_headers = json.loads(resp_headers) # Verify this is a JSON map of `header_name => [value, ...]` if not isinstance(resp_headers, dict): abort(400) for hdr_name, hdr_vals in resp_headers.iteritems(): if not isinstance(hdr_name, basestring): abort(400) if not all(isinstance(h, basestring) for h in hdr_vals): abort(400) except ValueError: abort(400) if not resp_headers: resp_headers = {} poison_info = dict( poisoner_name=poisoner_name, poisoner_id=str(poisoner_id), # Convert the JS timestamp to a standard one render_time=render_time * 1000, route_name=route_name, url=url, source=source, cache_policy=cache_policy, resp_headers=resp_headers, ) # For immediate feedback when tracking the effects of caching changes g.stats.simple_event("cache.poisoning.%s.%s" % (source, cache_policy)) # For longer-term diagnosing of caching issues g.events.cache_poisoning_event(poison_info, request=request, context=c) VRatelimit.ratelimit(rate_ip=True, prefix="rate_poison_", seconds=10) return self.api_wrapper({})
def handle_register( controller, form, responder, name, email, password, rem=None, newsletter_subscribe=False, sponsor=False, signature=None, **kwargs ): def _event(error): g.events.login_event( 'register_attempt', error_msg=error, user_name=request.urlvars.get('url_user'), email=request.POST.get('email'), remember_me=rem, newsletter=newsletter_subscribe, signature=signature, request=request, context=c) if signature and not signature.is_valid(): _event(error="SIGNATURE") abort(403) if responder.has_errors('user', errors.USERNAME_TOO_SHORT): _event(error='USERNAME_TOO_SHORT') elif responder.has_errors('user', errors.USERNAME_INVALID_CHARACTERS): _event(error='USERNAME_INVALID_CHARACTERS') elif responder.has_errors('user', errors.USERNAME_TAKEN_DEL): _event(error='USERNAME_TAKEN_DEL') elif responder.has_errors('user', errors.USERNAME_TAKEN): _event(error='USERNAME_TAKEN') elif responder.has_errors('email', errors.BAD_EMAIL): _event(error='BAD_EMAIL') elif responder.has_errors('passwd', errors.SHORT_PASSWORD): _event(error='SHORT_PASSWORD') elif responder.has_errors('passwd', errors.BAD_PASSWORD): # BAD_PASSWORD is set when SHORT_PASSWORD is set _event(error='BAD_PASSWORD') elif responder.has_errors('passwd2', errors.BAD_PASSWORD_MATCH): _event(error='BAD_PASSWORD_MATCH') elif responder.has_errors('ratelimit', errors.RATELIMIT): _event(error='RATELIMIT') elif (not g.disable_captcha and responder.has_errors('captcha', errors.BAD_CAPTCHA)): _event(error='BAD_CAPTCHA') elif newsletter_subscribe and not email: c.errors.add(errors.NEWSLETTER_NO_EMAIL, field="email") form.has_errors("email", errors.NEWSLETTER_NO_EMAIL) _event(error='NEWSLETTER_NO_EMAIL') elif sponsor and not email: c.errors.add(errors.SPONSOR_NO_EMAIL, field="email") form.has_errors("email", errors.SPONSOR_NO_EMAIL) _event(error='SPONSOR_NO_EMAIL') else: try: user = register(name, password, request.ip) except AccountExists: c.errors.add(errors.USERNAME_TAKEN, field="user") form.has_errors("user", errors.USERNAME_TAKEN) _event(error='USERNAME_TAKEN') return VRatelimit.ratelimit(rate_ip=True, prefix="rate_register_") # anything else we know (email, languages)? if email: user.set_email(email) emailer.verify_email(user) user.pref_lang = c.lang user._commit() amqp.add_item('new_account', user._fullname) hooks.get_hook("account.registered").call(user=user) reject = hooks.get_hook("account.spotcheck").call(account=user) if any(reject): _event(error='ACCOUNT_SPOTCHECK') return if newsletter_subscribe and email: try: newsletter.add_subscriber(email, source="register") except newsletter.NewsletterError as e: g.log.warning("Failed to subscribe: %r" % e) controller._login(responder, user, rem) _event(error=None)
def handle_register(controller, form, responder, name, email, password, rem=None, newsletter_subscribe=False, sponsor=False, signature=None, **kwargs): def _event(error): g.events.login_event('register_attempt', error_msg=error, user_name=request.urlvars.get('url_user'), email=request.POST.get('email'), remember_me=rem, newsletter=newsletter_subscribe, signature=signature, request=request, context=c) if signature and not signature.is_valid(): _event(error="SIGNATURE") abort(403) if responder.has_errors('user', errors.USERNAME_TOO_SHORT): _event(error='USERNAME_TOO_SHORT') elif responder.has_errors('user', errors.USERNAME_INVALID_CHARACTERS): _event(error='USERNAME_INVALID_CHARACTERS') elif responder.has_errors('user', errors.USERNAME_TAKEN_DEL): _event(error='USERNAME_TAKEN_DEL') elif responder.has_errors('user', errors.USERNAME_TAKEN): _event(error='USERNAME_TAKEN') elif responder.has_errors('email', errors.BAD_EMAIL): _event(error='BAD_EMAIL') elif responder.has_errors('passwd', errors.SHORT_PASSWORD): _event(error='SHORT_PASSWORD') elif responder.has_errors('passwd', errors.BAD_PASSWORD): # BAD_PASSWORD is set when SHORT_PASSWORD is set _event(error='BAD_PASSWORD') elif responder.has_errors('passwd2', errors.BAD_PASSWORD_MATCH): _event(error='BAD_PASSWORD_MATCH') elif responder.has_errors('ratelimit', errors.RATELIMIT): _event(error='RATELIMIT') elif (not g.disable_captcha and responder.has_errors('captcha', errors.BAD_CAPTCHA)): _event(error='BAD_CAPTCHA') #Blockpath may require email elif not email and g.config.get('verifyemailrequired', ()): c.errors.add(errors.NO_EMAIL, field="email") form.has_errors("email", errors.NO_EMAIL) _event( error='NO_EMAIL') #might already be set by ValidEmail validator?? elif newsletter_subscribe and not email: c.errors.add(errors.NEWSLETTER_NO_EMAIL, field="email") form.has_errors("email", errors.NEWSLETTER_NO_EMAIL) _event(error='NEWSLETTER_NO_EMAIL') elif sponsor and not email: c.errors.add(errors.SPONSOR_NO_EMAIL, field="email") form.has_errors("email", errors.SPONSOR_NO_EMAIL) _event(error='SPONSOR_NO_EMAIL') else: try: user = register(name, password, request.ip) except AccountExists: c.errors.add(errors.USERNAME_TAKEN, field="user") form.has_errors("user", errors.USERNAME_TAKEN) _event(error='USERNAME_TAKEN') return VRatelimit.ratelimit(rate_ip=True, prefix="rate_register_", seconds=86400) # anything else we know (email, languages)? if email: user.set_email(email) emailer.verify_email(user) user.pref_lang = c.lang user._commit() amqp.add_item('new_account', user._fullname) hooks.get_hook("account.registered").call(user=user) reject = hooks.get_hook("account.spotcheck").call(account=user) if any(reject): _event(error='ACCOUNT_SPOTCHECK') return if newsletter_subscribe and email: try: newsletter.add_subscriber(email, source="register") except newsletter.NewsletterError as e: g.log.warning("Failed to subscribe: %r" % e) controller._login(responder, user, rem) _event(error=None)
def handle_register( controller, form, responder, name, email, password, rem=None, newsletter_subscribe=False, sponsor=False, signature=None, **kwargs ): # check captcha before register (if any) since its answer might # change once c.user is set. captcha_shown = not signature and need_provider_captcha("register") def _event(error, captcha_shown=captcha_shown): g.events.login_event( 'register_attempt', error_msg=error, user_name=request.urlvars.get('url_user'), email=request.POST.get('email'), remember_me=rem, newsletter=newsletter_subscribe, captcha_shown=captcha_shown, signature=signature, request=request, context=c) if signature and not signature.is_valid(): _event(error="SIGNATURE") abort(403) if responder.has_errors('user', errors.USERNAME_TOO_SHORT): _event(error='USERNAME_TOO_SHORT') elif responder.has_errors('user', errors.USERNAME_INVALID_CHARACTERS): _event(error='USERNAME_INVALID_CHARACTERS') elif responder.has_errors('user', errors.USERNAME_TAKEN_DEL): _event(error='USERNAME_TAKEN_DEL') elif responder.has_errors('user', errors.USERNAME_TAKEN): _event(error='USERNAME_TAKEN') elif responder.has_errors('email', errors.BAD_EMAIL): _event(error='BAD_EMAIL') elif responder.has_errors('passwd', errors.SHORT_PASSWORD): _event(error='SHORT_PASSWORD') elif responder.has_errors('passwd', errors.BAD_PASSWORD): # BAD_PASSWORD is set when SHORT_PASSWORD is set _event(error='BAD_PASSWORD') elif responder.has_errors('passwd2', errors.BAD_PASSWORD_MATCH): _event(error='BAD_PASSWORD_MATCH') elif responder.has_errors('ratelimit', errors.RATELIMIT): _event(error='RATELIMIT') elif newsletter_subscribe and not email: c.errors.add(errors.NEWSLETTER_NO_EMAIL, field="email") form.has_errors("email", errors.NEWSLETTER_NO_EMAIL) _event(error='NEWSLETTER_NO_EMAIL') elif sponsor and not email: c.errors.add(errors.SPONSOR_NO_EMAIL, field="email") form.has_errors("email", errors.SPONSOR_NO_EMAIL) _event(error='SPONSOR_NO_EMAIL') # last but not least, we have to check the captcha elif (not signature and not g.disable_captcha and not valid_provider_captcha(responder, "register")): _event(error='BAD_CAPTCHA') else: try: user = register(name, password, request.ip) except AccountExists: c.errors.add(errors.USERNAME_TAKEN, field="user") form.has_errors("user", errors.USERNAME_TAKEN) _event(error='USERNAME_TAKEN') return VRatelimit.ratelimit(rate_ip=True, prefix="rate_register_") # anything else we know (email, languages)? if email: user.set_email(email) emailer.verify_email(user) user.pref_lang = c.lang if (is_api("html") and feature.is_enabled("new_user_onboarding", user=user)): user.has_been_onboarded = False user._commit() amqp.add_item('new_account', user._fullname) hooks.get_hook("account.registered").call(user=user) reject = hooks.get_hook("account.spotcheck").call(account=user) if any(reject): _event(error='ACCOUNT_SPOTCHECK') return if newsletter_subscribe and email: try: newsletter.add_subscriber(email, source="register") except newsletter.NewsletterError as e: g.log.warning("Failed to subscribe: %r" % e) controller._login(responder, user, rem) _event(error=None)