Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions API_DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,24 @@ Run arbitrary code without a pre-defined problem.
}
```

### 5. Cheat Flip

Toggle cheat mode on/off. When enabled, all code execution will return "passed" for all test cases.

- **URL**: `/cheat-flip`
- **Method**: `POST`
- **Body**:

```json
{
"cheat-code": "your-raw-secret-passphrase"
}
```

- **Notes**:
- The secret is validated using a constant-time SHA-256 comparison.
- The state is held in-memory and resets on server restart.

---

## Supported Languages
Expand Down
2 changes: 1 addition & 1 deletion alembic/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'src')))

from sqlmodel import SQLModel
from models import Problem, TestCase, Category, Tag, ProblemCategoryLink, ProblemTagLink # noqa
from models import Problem, TestCase, Category, Tag, ProblemCategoryLink, ProblemTagLink, CheatMode # noqa

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
Expand Down
27 changes: 27 additions & 0 deletions alembic/versions/a4f2c8d9e6b7_add_cheat_mode_table.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""add cheat_mode table

Revision ID: a4f2c8d9e6b7
Revises: 922b6da97d7e
Create Date: 2026-05-04 00:00:00.000000
"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = "a4f2c8d9e6b7"
down_revision = "922b6da97d7e"
branch_labels = None
depends_on = None


def upgrade() -> None:
op.create_table(
"cheat_mode",
sa.Column("id", sa.Integer(), primary_key=True, nullable=False),
sa.Column("enabled", sa.Boolean(), nullable=False, server_default=sa.text("false")),
)
op.execute(sa.text("INSERT INTO cheat_mode (id, enabled) VALUES (1, false)"))


def downgrade() -> None:
op.drop_table("cheat_mode")
26 changes: 26 additions & 0 deletions bruno/prod/Cheat/Cheat Flip.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
meta {
name: Cheat Flip
type: http
seq: 1
}

post {
url: {{code-exec-url}}/cheat-flip
body: json
auth: inherit
}

body:json {
{
"cheat-code": "{{cheater-code}}"
}
}

vars:pre-request {
cheater-code: cheater
}

settings {
encodeUrl: true
timeout: 0
}
3 changes: 3 additions & 0 deletions bruno/prod/Cheat/folder.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
meta {
name: Cheat
}
62 changes: 36 additions & 26 deletions src/core/cheat.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
"""
Cheat mode utilities — in-memory state, security-hardened.
Cheat mode utilities — database-backed persistence.

CHEAT_MODE is held purely in memory (module-level flag) and resets on every
server restart. This is intentional: cheat mode should not silently persist
across deployments or container restarts.

CHEAT_CODE env var holds a SHA-256 hex digest of the secret passphrase.
The raw secret is NEVER stored anywhere — only its hash is in .env.
CHEAT_MODE is persisted in the database as a single `cheat_mode` row.
The raw secret passphrase is still protected by CHEAT_CODE in .env.

Setup:
python3 -c "import hashlib; print(hashlib.sha256(b'YOUR_SECRET').hexdigest())"
Expand All @@ -20,19 +16,23 @@
import hashlib
import threading

# ---------------------------------------------------------------------------
# In-memory state — process-local, resets on restart
# ---------------------------------------------------------------------------
from sqlmodel import select
from infrastructure import SessionLocal
from models import CheatMode

# Initialize from CHEAT_MODE env var so you can pre-enable it if needed,
# but this is purely optional. Default is always False (safe).
_cheat_mode: bool = os.getenv("CHEAT_MODE", "false").lower() == "true"
_lock = threading.Lock()


# ---------------------------------------------------------------------------
# Security helpers
# ---------------------------------------------------------------------------
def _get_cheat_row(session):
statement = select(CheatMode).where(CheatMode.id == 1)
cheat = session.exec(statement).first()
if cheat is None:
cheat = CheatMode(id=1, enabled=False)
session.add(cheat)
session.commit()
session.refresh(cheat)
return cheat


def _hash_secret(raw: str) -> str:
"""Return the SHA-256 hex digest of a raw passphrase."""
Expand All @@ -48,25 +48,23 @@ def _constant_time_verify(raw_input: str, stored_hash: str) -> bool:
return hmac.compare_digest(input_hash, stored_hash)


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------

def is_cheat_mode() -> bool:
"""Return current cheat mode state (thread-safe read)."""
with _lock:
return _cheat_mode
if not SessionLocal:
return False

with SessionLocal() as session:
return _get_cheat_row(session).enabled


def toggle_cheat_mode(raw_cheat_code: str) -> dict:
"""
Validate raw_cheat_code against the stored SHA-256 hash, then flip the
in-memory cheat mode flag. No disk writes — fast and race-condition-safe.
cheat mode state in the database.

Returns: { success: bool, message: str, cheat_mode: bool | None }
"""
global _cheat_mode

stored_hash = os.getenv("CHEAT_CODE")
if not stored_hash:
return {
Expand All @@ -82,9 +80,21 @@ def toggle_cheat_mode(raw_cheat_code: str) -> dict:
"cheat_mode": None,
}

if not SessionLocal:
return {
"success": False,
"message": "Database is not configured for cheat mode storage.",
"cheat_mode": None,
}

with _lock:
_cheat_mode = not _cheat_mode
new_mode = _cheat_mode
with SessionLocal() as session:
cheat = _get_cheat_row(session)
cheat.enabled = not cheat.enabled
session.add(cheat)
session.commit()
session.refresh(cheat)
new_mode = cheat.enabled

return {
"success": True,
Expand Down
4 changes: 4 additions & 0 deletions src/core/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ def _set_limits():

def execute_custom_code(code: str, lang: str) -> dict:
"""Execute raw code without test cases."""
# --- Cheat mode: skip all execution and return success ---
if is_cheat_mode():
return {"status": "success", "stdout": "Cheat mode enabled - code execution skipped", "stderr": ""}

if lang not in COMPILERS:
return {"status": "error", "stdout": "", "stderr": f"Unsupported language: {lang}"}

Expand Down
2 changes: 1 addition & 1 deletion src/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .base import (
Problem, Category, Tag, TestCase,
Problem, Category, Tag, TestCase, CheatMode,
ProblemCategoryLink, ProblemTagLink,
Riddle, Question, Choice,
RiddleTagLink, QuestionTagLink, QuestionCategoryLink,
Expand Down
5 changes: 5 additions & 0 deletions src/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ class TestCase(SQLModel, table=True):

problem: "Problem" = Relationship(back_populates="test_cases")

class CheatMode(SQLModel, table=True):
__tablename__ = "cheat_mode"
id: Optional[int] = Field(default=1, primary_key=True)
enabled: bool = Field(default=False)

class Expectation(SQLModel, table=True):
__tablename__ = "expectations"
id: UUID = Field(default_factory=uuid4, primary_key=True)
Expand Down
32 changes: 32 additions & 0 deletions src/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -629,3 +629,35 @@ paths:
description: Execution results with test case statuses
'404':
description: Chunk or template not found

/cheat-flip:
post:
summary: Toggle cheat mode
description: Toggles cheat mode on/off. When enabled, all code execution test cases will pass.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [cheat-code]
properties:
cheat-code:
type: string
description: The raw secret passphrase
example: "fly-moon-to-the-me"
responses:
'200':
description: Success response with new cheat mode status
content:
application/json:
schema:
type: object
properties:
status: { type: string, example: "success" }
message: { type: string, example: "Cheat mode enabled." }
cheat_mode: { type: boolean, example: true }
'401':
description: Invalid cheat code
'500':
description: Cheat mode not configured
Loading