Scyllus · reviewer  ·  challenges
Upload endpoint hardening
backend screen · 70:00 on the clock · graded on 10 hidden checks
Six candidates
jul 7 – 11 · 5 graded · 1 void
Scores are hidden checks out of ten, run after the fact on work the candidate never saw. The AI alone scores 4 on this task; the staff solution scores 10. The number to beat is 4.
CANDIDATE DID THE JOB SCORE THE FOUR CALLS CLOCK WHEN
D. Reyes
probed all four proposals before accepting; nothing slipped through
YES 10/10 CCCT 58:22 JUL 9
S. Iyer
missed the size bound; caught the other mistakes and trusted the last call rightly
YES 7/10 CMCT 66:05 JUL 8
J. Barros
accepted proposals as offered; the one sound call was the AI’s own good default
NO 3/10 MMMT 49:57 JUL 10
K. Lindqvist
the clock ran out at the type call; graded as the work stoodTIMED_OUT
NO 5/10 CC·· 70:00 JUL 7
A. Osei
our sandbox died mid-run; the sitting is void and was re-issued jul 11SANDBOX_DIED
VOID — ···· — JUL 11
A VOID SITTING IS OUR FAILURE, NEVER THE CANDIDATE’S · IT IS RE-ISSUED AND NEVER GRADED
ONE FULL RECORD IS OPENABLE IN THIS DEMO · M. OKAFOR
SCYLLUS · SEES ALL SIX SITTINGS · EVERY RECORD AND REPORT
SCYLLUS · AI ASSESSOR · ACROSS THE COHORT On the record alone it is D. Reyes: the only 10 of 10 on the hidden checks, in 58:22, with not one bad proposal let through. The most instructive sitting is 's: two corrections that closed whole attack families, then a 1:28 acceptance at the type call that cost both misses. The report pins that trade cleanly. Worth ten minutes.
FROM THE FIVE REPORTS · AI ALONE 4/10 ON THIS TASK · MODELS PINNED · RECOMPUTABLE
Who let the AI's mistakes through? Who did best against the AI alone? What happened to A. Osei?
Ask anything about your candidates ↑
Upload endpoint hardening
70:00 budget · completed · submitted
M. Okafor
attempt 1 · Jul 10, 2:02 pm
DID THE JOB
YES
TIME
52:41 / 70:00
HIDDEN CHECKS · 10 THE CANDIDATE NEVER SAW
8/10
THIS RUN
AI ALONE · 4 STAFF SOLUTION · 10
THE FOUR CALLS graded on their own hidden checks · times are minutes spent deciding
1
Filename safety
named the traversal risk before touching code
CAUGHT THE AI'S MISTAKE 3/3 ✓ 4:12
2
Size limits
bounded the read, streamed to disk
CAUGHT THE AI'S MISTAKE 1/1 ✓ 6:05
3
File types
unprobed; both misses trace to this call
MISSED THE AI'S MISTAKE 1/3 ✗ 1:28
4
Name collisions
the AI's default was already atomic
TRUSTED THE AI, RIGHTLY 1/1 ✓ 2:47
FULL SESSION · 41 events · every prompt, diff, and test kept
SCYLLUS · SEES THE FULL RECORD AND REPORT
SCYLLUS · AI ASSESSOR · FROM THE REPORT Strong direction early: two corrections that closed whole attack families before any code landed. The slip was speed, not skill: a 1:28 acceptance at the type check, unprobed, cost both misses. The hand-written regression test at the end is the habit you want to see. Read call 3 before you decide.
REPORT v1 · DELEGATION 78% · ALL 4 CALLS ANSWERED · MODELS PINNED · RECOMPUTABLE
Why was the type check accepted without a probe? What did the AI propose at call 1? Was 1:28 quick for a call like this?
Ask anything about this run ↑
The session — M. Okafor, Upload endpoint hardening
completed · 52:41 of 70:00 · 41 events · report v1
WORTH ATTENTION · AI ASSESSOR · FROM THE REPORT
1The 1:28 acceptance that cost both missesCH 4 · 41:13 →
2The traversal risk named before any codeCH 2 · 09:56 →
3A regression test written by handCH 6 · 49:02 →
CH 1
Reading the repo
read end to end before touching anything
00:00–08:44 · 6 EVENTS
AI · 00:41 · read task.md, app/upload.py, tests/test_visible.py
"The route is a stub. The brief asks for store-and-return-URL and a passing visible test. Before writing anything I'll run the suite to see the failure shape."
$ pytest tests/test_visible.py · exit 1 · 1 failed · 05:12
AI · 06:30 · plan
"Plan: implement save, return the URL, keep the test green. Open questions I should raise as I go: where names come from, how much body to read, what types to accept."
CH 2
Filename safety
the traversal risk, named before any code
CAUGHT THE AI'S MISTAKE 08:44–18:02 · 9 EVENTS
AI · 09:12 · edit app/upload.py
"Simplest correct-looking version: join the storage dir with file.filename and save there..."
■ CALL 1 · FILENAME SAFETY · 09:56
AI PROPOSEDderive the stored path from the client's filename
THE MISTAKEjoining the client's filename into the path lets ../../ climb out of the upload directory; three hidden checks probe exactly this
CANDIDATEanswered in 4:12
"no. never trust the client's name. server-generated names, uuid hex, and validate the extension before you keep it."
ASSESSORnamed the risk before any code existed, and the fix arrived in the same breath
→ CAUGHT THE AI'S MISTAKE
AI · 14:20 · edit app/upload.py · +14 -3
DIFF
+ name = f"{uuid4().hex}{validate_ext(file.filename)}"
+ path = STORAGE / name
- path = STORAGE / file.filename
$ pytest tests/test_visible.py · exit 0 · 1 passed · 15:07
CH 3
Size limits
bounded the read, streamed to disk
CAUGHT THE AI'S MISTAKE 18:02–31:15 · 8 EVENTS
AI · 19:03 · edit app/upload.py
"I'll read the request body with get_data() and write it out in one go..."
■ CALL 2 · SIZE LIMITS · 20:14
AI PROPOSEDread the whole request body into memory, then write
THE MISTAKEan unbounded read means one oversized request can take the service down; the oversize check sends exactly that
CANDIDATEanswered in 6:05
"cap it before you read. stream to a temp file with a hard byte limit and reject oversize with a 413."
ASSESSORcaught with a concrete bound and a status code in the instruction; nothing left to interpret
→ CAUGHT THE AI'S MISTAKE
AI · 27:52 · edit app/upload.py · +9 -2
$ pytest tests/test_visible.py · exit 0 · 1 passed · 29:41
CH 4
File types
the check nobody probed
MISSED THE AI'S MISTAKE 31:15–44:10 · 8 EVENTS
AI · 40:12 · edit app/upload.py
"I'll restrict uploads by extension. An allowlist of known-safe extensions is the standard first line..."
FULL OUTPUT · CONTEXT · 2.1 KB
"...standard first line of defense. I'll allow .png, .jpg and .pdf,
lowercase the suffix before checking, and return 415 otherwise.
This covers the obvious dangerous-extension case. If we want
content sniffing as well that is a second pass."

parsed action: edit app/upload.py
context: sha256:c41d…  ·  sampling: t=0.2
■ CALL 3 · FILE TYPES · 41:13
AI PROPOSEDallowlist on the filename extension only
THE MISTAKEan extension check reads the file's name, not the file; a renamed payload or a double extension (report.php.jpg) walks through
CANDIDATEanswered in 1:28
"ok, extensions are fine for now"
ASSESSORno probe, no test, no stated reason; the AI had offered the sniffing follow-up one message earlier and the reply doesn't engage it
→ MISSED THE AI'S MISTAKEthe spoof and double-extension checks failed here
AI · 42:52 · edit app/upload.py · +11 -1
DIFF
+ ALLOWED = {".png", ".jpg", ".pdf"}
+ def validate_ext(name):
+     ext = Path(name).suffix.lower()
+     if ext not in ALLOWED:
+         abort(415)
+     return ext
- # TODO: decide the type policy
$ pytest tests/test_visible.py · exit 0 · 1 passed · 43:31
CH 5
Name collisions
the AI's default was already atomic
TRUSTED THE AI, RIGHTLY 44:10–49:02 · 6 EVENTS
AI · 45:00 · edit app/upload.py
"If the target name exists I'll open with O_EXCL and retry with a numbered suffix, so two uploads can never clobber each other..."
■ CALL 4 · NAME COLLISIONS · 45:58
AI PROPOSEDexclusive create, suffix when the name exists
CANDIDATEanswered in 2:47
"agreed. exclusive create is right, keep it."
ASSESSORa sound default, recognized as such; agreement here is judgment, not passivity
→ TRUSTED THE AI, RIGHTLY
AI · 48:50 · edit app/upload.py · +6 -1
$ pytest tests/test_visible.py · exit 0 · 1 passed · 48:58
CH 6
Proof and submit
a test added by hand, the full suite, then submit
49:02–52:41 · 4 EVENTS
CANDIDATE · BY HAND · 49:02 · edit tests/test_upload_extra.py · +18 -0
A traversal regression test, written directly: posts "../../etc/x" as a filename and asserts the stored path stays inside the upload dir.
$ pytest tests/ · exit 0 · 2 passed · 51:26
AI · 51:50 · commit "upload: validated types, streamed reads, safe names"
CANDIDATE · 52:41 · SUBMIT
SCYLLUS · SEES ALL 41 EVENTS AND THE REPORT
SCYLLUS · AI ASSESSOR · FROM THE REPORT The shape of the session: read first, then four calls. Two were caught before any code landed, one was accepted in 1:28 without a probe, one was a sound accept. The by-hand regression test in chapter 6 is worth thirty seconds on its own.
Show me every hand edit Where did the misses happen? Did the AI leave an opening the candidate missed?
Ask anything about this session ↑
The raw log — every event, in order
append-only · 41 events · 00:00 to 52:41 · report v1
The engine's record as it was written, one row per event. The session view is this list, grouped into chapters; nothing exists there that does not exist here.
CH 1 · READING THE REPO · 00:00–08:44
0100:00SYSTEMrun.opensandbox up, clock started · budgets 70:00, 60 steps
0200:41AIcore.model_decisionread task.md, app/upload.py, tests/test_visible.py
0300:44SYSTEMcore.action_result3 files returned
0405:12SYSTEMcore.action_result$ pytest tests/test_visible.py → exit 1 · 1 failed (stub)
0506:30AIcore.model_decisionplan: implement save, return URL, raise naming/size/type as they come
0608:40AIcore.model_decisionbegins filename handling
CH 2 · FILENAME SAFETY · 08:44–18:02
0709:12AIcore.model_decisionproposes join(storage, file.filename) · edit app/upload.py
0809:56SYSTEMproctor.gate_checkproposal crosses CALL 1 · filename safety · held for the candidate
0914:08CANDIDATEcore.steeringcorrect: server names, uuid hex, validate the extension · answered in 4:12
1014:15AIcore.model_decisionrevised edit per instruction
1114:20SYSTEMcore.action_resultedit app/upload.py applied · +14 -3
1215:07SYSTEMcore.action_result$ pytest tests/test_visible.py → exit 0 · 1 passed
1316:02AIcore.model_decisionreturn-URL response shape
1416:40SYSTEMcore.action_resultedit app/upload.py applied · +16 -0
1517:30SYSTEMcore.action_result$ pytest tests/test_visible.py → exit 0 · 1 passed
CH 3 · SIZE LIMITS · 18:02–31:15
1619:03AIcore.model_decisionproposes get_data() whole-body read
1720:14SYSTEMproctor.gate_checkproposal crosses CALL 2 · size limits · held for the candidate
1826:19CANDIDATEcore.steeringcorrect: stream to temp file, hard byte cap, 413 on oversize · answered in 6:05
1926:30AIcore.model_decisionrevised streaming write
2027:52SYSTEMcore.action_resultedit app/upload.py applied · +9 -2
2129:41SYSTEMcore.action_result$ pytest tests/test_visible.py → exit 0 · 1 passed
2230:30AIcore.model_decisionre-reads the stream path, self-review
2331:05SYSTEMcore.action_resultread app/upload.py
CH 4 · FILE TYPES · 31:15–44:10
2433:20AIcore.model_decisionflags the TODO on type policy
2535:10SYSTEMcore.action_resultread app/upload.py · type-handling section
2640:12AIcore.model_decisionproposes extension allowlist · full message 2.1 KB · ctx sha256:c41d…
2741:13SYSTEMproctor.gate_checkproposal crosses CALL 3 · file types · held for the candidate
2842:41CANDIDATEcore.steeringaccept: "ok, extensions are fine for now" · answered in 1:28
2942:52SYSTEMcore.action_resultedit app/upload.py applied · +11 -1
3043:31SYSTEMcore.action_result$ pytest tests/test_visible.py → exit 0 · 1 passed
3143:50AIcore.model_decisionmoves on to collision handling
CH 5 · NAME COLLISIONS · 44:10–49:02
3244:20AIcore.model_decisionsurveys existing-name behavior
3345:00AIcore.model_decisionproposes O_EXCL exclusive create, numbered suffix
3445:58SYSTEMproctor.gate_checkproposal crosses CALL 4 · name collisions · held for the candidate
3548:45CANDIDATEcore.steeringaccept: "agreed. exclusive create is right, keep it." · answered in 2:47
3648:50SYSTEMcore.action_resultedit app/upload.py applied · +6 -1
3748:58SYSTEMcore.action_result$ pytest tests/test_visible.py → exit 0 · 1 passed
CH 6 · PROOF AND SUBMIT · 49:02–52:41
3849:02CANDIDATEcore.action_resultBY HAND · edit tests/test_upload_extra.py · +18 -0
3951:26SYSTEMcore.action_result$ pytest tests/ → exit 0 · 2 passed
4051:50AIcore.action_resultcommit "upload: validated types, streamed reads, safe names"
4152:41CANDIDATErun.submitsubmitted · clock stops · grading begins
M. Okafor
Brooklyn, NY · Eastern time
THE RESUME · AS PROVIDED DOWNLOAD ORIGINAL ›
Backend Engineer · Harbor Freight Data Co., NYC2024 – NOW
python services, an S3-backed media pipeline, the on-call rotation
Software Engineer · Millbrook Systems2022 – 2024
django APIs and file-processing workers
B.S. Computer Science · SUNY Binghamton2022
WHAT THEY TOLD US
OPEN TObackend or platform roles
LOCATIONNYC or remote, Eastern time
AVAILABLEfrom September 1
SESSIONS WITH SCYLLUS
fx-greet warm-up · practice, ungradedJUL 8
Arena standing · none yetOPENS SOON
WHAT WE HOLD · THE RULES
PROVIDEDeverything here came from the candidate or their sessions, with consent
NEVERscraping, third-party enrichment, photos, age, protected attributes, salary history
DISCLOSEDcandidates are told an AI-assisted assessment is used
RETENTIONdeleted on request