diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index b7ad2f868..000000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,70 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ master ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ master ] - schedule: - - cron: '18 19 * * 1' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'javascript', 'python' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://git.io/codeql-language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 87af76111..d5f6da0a6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -42,7 +42,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -53,7 +53,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -67,4 +67,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 \ No newline at end of file diff --git a/.github/workflows/ossar.yml b/.github/workflows/ossar.yml index 4cfa76d5b..726eb0c3f 100644 --- a/.github/workflows/ossar.yml +++ b/.github/workflows/ossar.yml @@ -22,6 +22,8 @@ jobs: # OSSAR runs on windows-latest. # ubuntu-latest and macos-latest support coming soon runs-on: windows-latest + permissions: + security-events: write steps: - name: Checkout repository @@ -44,6 +46,6 @@ jobs: # Upload results to the Security tab - name: Upload OSSAR results - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v2 with: - sarif_file: ${{ steps.ossar.outputs.sarifFile }} + sarif_file: ${{ steps.ossar.outputs.sarifFile }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..b4eacda94 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,14 @@ +name: "run_tests.py" + +on: [push, pull_request] + +jobs: + analyze: + runs-on: ubuntu-20.04 + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: run_tests.py + run: | + ./run_tests.py \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 691c16797..ac99b2d20 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,7 @@ version: '2.3' services: files: + container_name: "rDrama" build: context: . volumes: diff --git a/files/helpers/const.py b/files/helpers/const.py index 72a9ae29b..7d0b2fe9d 100644 --- a/files/helpers/const.py +++ b/files/helpers/const.py @@ -26,25 +26,18 @@ AJ_REPLACEMENTS = { ' YOUR ': " YOU'RE ", ' TO ': " TOO ", + + 'anybody': 'anypony', + 'everybody': 'everypony', + + 'Anybody': 'Anypony', + 'Everybody': 'Everypony', + + 'ANYBODY': 'ANYPONY', + 'EVERYBODY': 'EVERYPONY', } -if SITE_NAME == 'Cringetopia': - SLURS = { - "retarded": "neurodivergent", - "retard": "neurodivergent", - "faggot": "cute twink", - "fag": "cute twink", - "n1gger": "🏀", - "nlgger": "🏀", - "nigger": "🏀", - "uss liberty incident": "tragic accident aboard the USS Liberty", - "lavon affair": "Lavon Misunderstanding", - "i hate marsey": "i love marsey", - "autistic": "neurodivergent", - "holohoax": "i tried to claim the Holocaust didn't happen because I am a pencil-dicked imbecile and the word filter caught me lol", - "i hate carp": "i love Carp", - "heil hitler": "hello kitty", } -else: +if SITE_NAME == 'rDrama': SLURS = { "california": "commiefornia", "hollywood": "hollyweird", @@ -95,9 +88,9 @@ else: "pedocord": "discord (actually a pretty cool service)", "i hate carp": "i love Carp", "manlet": "little king", - "gamer": "g\*mer", - "journalist": "journ\*list", - "journalism": "journ\*lism", + "gamer": "g*mer", + "journalist": "journ*list", + "journalism": "journ*lism", "wuhan flu": "SARS-CoV-2 syndemic", "china flu": "SARS-CoV-2 syndemic", "china virus": "SARS-CoV-2 syndemic", @@ -111,6 +104,23 @@ else: " pedo ": " libertarian ", " pedos ": " libertarians ", } +else: + SLURS = { + "retarded": "neurodivergent", + "retard": "neurodivergent", + "faggot": "cute twink", + "fag": "cute twink", + "n1gger": "🏀", + "nlgger": "🏀", + "nigger": "🏀", + "uss liberty incident": "tragic accident aboard the USS Liberty", + "lavon affair": "Lavon Misunderstanding", + "i hate marsey": "i love marsey", + "autistic": "neurodivergent", + "holohoax": "i tried to claim the Holocaust didn't happen because I am a pencil-dicked imbecile and the word filter caught me lol", + "i hate carp": "i love Carp", + "heil hitler": "hello kitty", + } single_words = "|".join([slur.lower() for slur in SLURS.keys()]) diff --git a/files/helpers/sanitize.py b/files/helpers/sanitize.py index 89b5d5b59..d45cec26e 100644 --- a/files/helpers/sanitize.py +++ b/files/helpers/sanitize.py @@ -42,8 +42,7 @@ def allowed_attributes(tag, name, value): if name == 'loading' and value == 'lazy': return True if name == 'referrpolicy' and value == 'no-referrer': return True if name == 'data-bs-toggle' and value == 'tooltip': return True - if name in ['alt','title','g','b','pat']: return True - if name == 'class' and value == 'pat-hand': return True + if name in ['alt','title','g','b']: return True return False if tag == 'lite-youtube': @@ -71,7 +70,6 @@ def allowed_attributes(tag, name, value): return False if tag == 'span': - if name == 'class' and value in ['pat-container', 'pat-hand']: return True if name == 'data-bs-toggle' and value == 'tooltip': return True if name == 'title': return True if name == 'alt': return True @@ -81,8 +79,17 @@ def allowed_attributes(tag, name, value): url_re = build_url_re(tlds=TLDS, protocols=['http', 'https']) def callback(attrs, new=False): + if (None, "href") not in attrs: + return # Incorrect tag + href = attrs[(None, "href")] + # \ in href right after / makes most browsers ditch site hostname and allows for a host injection bypassing the check, see cool + if "\\" in href: + attrs["_text"] = href # Laugh at this user + del attrs[(None, "href")] # Make unclickable and reset harmful payload + return attrs + if not href.startswith('/') and not href.startswith(f'{SITE_FULL}/'): attrs[(None, "target")] = "_blank" attrs[(None, "rel")] = "nofollow noopener noreferrer" @@ -117,17 +124,16 @@ def render_emoji(html, regexp, edit, marseys_used=set(), b=False): if emoji.endswith('pat'): if path.isfile(f"files/assets/images/emojis/{emoji.replace('pat','')}.webp"): - attrs += ' pat' - emoji_html = f'{emoji_partial_pat.format(old, f"/e/{emoji[:-3]}.webp", attrs)}' + emoji_html = f'{emoji_partial_pat.format(old, f"/e/{emoji[:-3]}.webp", attrs)}' elif emoji.startswith('@'): if u := get_user(emoji[1:-3], graceful=True): - attrs += ' pat' - emoji_html = f'{emoji_partial_pat.format(old, f"/pp/{u.id}", attrs)}' + emoji_html = f'{emoji_partial_pat.format(old, f"/pp/{u.id}", attrs)}' elif path.isfile(f'files/assets/images/emojis/{emoji}.webp'): emoji_html = emoji_partial.format(old, f'/e/{emoji}.webp', attrs) if emoji_html: + marseys_used.add(emoji) html = re.sub(f'(?\1', title) - title = bleach.clean(title, tags=['img','del'], attributes=allowed_attributes_emojis, protocols=['http','https']) + title = bleach.clean(title, tags=['img','del','span'], attributes=allowed_attributes_emojis, protocols=['http','https']) signal.alarm(0) diff --git a/files/routes/settings.py b/files/routes/settings.py index 36f177e67..85c144c8e 100644 --- a/files/routes/settings.py +++ b/files/routes/settings.py @@ -788,8 +788,8 @@ def settings_name_change(v): return redirect("/settings/profile") @app.post("/settings/song_change") -@limiter.limit("2/second;10/day") -@limiter.limit("2/second;10/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("3/second;10/day") +@limiter.limit("3/second;10/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') @auth_required def settings_song_change(v): song=request.values.get("song").strip() diff --git a/files/routes/static.py b/files/routes/static.py index ca077dff1..32d9c51b2 100644 --- a/files/routes/static.py +++ b/files/routes/static.py @@ -244,8 +244,8 @@ def cached_chart(kind, site): ) today_cutoff = calendar.timegm(midnight_this_morning) - if kind == "daily": day_cutoffs = [today_cutoff - 86400 * i for i in range(47)][1:] - else: day_cutoffs = [today_cutoff - 86400 * 7 * i for i in range(47)][1:] + if kind == "daily": day_cutoffs = [today_cutoff - 86400 * i for i in range(55)][1:] + else: day_cutoffs = [today_cutoff - 86400 * 7 * i for i in range(55)][1:] day_cutoffs.insert(0, calendar.timegm(now)) diff --git a/files/templates/authforms.html b/files/templates/authforms.html index 2d6cd3790..79d60118c 100644 --- a/files/templates/authforms.html +++ b/files/templates/authforms.html @@ -15,7 +15,7 @@ {% if v %} - + {% if v.agendaposter %} - + {% endif %} diff --git a/files/templates/chat.html b/files/templates/chat.html index a35fbb4b7..cf697f29a 100644 --- a/files/templates/chat.html +++ b/files/templates/chat.html @@ -14,7 +14,7 @@ Chat - + {% if v.css %} diff --git a/files/templates/comments.html b/files/templates/comments.html index 929f5cf41..c44f224e4 100644 --- a/files/templates/comments.html +++ b/files/templates/comments.html @@ -845,8 +845,8 @@ {% endif %} {% if v %} - - + + {% endif %} @@ -858,7 +858,7 @@ {% include "expanded_image_modal.html" %} - + + {% if v %} - + {% if v.agendaposter %} - + {% endif %} diff --git a/files/templates/formatting.html b/files/templates/formatting.html index 1ca22135b..c54331aa9 100644 --- a/files/templates/formatting.html +++ b/files/templates/formatting.html @@ -104,12 +104,12 @@ Text 2 Pat Emojis :marseylovepat: - :marseylovepat: + :marseylovepat: Pat User :@snappypat: - :@snappypat: + :@snappypat: Random Marsey diff --git a/files/templates/log.html b/files/templates/log.html index beafaef8c..ff2ff79bf 100644 --- a/files/templates/log.html +++ b/files/templates/log.html @@ -6,7 +6,7 @@ {% block content %} {% if v %} - + {% if v.agendaposter %} - + {% endif %} diff --git a/files/templates/login.html b/files/templates/login.html index 6747c4392..6fb7cd336 100644 --- a/files/templates/login.html +++ b/files/templates/login.html @@ -18,7 +18,7 @@ {% endblock %} - + diff --git a/files/templates/login_2fa.html b/files/templates/login_2fa.html index 7fce5d5e9..7311290a4 100644 --- a/files/templates/login_2fa.html +++ b/files/templates/login_2fa.html @@ -14,7 +14,7 @@ 2-Step Login - {{SITE_NAME}} - + diff --git a/files/templates/settings.html b/files/templates/settings.html index 1b2564b19..de414b787 100644 --- a/files/templates/settings.html +++ b/files/templates/settings.html @@ -34,7 +34,7 @@ - + {% if v.agendaposter %} - + {% else %} - + {% endif %} diff --git a/files/templates/sign_up.html b/files/templates/sign_up.html index 31498bf84..a22e43fa6 100644 --- a/files/templates/sign_up.html +++ b/files/templates/sign_up.html @@ -31,7 +31,7 @@ {% if ref_user %}{{ref_user.username}} invites you to {{SITE_NAME}}{% else %}Sign up - {{SITE_NAME}}{% endif %} - + diff --git a/files/templates/sign_up_failed_ref.html b/files/templates/sign_up_failed_ref.html index 7bc51d1cf..2e545e8e6 100644 --- a/files/templates/sign_up_failed_ref.html +++ b/files/templates/sign_up_failed_ref.html @@ -32,7 +32,7 @@ {% if ref_user %}{{ref_user.username}} invites you to {{SITE_NAME}}{% else %}{{SITE_NAME}}{% endif %} - + diff --git a/files/templates/submit.html b/files/templates/submit.html index c68aa27e5..94ec7b9a5 100644 --- a/files/templates/submit.html +++ b/files/templates/submit.html @@ -26,7 +26,7 @@ {% block stylesheets %} {% if v %} - + {% if v.agendaposter %} - + {% endif %} {% endblock %} @@ -224,7 +224,7 @@ {% endif %} - + {% include "emoji_modal.html" %} diff --git a/files/templates/userpage.html b/files/templates/userpage.html index 3d3c8083b..88a505d1f 100644 --- a/files/templates/userpage.html +++ b/files/templates/userpage.html @@ -769,7 +769,7 @@ {% endif %} - + {% if v and v.id != u.id and '/comments' not in request.path %} diff --git a/files/tests/test_e2e.py b/files/tests/test_e2e.py new file mode 100644 index 000000000..865184d00 --- /dev/null +++ b/files/tests/test_e2e.py @@ -0,0 +1,37 @@ +from bs4 import BeautifulSoup +from time import time, sleep +from files.__main__ import app + +# these tests require `docker-compose up` first + +def test_rules(): + response = app.test_client().get("/logged_out/rules") + assert response.status_code == 200 + assert response.text.startswith("") + + +def test_signup(): + client = app.test_client() + with client: # this keeps the session between requests, which we need + signup_get_response = client.get("/signup") + assert signup_get_response.status_code == 200 + soup = BeautifulSoup(signup_get_response.text, 'html.parser') + # these hidden input values seem to be used for anti-bot purposes and need to be submitted + formkey = next(tag for tag in soup.find_all("input") if tag.get("name") == "formkey").get("value") + form_timestamp = next(tag for tag in soup.find_all("input") if tag.get("name") == "now").get("value") + + sleep(5) # too-fast submissions are rejected (bot check?) + username = "testuser" + str(round(time())) + signup_post_response = client.post("/signup", data={ + "username": username, + "password": "password", + "password_confirm": "password", + "email": "", + "formkey": formkey, + "now": form_timestamp + }) + print(f"Signing up as {username}") + assert signup_post_response.status_code == 302 + assert "error" not in signup_post_response.location + + # we should now be logged in and able to post \ No newline at end of file diff --git a/push.sh b/push.sh deleted file mode 100644 index edce13413..000000000 --- a/push.sh +++ /dev/null @@ -1,4 +0,0 @@ -git pull -git add . -git commit -m "sneed" -git push \ No newline at end of file diff --git a/pushforce.sh b/pushforce.sh deleted file mode 100644 index 26412e5da..000000000 --- a/pushforce.sh +++ /dev/null @@ -1,3 +0,0 @@ -git add . -git commit -m "force push" -git push --force \ No newline at end of file diff --git a/readme.md b/readme.md index 1317d2f04..cb0e18f53 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,6 @@ +[![Build status](https://img.shields.io/github/workflow/status/TheMotte/rDrama/run_tests.py/frost)](https://github.com/Aevann1/rDrama/actions?query=workflow%3Arun_tests.py+branch%3Afrost) + + This code runs https://rdrama.net and https://pcmemes.net # Installation (Windows/Linux/MacOS) diff --git a/requirements.txt b/requirements.txt index 9d27fd158..2c8c517f6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,6 +24,7 @@ tldextract psycopg2-binary pusher_push_notifications pyenchant +pytest youtube-dl yattag webptools \ No newline at end of file diff --git a/run_tests.py b/run_tests.py new file mode 100755 index 000000000..80e6114a3 --- /dev/null +++ b/run_tests.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 + +import subprocess +import sys + +# we want to leave the container in whatever state it currently is, so check to see if it's running +docker_inspect = subprocess.run([ + "docker", + "container", + "inspect", + "-f", "{{.State.Status}}", + "rDrama", + ], + capture_output = True, + ).stdout.decode("utf-8").strip() + +was_running = docker_inspect == "running" + +# update containers, just in case they're out of date +if was_running: + print("Updating containers . . .") +else: + print("Starting containers . . .") +subprocess.run([ + "docker-compose", + "up", + "--build", + "-d", + ], + check = True, + ) + +# run the test +print("Running test . . .") +result = subprocess.run([ + "docker", + "exec", + "rDrama", + "bash", "-c", "cd service && python3 -m pytest -s" + ]) + +if not was_running: + # shut down, if we weren't running in the first place + print("Shutting down containers . . .") + subprocess.run([ + "docker-compose", + "stop", + ], + check = True, + ) + +sys.exit(result.returncode) \ No newline at end of file