fix #35
Also add an option `direct-reply` to control the number of comments on a thread without referencing a child (to avoid a simple while loop that `curl -XPOST ...` the url). Defaults to 3, that means a /24 (or /48 for IPv6) address can only post 3 direct responses on a thread at all.pull/38/head
parent
f0ee0a18b1
commit
9f2062a900
@ -1,47 +1,55 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
import functools
|
||||||
|
|
||||||
from isso.db import IssoDBException
|
|
||||||
|
|
||||||
|
class Guard:
|
||||||
|
|
||||||
class TooManyComments(IssoDBException):
|
def __init__(self, db):
|
||||||
pass
|
|
||||||
|
|
||||||
|
self.db = db
|
||||||
|
self.conf = db.conf.section("guard")
|
||||||
|
|
||||||
def check(func):
|
def validate(self, uri, comment):
|
||||||
|
|
||||||
def dec(self, uri, c):
|
if not self.conf.getboolean("enabled"):
|
||||||
|
return True, ""
|
||||||
|
|
||||||
if not self.db.conf.getboolean("guard", "enabled"):
|
for func in (self._limit, self._spam):
|
||||||
return func(self, uri, c)
|
valid, reason = func(uri, comment)
|
||||||
|
if not valid:
|
||||||
|
return False, reason
|
||||||
|
return True, ""
|
||||||
|
|
||||||
# block more than two comments per minute
|
@classmethod
|
||||||
rv = self.db.execute([
|
def ids(cls, rv):
|
||||||
'SELECT id FROM comments WHERE remote_addr = ? AND created > ?;'
|
return [str(col[0]) for col in rv]
|
||||||
], (c["remote_addr"], time.time() + 60)).fetchall()
|
|
||||||
|
|
||||||
if len(rv) >= self.db.conf.getint("guard", "ratelimit"):
|
def _limit(self, uri, comment):
|
||||||
raise TooManyComments
|
|
||||||
|
|
||||||
# block more than three comments as direct response to the post
|
# block more than :param:`ratelimit` comments per minute
|
||||||
rv = self.db.execute([
|
rv = self.db.execute([
|
||||||
'SELECT id FROM comments WHERE remote_addr = ? AND parent IS NULL;'
|
'SELECT id FROM comments WHERE remote_addr = ? AND ? - created < 60;'
|
||||||
], (c["remote_addr"], )).fetchall()
|
], (comment["remote_addr"], time.time())).fetchall()
|
||||||
|
|
||||||
if len(rv) >= 3:
|
if len(rv) >= self.conf.getint("ratelimit"):
|
||||||
raise TooManyComments
|
return False, "{0}: ratelimit exceeded ({1})".format(
|
||||||
|
comment["remote_addr"], ', '.join(Guard.ids(rv)))
|
||||||
|
|
||||||
# block reply to own comment if the cookie is still available (max age)
|
# block more than three comments as direct response to the post
|
||||||
if "parent" in c:
|
if comment["parent"] is None:
|
||||||
rv = self.db.execute([
|
rv = self.db.execute([
|
||||||
'SELECT id FROM comments WHERE remote_addr = ? AND id = ? AND ? - created < ?;'
|
'SELECT id FROM comments WHERE',
|
||||||
], (c["remote_addr"], c["parent"], time.time(),
|
' tid = (SELECT id FROM threads WHERE uri = ?)',
|
||||||
self.db.conf.getint("general", "max-age"))).fetchall()
|
'AND remote_addr = ?',
|
||||||
|
'AND parent IS NULL;'
|
||||||
|
], (uri, comment["remote_addr"])).fetchall()
|
||||||
|
|
||||||
if len(rv) > 0:
|
if len(rv) >= self.conf.getint("direct-reply"):
|
||||||
raise TooManyComments
|
return False, "%i direct responses to %s" % (len(rv), uri)
|
||||||
|
|
||||||
return func(self, uri, c)
|
return True, ""
|
||||||
|
|
||||||
return dec
|
def _spam(self, uri, comment):
|
||||||
|
return True, ""
|
||||||
|
Loading…
Reference in new issue