Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c320012
Change emoji validation
Leonabcd123 Oct 16, 2025
73c0573
Add advanced_award_dojo.yml configuration file
Leonabcd123 Oct 16, 2025
3005276
Add advanced_award_dojo fixture for testing
Leonabcd123 Oct 16, 2025
bd34aea
Add advanced_award_dojo to event feed test
Leonabcd123 Oct 16, 2025
3572294
Modify completionist_user fixture for advanced dojo
Leonabcd123 Oct 16, 2025
1409f21
Update tests to support multiple award dojos
Leonabcd123 Oct 16, 2025
fad6c61
Add emoji dependency
Leonabcd123 Nov 7, 2025
1fa2cb9
Fix typo
Leonabcd123 Nov 7, 2025
19b3efd
Add feed tests for advanced_award_dojo
Leonabcd123 Nov 7, 2025
b369723
Remove duplicate modules
Leonabcd123 Nov 7, 2025
bfa2647
Change id
Leonabcd123 Nov 7, 2025
91d42f8
Try normal emoji
Leonabcd123 Nov 8, 2025
ece456b
Improve test
Leonabcd123 Nov 8, 2025
9dc9077
Downgrade version of emoji library
Leonabcd123 Nov 8, 2025
9934e22
Merge branch 'master' into change-emoji-validation
Leonabcd123 Nov 8, 2025
8642255
Change import
Leonabcd123 Nov 8, 2025
3168bb8
Change emoji back
Leonabcd123 Nov 8, 2025
39c4965
Merge branch 'master' into change-emoji-validation
Leonabcd123 Dec 4, 2025
a11f3c2
Change badges length in test
Leonabcd123 Dec 4, 2025
f8667e6
Change badges length in tests
Leonabcd123 Dec 4, 2025
0a70a7b
Enumerate over award_dojos
Leonabcd123 Dec 4, 2025
df6b145
Fix test
Leonabcd123 Dec 5, 2025
82077f4
Fix
Leonabcd123 Dec 5, 2025
371ba3d
Update deploy.sh
Leonabcd123 Dec 7, 2025
2037f73
Update deploy.sh
Leonabcd123 Dec 7, 2025
cefc614
Add echo for OPENAI_API_KEY
Leonabcd123 Dec 7, 2025
71cbf3f
Update deploy.sh
Leonabcd123 Dec 7, 2025
96d6f2a
Update deploy.sh
Leonabcd123 Dec 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ctfd/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pyyaml==6.0.2
schema==0.7.5
bleach==6.1.0
bleach[css]==6.1.0
emoji==2.10.0
ipython==8.16.1
flask-shell-ipython==0.5.1
sshpubkeys==3.3.1
Expand Down
3 changes: 2 additions & 1 deletion dojo_plugin/utils/dojo.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import urllib.request
import base64
import logging
import emoji

import yaml
import requests
Expand Down Expand Up @@ -59,7 +60,7 @@

Optional("type"): ID_REGEX,
Optional("award"): {
Optional("emoji"): Regex(r"^\S$"),
Optional("emoji"): emoji.is_emoji,
Optional("belt"): IMAGE_REGEX
},

Expand Down
19 changes: 13 additions & 6 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,19 @@ def random_user_session(random_user):


@pytest.fixture
def completionist_user(simple_award_dojo):
def completionist_user(simple_award_dojo, advanced_award_dojo):
random_id = "".join(random.choices(string.ascii_lowercase, k=16))
session = login(random_id, random_id, register=True)

response = session.get(f"{DOJO_URL}/dojo/{simple_award_dojo}/join/")
assert response.status_code == 200
for module, challenge in [ ("hello", "apple"), ("hello", "banana") ]:
start_challenge(simple_award_dojo, module, challenge, session=session)
solve_challenge(simple_award_dojo, module, challenge, session=session, user=random_id)
award_dojos = [simple_award_dojo, advanced_award_dojo]

for award_dojo in award_dojos:
response = session.get(f"{DOJO_URL}/dojo/{award_dojo}/join/")
assert response.status_code == 200
for module, challenge in [ ("hello", "apple"), ("hello", "banana") ]:
start_challenge(award_dojo, module, challenge, session=session)
solve_challenge(award_dojo, module, challenge, session=session, user=random_id)

yield random_id, session


Expand Down Expand Up @@ -88,6 +91,10 @@ def example_import_dojo(admin_session, example_dojo):
def simple_award_dojo(admin_session):
return create_dojo_yml(open(TEST_DOJOS_LOCATION / "simple_award_dojo.yml").read(), session=admin_session)

@pytest.fixture
def advanced_award_dojo(admin_session):
return create_dojo_yml(open(TEST_DOJOS_LOCATION / "advanced_award_dojo.yml").read(), session=admin_session)

@pytest.fixture(scope="session")
def no_practice_challenge_dojo(admin_session, example_dojo):
return create_dojo_yml(open(TEST_DOJOS_LOCATION / "no_practice_challenge.yml").read(), session=admin_session)
Expand Down
20 changes: 20 additions & 0 deletions test/dojos/advanced_award_dojo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
id: advanced-award
type: public
award:
emoji: 🐻‍❄️
modules:
- id: hello
challenges:
- id: apple
- id: banana
files:
- type: text
path: hello/apple/src
content: |
#!/opt/pwn.college/bash
cat /flag
- type: text
path: hello/banana/src
content: |
#!/opt/pwn.college/bash
cat /flag
68 changes: 37 additions & 31 deletions test/test_dojos.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,15 @@ def test_promote_dojo_member(admin_session, guest_dojo_admin, example_dojo):
assert random_user_name in response.text and response.text.index("Members") > response.text.index(random_user_name)


def test_dojo_completion_emoji(simple_award_dojo, completionist_user):
def test_dojo_completion_emoji(simple_award_dojo, advanced_award_dojo, completionist_user):
user_name, session = completionist_user

scoreboard = session.get(f"{DOJO_URL}/pwncollege_api/v1/scoreboard/{simple_award_dojo}/_/0/1").json()
us = next(u for u in scoreboard["standings"] if u["name"] == user_name)
assert us["solves"] == 2
assert len(us["badges"]) == 1

award_dojos = [simple_award_dojo, advanced_award_dojo]
for award_dojo in award_dojos:
scoreboard = session.get(f"{DOJO_URL}/pwncollege_api/v1/scoreboard/{award_dojo}/_/0/1").json()
us = next(u for u in scoreboard["standings"] if u["name"] == user_name)
assert us["solves"] == 2
assert len(us["badges"]) == 2

def test_no_practice(no_practice_challenge_dojo, no_practice_dojo, random_user_session):
for dojo in [ no_practice_challenge_dojo, no_practice_dojo ]:
Expand Down Expand Up @@ -98,36 +99,41 @@ def test_no_import(no_import_challenge_dojo, admin_session):
raise AssertionError("forbidden-import dojo creation should have failed, but it succeeded")


def test_prune_dojo_emoji(simple_award_dojo, admin_session, completionist_user):
def test_prune_dojo_emoji(simple_award_dojo, advanced_award_dojo, admin_session, completionist_user):
user_name, _ = completionist_user
db_sql(f"DELETE FROM submissions WHERE id IN (SELECT id FROM submissions WHERE user_id={get_user_id(user_name)} ORDER BY id DESC LIMIT 1)")

response = admin_session.post(f"{DOJO_URL}/pwncollege_api/v1/dojos/{simple_award_dojo}/awards/prune", json={})
assert response.status_code == 200

scoreboard = admin_session.get(f"{DOJO_URL}/pwncollege_api/v1/scoreboard/{simple_award_dojo}/_/0/1").json()
us = next(u for u in scoreboard["standings"] if u["name"] == user_name)
assert us["solves"] == 1
assert len(us["badges"]) == 1
assert us["badges"][0]["stale"] == True

award_dojos = [simple_award_dojo, advanced_award_dojo]
for award_dojo in award_dojos:
db_sql(f"DELETE FROM submissions WHERE id IN (SELECT id FROM submissions WHERE user_id={get_user_id(user_name)} ORDER BY id DESC LIMIT 1)")
response = admin_session.post(f"{DOJO_URL}/pwncollege_api/v1/dojos/{award_dojo}/awards/prune", json={})
assert response.status_code == 200

scoreboard = admin_session.get(f"{DOJO_URL}/pwncollege_api/v1/scoreboard/{award_dojo}/_/0/1").json()
us = next(u for u in scoreboard["standings"] if u["name"] == user_name)

assert us["solves"] == 2
assert len(us["badges"]) == 2
assert us["badges"][0]["stale"] == True


def test_dojo_removes_emoji(simple_award_dojo, admin_session, completionist_user):
def test_dojo_removes_emoji(simple_award_dojo, advanced_award_dojo, admin_session, completionist_user):
user_name, _ = completionist_user

scoreboard = admin_session.get(f"{DOJO_URL}/pwncollege_api/v1/scoreboard/{simple_award_dojo}/_/0/1").json()
us = next(u for u in scoreboard["standings"] if u["name"] == user_name)
assert us["solves"] == 2
assert len(us["badges"]) == 1
assert us["badges"][0]["stale"] == False

dojo_id = simple_award_dojo.split("~")[1]
db_sql(f"UPDATE dojos SET data = data - 'award' || jsonb_build_object('award', jsonb_build_object('belt', 'orange')) WHERE dojo_id = x'{dojo_id}'::int")

scoreboard = admin_session.get(f"{DOJO_URL}/pwncollege_api/v1/scoreboard/{simple_award_dojo}/_/0/1").json()
us = next(u for u in scoreboard["standings"] if u["name"] == user_name)
assert us["solves"] == 2
assert len(us["badges"]) == 0
award_dojos = [simple_award_dojo, advanced_award_dojo]
for award_dojo in award_dojos:
scoreboard = admin_session.get(f"{DOJO_URL}/pwncollege_api/v1/scoreboard/{award_dojo}/_/0/1").json()
us = next(u for u in scoreboard["standings"] if u["name"] == user_name)
assert us["solves"] == 2
assert len(us["badges"]) == 2
assert us["badges"][0]["stale"] == False

dojo_id = award_dojo.split("~")[1]
db_sql(f"UPDATE dojos SET data = data - 'award' || jsonb_build_object('award', jsonb_build_object('belt', 'orange')) WHERE dojo_id = x'{dojo_id}'::int")

scoreboard = admin_session.get(f"{DOJO_URL}/pwncollege_api/v1/scoreboard/{award_dojo}/_/0/1").json()
us = next(u for u in scoreboard["standings"] if u["name"] == user_name)
assert us["solves"] == 2
assert len(us["badges"]) == 0


def test_lfs(lfs_dojo, random_user_name, random_user_session):
Expand Down
8 changes: 7 additions & 1 deletion test/test_feed.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def challenge_start(browser, idx):
time.sleep(1)


def test_feed_shows_all_events(welcome_dojo, simple_award_dojo, random_user_name, random_user_session):
def test_feed_shows_all_events(welcome_dojo, simple_award_dojo, advanced_award_dojo, random_user_name, random_user_session):
watcher_options = FirefoxOptions()
watcher_options.add_argument("--headless")
watcher = Firefox(options=watcher_options)
Expand Down Expand Up @@ -92,6 +92,12 @@ def test_feed_shows_all_events(welcome_dojo, simple_award_dojo, random_user_name
solve_challenge(simple_award_dojo, "hello", "apple", session=random_user_session, user=random_user_name)
start_challenge(simple_award_dojo, "hello", "banana", session=random_user_session)
solve_challenge(simple_award_dojo, "hello", "banana", session=random_user_session, user=random_user_name)

random_user_session.get(f"{DOJO_URL}/dojo/{advanced_award_dojo}/join/")
start_challenge(advanced_award_dojo, "hello", "apple", session=random_user_session)
solve_challenge(advanced_award_dojo, "hello", "apple", session=random_user_session, user=random_user_name)
start_challenge(advanced_award_dojo, "hello", "banana", session=random_user_session)
solve_challenge(advanced_award_dojo, "hello", "banana", session=random_user_session, user=random_user_name)
time.sleep(1)
events_with_emoji = watcher.find_element(By.ID, "events-list").find_elements(By.CLASS_NAME, "event-card")

Expand Down
Loading