From 5abdfa09e0abfa4b97223c92aea20db6a77bcb97 Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Thu, 20 Sep 2018 16:17:42 +0000 Subject: [PATCH] Golang port is now MOTHv3 --- docs/api.md | 215 ------------------------------------------- tools/fake-server.py | 135 --------------------------- 2 files changed, 350 deletions(-) delete mode 100644 docs/api.md delete mode 100755 tools/fake-server.py diff --git a/docs/api.md b/docs/api.md deleted file mode 100644 index 610a814..0000000 --- a/docs/api.md +++ /dev/null @@ -1,215 +0,0 @@ -MOTHv3 API -========== - -MOTH, by design, uses a small number of API endpoints. - -Whenever possible, -we decided to push complexity into the client, -keeping the server as simple as we could make it. -After all, -this is a hacking contest. -If a participant finds a vulnerability in code running on their own machine, -the people running the server don't care. - -Specification -============= - -You make requests as HTTP GET query arguments: - - https://server/path/elements/api/v3/endpoint?var1=val1&var2=val2 - -The server returns a -[JSend](https://labs.omniti.com/labs/jsend) response: - - { - "status": "success", - "data": "Any JS data type here" - } - - -Client State -============ - -The client (or user interacting with the client) needs to remember only one thing: - -* teamId: the team ID used to register - -A naive client, -like the one we used from 2009-2018, -can ask the user to type in the team ID for every submission. -This is fine. - - -Endpoints -========= - -RegisterTeam(teamId, teamName) -------------------------------- - -Register a team name with a team hash. - -### Parameters - -* teamId: Team's unique identifier (usually a hex value) -* teamName: Team's human-readable name - -On success, no data is returned. -On failure, message contains an English explanation of why. - - -### Example - - https://server/api/v3/RegisterTeam?teamId=8b1292ca&teamName=Lexical+Pedants - - { - "status": "success", - "data": null - } - - -GetState() ----------- - -Return all current state of the puzzle server. - -### Parameters - -None - - -### Return data - -* puzzles: dictionary mapping from category to one of the following: - * list of point values currently open - * URL to puzzle root (intended for token-based puzzles) -* teams: mapping from anonymized team ID to team name -* log: list of (timestamp, team number, category, points) -* notices: list of HTML broadcast notices to display to the user -* now: current server time (unix epoch) - - -### Example - - https://server/api/v3/GetState - - { - "status": "success", - "data": { - "puzzles": { - "sequence": [1, 2], - "codebreaking": [10], - "wopr": "https://appspot.com/dooted-bagel-8372/entry" - }, - "teams": { - "0": "Zelda", - "1": "Defender" - }, - "log": [ - [1526478368, "0", "sequence", 1], - [1526478524, "1", "sequence", 1], - [1526478536, "0", "nocode", 1] - ], - "notices": [ - "WOPR category is now open", - "Event closes at 18:00 today, and will resume tomorrow at 08:00" - ], - "now": 1527170088 - } - } - - -GetPuzzle(category, points) --------------------- - -Return a puzzle. - -### Parameters - -* category: name of category to fetch from -* points: point value of the puzzle to fetch - - -### Return data - -* authors: List of puzzle authors -* hashes: list of djbhash values of acceptable answers -* files: dictionary of puzzle-associated filenames and their URLs -* body: HTML body of the puzzle - - -### Example - - https://server/api/v3/GetPuzzle?category=sequence&points=1 - - { - "status": "success", - "data": { - "authors": ["neale"], - "hashes": [177627], - "files": { - "happy.png": "https://cdn/assets/0904cf3a437a348bea2c49d56a3087c26a01a63c.png" - }, - "body": "
1 2 3 4 5 _\n
\n" - } - - - -SubmitAnswer(teamId, category, points, answer) ----------------------- - -Submit an answer to a puzzle. - -### Parameters - -* teamId: Team ID (optional: if ommitted, answer is verified but no points are awarded) -* category: category name of puzzle -* points: point value of puzzle -* answer: attempted answer - - -### Return Data - -* epilog: HTML to display upon successfully answering the puzzle - - -### Example - - https://server/api/v3/SubmitAnswer?teamId=8b1292ca&category=sequence&points=1&answer=6 - - { - "status": "success", - "data": { - "epilog": "That's right: in base 10, 5 + 1 = 6." - } - } - -SubmitToken(teamId, token) ---------------------- - -Submit a token for points - -### Parameters - -* teamId: Team ID -* token: Token being submitted - - -### Return data - -* category: category for which this token awarded points -* points: number of points awarded -* epilog: HTML to display upon successfully answering the puzzle - - -### Example - - https://server/api/v3/SubmitToken?teamId=8b1292ca&token=wat:30:xylep-radar-nanox - - { - "status": "success", - "data": { - "category": "wat", - "points": 30, - "epilog": "" - } - } diff --git a/tools/fake-server.py b/tools/fake-server.py deleted file mode 100755 index 340b43b..0000000 --- a/tools/fake-server.py +++ /dev/null @@ -1,135 +0,0 @@ -#! /usr/bin/python3 - -from aiohttp import web -import time - -async def fake_register(request): - teamId = request.query.get("teamId") - teamName = request.query.get("teamName") - if teamId == "ffff" and teamName == "dirtbags": - resp = { - "status": "success", - "data": None, - } - elif teamId and teamName: - resp = { - "status": "error", - "message": "Query was correctly formed but I'm feeling cranky" - } - else: - resp = { - "status": "fail", - "data": "You must send teamId and teamName", - } - return web.json_response(resp) - -async def fake_state(request): - resp = { - "status": "success", - "data": { - "puzzles": { - "sequence": [1, 2], - "codebreaking": [10], - "wopr": "https://appspot.com/dooted-bagel-8372/entry" - }, - "teams": { - "0": "Zelda", - "1": "Defender" - }, - "log": [ - [1526478368, "0", "sequence", 1], - [1526478524, "1", "sequence", 1], - [1526478536, "0", "nocode", 1] - ], - "notices": [ - "WOPR category is now open", - "Event closes at 18:00 today, and will resume tomorrow at 08:00" - ], - "now": int(time.time()), - } - } - return web.json_response(resp) - -async def fake_getpuzzle(request): - category = request.query.get("category") - points = request.query.get("points") - if category == "sequence" and points == "1": - resp = { - "status": "success", - "data": { - "authors": ["neale"], - "hashes": [177627], - "files": { - "happy.png": "https://cdn/assets/0904cf3a437a348bea2c49d56a3087c26a01a63c.png" - }, - "body": "
1 2 3 4 5 _\n
\n", - } - } - elif category and points: - resp = { - "status": "error", - "message": "Query was correctly formed but I'm feeling cranky" - } - else: - resp = { - "status": "fail", - "data": "You must send category and points" - } - return web.json_response(resp) - -async def fake_submitanswer(request): - teamId = request.query.get("teamId") - category = request.query.get("category") - points = request.query.get("points") - answer = request.query.get("answer") - if category == "sequence" and points == "1" and answer == "6": - resp = { - "status": "success", - "data": { - "epilog": "Now you know the answer, and knowing is half the battle. Go Joe!" - } - } - elif category and points and answer: - resp = { - "status": "error", - "message": "Query was correctly formed but I'm feeling cranky" - } - else: - resp = { - "status": "fail", - "data": "You must send category and points" - } - return web.json_response(resp) - -async def fake_submittoken(request): - teamId = request.query.get("teamId") - token = request.query.get("token") - if token == "wat:30:xylep-radar-nanox": - resp = { - "status": "success", - "data": { - "category": "wat", - "points": 30, - "epilog": "" - } - } - elif category and points and answer: - resp = { - "status": "error", - "message": "Query was correctly formed but I'm feeling cranky" - } - else: - resp = { - "status": "fail", - "data": "You must send category and points" - } - return web.json_response(resp) - -if __name__ == "__main__": - app = web.Application() - app.router.add_route("GET", "/api/v3/RegisterTeam", fake_register) - app.router.add_route("GET", "/api/v3/GetState", fake_state) - app.router.add_route("GET", "/api/v3/GetPuzzle", fake_getpuzzle) - app.router.add_route("GET", "/api/v3/SubmitAnswer", fake_submitanswer) - app.router.add_route("GET", "/api/v3/SubmitToken", fake_submittoken) - web.run_app(app)