From 3d1facaec306d453d5749fc49ad6772ebbafff7e Mon Sep 17 00:00:00 2001 From: Mia Herkt Date: Wed, 29 Mar 2023 07:21:36 +0200 Subject: [PATCH] Store user agent with files Needed for moderation. --- fhost.py | 20 ++++++++----- ...b7d2_store_user_agent_string_with_files.py | 30 +++++++++++++++++++ mod.py | 4 +++ 3 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 migrations/versions/dd0766afb7d2_store_user_agent_string_with_files.py diff --git a/fhost.py b/fhost.py index 0a04256..3d45c4d 100755 --- a/fhost.py +++ b/fhost.py @@ -135,6 +135,7 @@ class File(db.Model): ext = db.Column(db.UnicodeText) mime = db.Column(db.UnicodeText) addr = db.Column(db.UnicodeText) + ua = db.Column(db.UnicodeText) removed = db.Column(db.Boolean, default=False) nsfw_score = db.Column(db.Float) expiration = db.Column(db.BigInteger) @@ -143,11 +144,12 @@ class File(db.Model): last_vscan = db.Column(db.DateTime) size = db.Column(db.BigInteger) - def __init__(self, sha256, ext, mime, addr, expiration, mgmt_token): + def __init__(self, sha256, ext, mime, addr, ua, expiration, mgmt_token): self.sha256 = sha256 self.ext = ext self.mime = mime self.addr = addr + self.ua = ua self.expiration = expiration self.mgmt_token = mgmt_token @@ -212,7 +214,7 @@ class File(db.Model): Any value greater that the longest allowed file lifespan will be rounded down to that value. """ - def store(file_, requested_expiration: typing.Optional[int], addr, secret: bool): + def store(file_, requested_expiration: typing.Optional[int], addr, ua, secret: bool): data = file_.read() digest = sha256(data).hexdigest() @@ -278,9 +280,10 @@ class File(db.Model): mime = get_mime() ext = get_ext(mime) mgmt_token = secrets.token_urlsafe() - f = File(digest, ext, mime, addr, expiration, mgmt_token) + f = File(digest, ext, mime, addr, ua, expiration, mgmt_token) f.addr = addr + f.ua = ua if isnew: f.secret = None @@ -368,11 +371,11 @@ requested_expiration can be: Any value greater that the longest allowed file lifespan will be rounded down to that value. """ -def store_file(f, requested_expiration: typing.Optional[int], addr, secret: bool): +def store_file(f, requested_expiration: typing.Optional[int], addr, ua, secret: bool): if in_upload_bl(addr): return "Your host is blocked from uploading files.\n", 451 - sf, isnew = File.store(f, requested_expiration, addr, secret) + sf, isnew = File.store(f, requested_expiration, addr, ua, secret) response = make_response(sf.geturl()) response.headers["X-Expires"] = sf.expiration @@ -382,7 +385,7 @@ def store_file(f, requested_expiration: typing.Optional[int], addr, secret: boo return response -def store_url(url, addr, secret: bool): +def store_url(url, addr, ua, secret: bool): if is_fhost_url(url): abort(400) @@ -403,7 +406,7 @@ def store_url(url, addr, secret: bool): f = urlfile(read=r.raw.read, content_type=r.headers["content-type"], filename="") - return store_file(f, None, addr, secret) + return store_file(f, None, addr, ua, secret) else: abort(413) else: @@ -498,6 +501,7 @@ def fhost(): request.files["file"], int(request.form["expires"]), request.remote_addr, + request.user_agent.string, secret ) except ValueError: @@ -509,12 +513,14 @@ def fhost(): request.files["file"], None, request.remote_addr, + request.user_agent.string, secret ) elif "url" in request.form: return store_url( request.form["url"], request.remote_addr, + request.user_agent.string, secret ) elif "shorten" in request.form: diff --git a/migrations/versions/dd0766afb7d2_store_user_agent_string_with_files.py b/migrations/versions/dd0766afb7d2_store_user_agent_string_with_files.py new file mode 100644 index 0000000..4af7680 --- /dev/null +++ b/migrations/versions/dd0766afb7d2_store_user_agent_string_with_files.py @@ -0,0 +1,30 @@ +"""Store user agent string with files + +Revision ID: dd0766afb7d2 +Revises: 30bfe33aa328 +Create Date: 2023-03-29 07:18:49.113200 + +""" + +# revision identifiers, used by Alembic. +revision = 'dd0766afb7d2' +down_revision = '30bfe33aa328' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('file', schema=None) as batch_op: + batch_op.add_column(sa.Column('ua', sa.UnicodeText(), nullable=True)) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('file', schema=None) as batch_op: + batch_op.drop_column('ua') + + # ### end Alembic commands ### diff --git a/mod.py b/mod.py index 9a68e79..559fee8 100755 --- a/mod.py +++ b/mod.py @@ -26,6 +26,7 @@ class NullptrMod(Screen): ("f4", "filter(4, 'Filter extension:')", "Filter Ext."), ("f5", "refresh", "Refresh"), ("f6", "filter_clear", "Clear filter"), + ("f7", "filter(5, 'Filter user agent:')", "Filter UA"), ("r", "remove_file(False)", "Remove file"), ("ctrl+r", "remove_file(True)", "Ban file"), ("p", "ban_ip(False)", "Ban IP"), @@ -60,6 +61,7 @@ class NullptrMod(Screen): case 2: finput.value = self.current_file.addr case 3: finput.value = self.current_file.mime case 4: finput.value = self.current_file.ext + case 5: finput.value = self.current_file.ua def on_input_submitted(self, message: Input.Submitted) -> None: self.query_one("#filter_container").display = False @@ -74,6 +76,7 @@ class NullptrMod(Screen): case 2: ftable.query = ftable.base_query.filter(File.addr == message.value) case 3: ftable.query = ftable.base_query.filter(File.mime.like(message.value)) case 4: ftable.query = ftable.base_query.filter(File.ext.like(message.value)) + case 5: ftable.query = ftable.base_query.filter(File.ua.like(message.value)) else: ftable.query = ftable.base_query @@ -249,6 +252,7 @@ class NullptrMod(Screen): ("MIME type:", f.mime), ("SHA256 checksum:", f.sha256), ("Uploaded by:", Text(f.addr)), + ("User agent:", Text(f.ua or "")), ("Management token:", f.mgmt_token), ("Secret:", f.secret), ("Is NSFW:", ("Yes" if f.is_nsfw else "No") + (f" (Score: {f.nsfw_score:0.4f})" if f.nsfw_score else " (Not scanned)")),