Skip to content

Commit

Permalink
Set challenge attribution (#164)
Browse files Browse the repository at this point in the history
* Handle challenge attribution and its related tests

* Update upload-artifact to v3

---------

Co-authored-by: Kevin Chung <[email protected]>
  • Loading branch information
fuyu0425 and ColdHeat authored Nov 26, 2024
1 parent d8e08da commit 763c2de
Show file tree
Hide file tree
Showing 8 changed files with 43 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- name: Build package
run: poetry build

- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
path: |
./dist/*.tar.gz
Expand Down
12 changes: 7 additions & 5 deletions ctfcli/core/challenge.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def str_presenter(dumper, data):
class Challenge(dict):
key_order = [
# fmt: off
"name", "author", "category", "description", "value",
"name", "author", "category", "description", "attribution", "value",
"type", "extra", "image", "protocol", "host",
"connection_info", "healthcheck", "attempts", "flags",
"files", "topics", "tags", "files", "hints",
Expand Down Expand Up @@ -262,6 +262,7 @@ def _get_initial_challenge_payload(self, ignore: Tuple[str] = ()) -> Dict:
"name": self["name"],
"category": self.get("category", ""),
"description": self.get("description", ""),
"attribution": self.get("attribution", ""),
"type": self.get("type", "standard"),
# Hide the challenge for the duration of the sync / creation
"state": "hidden",
Expand Down Expand Up @@ -459,12 +460,13 @@ def normalize_requirements(requirements):
def _normalize_challenge(self, challenge_data: Dict[str, Any]):
challenge = {}

copy_keys = ["name", "category", "value", "type", "state", "connection_info"]
copy_keys = ["name", "category", "attribution", "value", "type", "state", "connection_info"]
for key in copy_keys:
if key in challenge_data:
challenge[key] = challenge_data[key]

challenge["description"] = challenge_data["description"].strip().replace("\r\n", "\n").replace("\t", "")
challenge["attribution"] = challenge_data.get("attribution", "").strip().replace("\r\n", "\n").replace("\t", "")
challenge["attempts"] = challenge_data["max_attempts"]

for key in ["initial", "decay", "minimum"]:
Expand Down Expand Up @@ -556,7 +558,7 @@ def sync(self, ignore: Tuple[str] = ()) -> None:
remote_challenge = self.load_installed_challenge(self.challenge_id)

# if value, category, type or description are ignored, revert them to the remote state in the initial payload
reset_properties_if_ignored = ["value", "category", "type", "description"]
reset_properties_if_ignored = ["value", "category", "type", "description", "attribution"]
for p in reset_properties_if_ignored:
if p in ignore:
challenge_payload[p] = remote_challenge[p]
Expand Down Expand Up @@ -670,7 +672,7 @@ def create(self, ignore: Tuple[str] = ()) -> None:
# value is required (unless the challenge is a dynamic value challenge),
# and the type will default to standard
# if category or description are ignored, set them to an empty string
reset_properties_if_ignored = ["category", "description"]
reset_properties_if_ignored = ["category", "description", "attribution"]
for p in reset_properties_if_ignored:
if p in ignore:
challenge_payload[p] = ""
Expand Down Expand Up @@ -716,7 +718,7 @@ def lint(self, skip_hadolint=False, flag_format="flag{") -> bool:
issues = {"fields": [], "dockerfile": [], "hadolint": [], "files": []}

# Check if required fields are present
for field in ["name", "author", "category", "description", "value"]:
for field in ["name", "author", "category", "description", "attribution", "value"]:
# value is allowed to be none if the challenge type is dynamic
if field == "value" and challenge.get("type") == "dynamic":
continue
Expand Down
30 changes: 26 additions & 4 deletions tests/core/test_challenge.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ def test_updates_simple_properties(self, mock_api_constructor: MagicMock, *args,
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"state": "hidden",
Expand Down Expand Up @@ -239,6 +240,7 @@ def test_updates_attempts(self, mock_api_constructor: MagicMock, *args, **kwargs
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"state": "hidden",
Expand Down Expand Up @@ -284,6 +286,7 @@ def test_updates_extra_properties(self, mock_api_constructor: MagicMock, *args,
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"value": 150,
"state": "hidden",
"type": "application_target",
Expand Down Expand Up @@ -342,6 +345,7 @@ def test_updates_flags(self, mock_api_constructor: MagicMock, *args, **kwargs):
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"state": "hidden",
Expand Down Expand Up @@ -430,6 +434,7 @@ def test_updates_topics(self, mock_api_constructor: MagicMock, *args, **kwargs):
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"state": "hidden",
Expand Down Expand Up @@ -490,6 +495,7 @@ def test_updates_tags(self, mock_api_constructor: MagicMock, *args, **kwargs):
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"state": "hidden",
Expand Down Expand Up @@ -553,6 +559,7 @@ def test_updates_files(self, mock_api_constructor: MagicMock, *args, **kwargs):
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"state": "hidden",
Expand Down Expand Up @@ -654,6 +661,7 @@ def test_updates_hints(self, mock_api_constructor: MagicMock, *args, **kwargs):
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"state": "hidden",
Expand Down Expand Up @@ -714,6 +722,7 @@ def test_updates_requirements(self, mock_api_constructor: MagicMock, *args, **kw
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"state": "hidden",
Expand Down Expand Up @@ -756,6 +765,7 @@ def test_challenge_cannot_require_itself(
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"state": "hidden",
Expand Down Expand Up @@ -816,6 +826,7 @@ def test_defaults_to_standard_challenge_type(self, mock_api_constructor: MagicMo
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"state": "hidden",
Expand Down Expand Up @@ -854,6 +865,7 @@ def test_defaults_to_visible_state(self, mock_api_constructor: MagicMock, *args,
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"max_attempts": 0,
Expand Down Expand Up @@ -904,6 +916,7 @@ def test_does_not_update_dynamic_value(self, mock_api_constructor: MagicMock, *a
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "dynamic",
"state": "hidden",
"max_attempts": 0,
Expand Down Expand Up @@ -961,6 +974,7 @@ def test_updates_multiple_attributes_at_once(self, mock_api_constructor: MagicMo
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"state": "hidden",
Expand Down Expand Up @@ -1018,7 +1032,7 @@ def test_does_not_update_ignored_attributes(self):
properties = [
# fmt: off
# simple types
"category", "description", "type", "value", "attempts", "connection_info", "state",
"category", "description", "attribution", "type", "value", "attempts", "connection_info", "state",
# complex types
"extra", "flags", "topics", "tags", "files", "hints", "requirements",
# fmt: on
Expand All @@ -1028,6 +1042,7 @@ def test_does_not_update_ignored_attributes(self):
"name": "Test Challenge",
"category": "Old Category",
"description": "Old Description",
"attribution": "Old Attribution",
"type": "some-custom-type",
"value": 100,
"state": "visible",
Expand Down Expand Up @@ -1057,6 +1072,7 @@ def test_does_not_update_ignored_attributes(self):
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"state": "hidden",
Expand All @@ -1072,7 +1088,7 @@ def test_does_not_update_ignored_attributes(self):
expected_challenge_payload["value"] = remote_installed_challenge["value"]
challenge["value"] = 200

if p in ["category", "description", "type"]:
if p in ["category", "description", "attribution", "type"]:
expected_challenge_payload[p] = remote_installed_challenge[p]
challenge[p] = "new-value"

Expand Down Expand Up @@ -1154,6 +1170,7 @@ def test_creates_standard_challenge(self, mock_api_constructor: MagicMock, *args
"name": "Test Challenge",
"category": "Test",
"description": "Test Description",
"attribution": "Test Attribution",
"value": 150,
"max_attempts": 5,
"type": "standard",
Expand Down Expand Up @@ -1244,7 +1261,7 @@ def test_exits_if_files_do_not_exist(self, mock_api_constructor: MagicMock, *arg
def test_does_not_set_ignored_attributes(self):
# fmt:off
properties = [
"value", "category", "description", "attempts", "connection_info", "state", # simple types
"value", "category", "description", "attribution", "attempts", "connection_info", "state", # simple types
"extra", "flags", "topics", "tags", "files", "hints", "requirements" # complex types
]
# fmt:on
Expand All @@ -1262,6 +1279,7 @@ def test_does_not_set_ignored_attributes(self):
"name": "Test Challenge",
"category": "New Test",
"description": "New Test Description",
"attribution": "New Test Attribution",
"type": "standard",
"value": 150,
"state": "hidden",
Expand All @@ -1282,7 +1300,7 @@ def test_does_not_set_ignored_attributes(self):
expected_challenge_payload[p] = "custom-type"

# expect these to be in the payload, with the defaults or empty:
if p in ["category", "description"]:
if p in ["category", "description", "attribution"]:
challenge[p] = "new-value"
expected_challenge_payload[p] = ""

Expand Down Expand Up @@ -1520,6 +1538,7 @@ def mock_get(self, *args, **kwargs):
"name": "Test Challenge",
"value": 150,
"description": "Test Description",
"attribution": "Test Attribution",
"connection_info": "https://example.com",
"next_id": None,
"category": "Test",
Expand Down Expand Up @@ -1681,6 +1700,7 @@ def test_normalize_fetches_and_normalizes_challenge(self, mock_api_constructor:
"name": "Test Challenge",
"category": "Test",
"description": "Test Description",
"attribution": "Test Attribution",
"value": 150,
"max_attempts": 5,
"type": "standard",
Expand All @@ -1703,6 +1723,7 @@ def test_normalize_fetches_and_normalizes_challenge(self, mock_api_constructor:
"state": "hidden",
"connection_info": "https://example.com",
"description": "Test Description",
"attribution": "Test Attribution",
"attempts": 5,
"flags": [
"flag{test-flag}",
Expand Down Expand Up @@ -1755,6 +1776,7 @@ def test_mirror_challenge(self, mock_api_constructor: MagicMock):
{
"value": 200,
"description": "other description",
"attribution": "other attribution",
"connection_info": "https://other.example.com",
"flags": ["flag{other-flag}", "other-flag"],
"topics": ["other-topic-1", "other-topic-2"],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
name: Test Challenge
category: New Test
description: New Test Description
attribution: New Test Attribution
value: 150
author: Test
type: standard
state: hidden
image: .
protocol: http
protocol: http
3 changes: 2 additions & 1 deletion tests/fixtures/challenges/test-challenge-files/challenge.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
name: Test Challenge
category: New Test
description: New Test Description
attribution: New Test Attribution
value: 150
author: Test
type: standard
state: hidden

files:
- files/test.png
- files/test.pdf
- files/test.pdf
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Test Challenge
category: Test
description: Test Description
attribution: Test Attribution
value: 150
author: Test
type: standard
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
name: Test Challenge
category: New Test
description: New Test Description
attribution: New Test Attribution
value: 150
author: Test
type: standard
state: hidden
image: .
image: .
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
name: Test Challenge
category: New Test
description: New Test Description
attribution: New Test Attribution
value: 150
author: Test
type: standard
state: hidden
state: hidden

0 comments on commit 763c2de

Please sign in to comment.