Walmart prep Campus Grad Cybersecurity
You and contextHigh yield7,577 words, about 38 min

Resume defence

Covers every bullet: Siege (QNU Labs), Aidan, Xlancr, PeerWatch, Coding Platform, Gravitas, CookOff, CTF, Skills. JD anchor: Tech 2 is a resume deep-dive; HR/HM pushes ownership. Walmart Cybersecurity candidates report interviewers interrupting stories to demand exact numbers, dates, and outcomes — vague answers cost offers.


1. Why Resume Defence Decides the Loop

Stage What happens to your bullets
Tech 1 "Walk me through one project" — they pick the one you lead with, go deep
Tech 2 Resume deep-dive + scenarios; interrupt, zoom in, demand numbers
HR/HM "What did you do?", "what went wrong?", "what would you do differently?"

They score ownership (what you did), numbers (baselines + deltas), tradeoffs (rejected alternative + cost), attacker mindset (how I'd break it), failure handling (bug → root cause → fix → regression test), and calibration (honest "I haven't done X").

6-beat skeleton (every bullet): 1. Problem — one sentence. ("Tenants in one pool could resolve each other's live session.") 2. Action — what you did, tool named. ("Keyed registry by (pool, uid), fail-closed resolution, regression tests.") 3. Tradeoff — rejected alternative + cost. ("Composite key keeps ops simple; live-session migration at deploy.") 4. Attacker view — how you'd break it. ("Blank/unicode-variant uid; stale session reuse.") 5. Rebuild — one concrete improvement. ("Signed opaque tokens; authorise at use time.") 6. Stop talking. Keep 10-, 2-, and 1-minute versions of every project. STAR mapping: Problem/Action = Situation/Task/Action, metric + verdict = Result.

Discretion clause (say once): "Siege is internal QNU tooling. I'll describe architecture and technique classes; I won't name customers, keys, internal endpoints, or unrepatched bugs. The cross-tenant issue was found internally, fixed, and regression-tested." Discretion is a scored behaviour.


2. Per-Bullet Briefs

Fill every N with your real number; an honest "19 implemented and runnable" beats a fake "19 in production".

Numbers consistency across surfaces (check once, before the room): resume, repo READMEs, and spoken answers must agree, because interviewers cross-check. Name the four Siege channels the same way everywhere — the resume parenthetical lists three of four, so name all four and say why the fourth is separate. 19 techniques means 19 implemented and runnable; for production runs give the run count N and its window. Cross-tenant is 1 bug, N regression tests — and only say "no exploitation found" if the review concluded that. Label Xlancr's ~50%/70% as measured or estimated, with the window. Tie Gravitas's 3M+/₹50L+ to a named analytics source and date window. When two numbers could both be true — 200/min peak vs ~3.3/s sustained — give the conversion yourself.

2.1 Siege — AI red-team platform, 4 channels + attack-graph executor

"Four delivery channels (prompt injection, workspace planting, MCP interception proxy) and an attack-graph executor for chained multi-turn attacks."

Hook: "Real attacks chain: a planted file is read on turn three, triggers a tool call, then exfiltrates. Siege models attacks as a graph — nodes are payloads, edges are order — and runs one technique through chat, workspace, or MCP tool plane."

Two lengths for Siege (pick by signal):

30-second pitch: "Siege is an AI red-team platform I built in Go. It runs attack techniques through four delivery channels — prompt injection, workspace planting, MCP tool interception — and executes chained multi-turn attacks as a graph: a planted file read on turn three can trigger a tool call and exfiltration on turn five. It logs every step as evidence and turns findings into detection-rule drafts. I wrote the 19-technique catalog behind it, and the security review surfaced a cross-tenant session bug I fixed and regression-tested."

5-minute whiteboard: open with the chained-attack problem, draw the executor — nodes, edges, topological pass, evidence rows — then one technique end to end: payload, channel, success detector. Then the detection-rule bridge, then the cross-tenant fix in 60 seconds. Close on one limitation (the DSL is a cost; the proxy is in-process) before they find it. If they interrupt the pitch at "graph", explain nodes and edges only, then wait.

  • Problem: manual single-turn testing; no chained attacks, cross-surface coverage, or reproducible evidence.
  • Action: Go platform — technique catalog + engine; 4 channels; DAG of steps (channel, payload, preconditions); MCP proxy logging/rewriting tool traffic.
  • Tradeoff: graph gives reproducibility + per-step attribution but needs a DSL; in-process proxy is simple, a sidecar would survive restarts and scale independently.
  • Attacker view: steer the chain via a step's own output; compromise the credential-holding proxy; template/path injection via payload fields; cycles → unbounded turns.
  • Rebuild: signed definitions, per-node turn/token budgets, sandboxed workspaces, zero-trust proxy (mTLS, least privilege, tamper-evident logs). Prep note: name all four channels from memory — the resume parenthetical lists three.

Why this design: a graph over a linear script list because real attacks chain across turns and surfaces — a linear runner can't express "wait for the file read, then fire the tool-call step". The cost: a DAG needs a definition format, so every technique carries schema I validate, version, and explain. The in-process MCP proxy was the same trade: rewriter and logger ship with the executor, so a run is reproducible from its own evidence — but a crash takes the interceptor with it. A sidecar survives restarts and scales independently; that's the next move.

How an interviewer attacks it:

"What stops an attack graph from looping forever?" — "Three layers. At load time I reject cycles — a DAG is a precondition, not a hope. At run time every node carries a turn and token budget and the context cancels when it's spent. And the run itself has a cap. The worst case is a bounded, evidence-filled failure, not an unbounded conversation."

"Why a detector instead of an LLM judging the transcript?" — "Because an LLM judge is a probabilistic witness and I want evidence an analyst can check. Where I can, I plant a canary and look for it in the output, a tool call, or a file read — deterministic. Where the leak is semantic, I fall back to the judge and label the verdict. I bias to precision: a false 'compliant' finding burns analyst trust in the platform."

"If you rebuilt one thing, what?" — "The proxy. Separate zero-trust service: mTLS to the executor, least privilege on the tool plane, tamper-evident logs. In-process is fine for a lab, wrong for anything long-lived. And I'd sign technique definitions — anything the executor runs should be authored, not merely present."

How an attacker breaks it (and the fix): the executor is a payload interpreter, so the definition format is the first surface — a payload field reaching a template engine or file path is injection with extra steps. Fix: parametrised rendering, path allow-lists, signed definitions. Second, the proxy holds the credentials it observes — mTLS, least privilege, tamper-evident logs. Third, the run log tells an attacker what tripped — treat evidence stores as sensitive. Cycles and budgets are handled above.

Q Model answer
Why Go? Goroutine-per-branch + context cancellation; static binary deploys into restricted networks; strong HTTP stdlib. Python loses on parallelism, Rust on iteration speed.
Draw the executor. Nodes (step_id, channel, payload_ref, preconditions); edges = order/dependency; topological pass runs ready nodes concurrently; persists (run_id, node_id, raw_io, verdict). Success propagates only if the detector tripped; cycles rejected at load.
How do you know an attack succeeded? Deterministic detectors where possible (canary in output, secret in a tool call, file-read recorded), LLM-judge otherwise; raw I/O kept for review. Precision over recall — false "compliance" findings burn analysts.

2.2 Siege — 19 techniques + detection-rule drafts

"Authored a catalog of 19 attack techniques … and generated detection-rule drafts from findings."

Hook: "I wrote the catalog — 19 techniques across prompt injection, credential exfiltration, and MCP tool-plane tampering — plus the bridge from finding to draft detection rule: a successful attack yields a reviewable Sigma-style draft, not a PDF nobody actions."

  • Problem: findings died in reports; no coverage map; no link to the SOC pipeline.
  • Action: each finding carries the observable (tool-call pattern, canary touch, anomalous sequence) → templated draft with severity, ATLAS/ATT&CK mapping, FP notes.
  • Tradeoff: drafts, not auto-deploy — a canary-based rule deployed blindly false-positives on real traffic; human review costs latency but protects alert quality.
  • Attacker view: rule-syntax injection via finding data; mimic legitimate automation so the rule never fires; flood canary-like traffic to get the rule muted.
  • Rebuild: parametrised templating (no string concat), 3 positive + 3 negative test events per draft, rule lifecycle (draft → tested → enabled → tuned), FP feedback loop.

Why this design: the catalog is the product; the engine executes what the catalog names. I wrote the 19 techniques as data — precondition, payload, channel, success detector — so a new technique is a reviewable diff, not a release. The cost: data-driven techniques are easier to audit and harder to make expressive, so anything the format can't say becomes engine work — worth it to keep the catalog reviewable by people who don't read Go. Drafts instead of auto-deploy because whoever enables a rule owns its alert quality, and a rule deployed blind will false-positive on real traffic.

How an interviewer attacks it:

"Why 19 — how did you pick the set?" — "I started from the surfaces I could reach — chat input, workspace files, the MCP tool plane — and wrote the techniques an attacker would chain across them. 19 is what was implemented and runnable, not a target. If I'd padded to a round 20, the padding is the first thing you'd find."

"What makes a draft reviewable?" — "Four things: the observable it keys on, severity, the ATLAS/ATT&CK mapping, and false-positive notes with three positive and three negative test events. The FP notes matter most — a rule that fires on every canary touch is a rule someone mutes in week one."

"What if the SOC wants rules enabled automatically?" — "Automation at the gate, not at the write: the lifecycle draft → tested → enabled → tuned gates each step on its test events."

How an attacker breaks it (and the fix): finding data flows into rule templates, so a payload that looks like rule syntax can smuggle in a rule that never fires — parametrised templating, never concatenation. An attacker who learns the canary can touch it deliberately until someone mutes the rule — rotate canaries, rate-limit the signal. And the mimicry attack: behave like legitimate automation so the sequence never trips — which is why the rules key on sequences and egress, not single events.

Q Model answer
What makes a good LLM detection rule? Key on what the defender controls: tool-call sequences, egress destinations, canary touchpoints, prompt-read attempts, rate spikes. Prompt content is a bad key — paraphrase/base64/multi-turn defeats it.

2.3 Siege — cross-tenant session takeover (pool, uid)

Hook: "A security review found the session registry keyed by pool, not user, so a request in the same pool could attach to another tenant's live session. I keyed it (pool, uid), made UID resolution fail closed — missing/ambiguous uid denies, never falls back — and wrote takeover regression tests."

  • Problem: broken access control (IDOR at the session layer) → auth bypass + cross-tenant disclosure.
  • Action: composite key; fail-closed resolution; live-session migration (verify before saying); tests plant tenant A's session and assert tenant B is denied.
  • Tradeoff: fail-closed causes visible outages on IdP hiccups — correct trade vs silent leak, alerted on. Opaque random tokens would eliminate the class but add a token store.
  • Attacker view: blank/duplicate/unicode-colliding uid; session reaped between resolve and use; stale reuse after offboarding; wildcard pool lookup.
  • Rebuild: high-entropy signed tokens bound to (tenant, uid, expiry); authorise at use time; audit every attach; property/fuzz tests on the resolver.

Why this design: I fixed the key instead of adding another check, because a scoped key makes the bug unrepresentable — a lookup that must contain (pool, uid) can't return another user's session even if a code path forgets a guard. Fail-closed resolution is the deliberate cost: when the IdP returns nothing or something ambiguous, the user sees a denial and an alert fires, instead of a silent fallback to a shared session — visible failures over invisible leaks. The rejected alternative — opaque high-entropy tokens — kills the whole class but adds a token store and rotation story; I named it as the next step.

How an interviewer attacks it:

"What happens to sessions already live when you deploy the fix?" — "The migration path is the part I verified before claiming anything. Old sessions either re-key cleanly or are reaped and force re-login. If a live session can't be re-keyed safely, I'd rather terminate it than leave it ambiguous. Which happened is a deploy fact — I checked it rather than smoothing it."

"Fail-closed is fine until an IdP outage denies a whole tenant — still the right trade?" — "Yes: a denial is visible, a leak is not. The mitigation is operational — alert on denial rate so an IdP hiccup pages someone. If availability ever forced a fallback, it would be an explicit, audited break-glass path with reduced scope, never a silent 'blank uid means shared pool'."

"Why not just add an authorisation check where sessions attach?" — "Because checks live on code paths and code paths multiply. I wrote the tests that assert the check, but the structural fix is the key: every path that resolves a session now gets a per-user session or an error. Scoped key plus takeover regression tests — plant A's session, assert B is denied — together are what I'd want in anyone's PR."

How an attacker breaks it (and the fix): against the old key the attack is a plain IDOR — request the same pool, name a victim, attach. After the fix the resolver is the target: blank, duplicate, or unicode-colliding uids, a wildcarded pool lookup, or a session reaped between resolve and use. The resolver matrix tests those and fails closed on all. The deeper fix is binding: high-entropy signed tokens carrying (tenant, uid, expiry), authorised at use time, every attach audited — so even a resolver bug becomes visible.

Q Model answer
Explain it like a PM. "Sessions were per worker pool, but the lookup asked 'which pool?', not 'which user?'. Two customers in a pool could attach to the same session — one seeing another's data. Only a logged-in customer could trigger it; that's the boundary that matters."
How did you find and prove it? Traced every registry read/write asking "does the key contain the principal?" It didn't. Wrote a failing A→B attach test first, then fixed; resolver matrix (missing, blank, duplicate, unicode) fails closed.
IDOR analogy? Same root cause: an object reference valid for one principal but not scoped to the requester. Fix the same way — authorise on object + principal, deny by default.
Did you disclose it? Internally, via the review process; fixed and regression-tested. (Say calmly; teams listen for over-dramatisation. Only say "no data left / no exploitation found" if that is what the review actually concluded — otherwise: "I know it was fixed and regression-tested; I can't speak to the incident analysis.")

2.4 Aidan — AI retrieval observability

Hook: "A bad answer looked like a good answer — failures were invisible. I added request-scoped structured logging, an admin failed-runs view showing the failing stage, and automatic retention. Diagnosis went from 'grep timestamps' to 'open failed-runs, replay the stage'."

  • Problem: silent retrieval/ranking failures; no per-request trace; unbounded log accumulation.
  • Action: request-id logging (stage, latency, retrieval count); failed-run admin views; windowed auto-retention.
  • Tradeoff: verbosity vs PII — metadata/hashes by default, raw prompts/documents only in short-TTL debug mode. Retention long enough to debug vs compliance cost.
  • Attacker view: log injection via newlines in retrieved content; PII/secrets in prompts; admin-view IDOR; retention job failing silently; huge payload logging as resource abuse.
  • Rebuild: redaction at the logging boundary, field allow-list, size caps, per-tenant RBAC on admin views, monitored deletion job, break-glass capture with audit.

Why this design: observability at the request boundary because that's the only place all stages meet — per-stage logs with no request id is grep archaeology. The cost: metadata and hashes by default means some investigations end in "enable debug and reproduce", and I chose that over storing raw prompts. Windowed retention for the same reason: a log store is a data store, and "keep it forever" is how a debugging aid becomes a breach.

How an interviewer attacks it: "First thing you check for a wrong answer?" — "Retrieved chunk ids and their scores. Right chunk retrieved but ranked below cutoff is a retrieval failure; right chunk in the prompt and ignored is a generation failure. That fork is why I log stage timings per request id." "What would make you log raw prompts?" — "A break-glass flag: short TTL, per-tenant, audited. Never a default."

How an attacker breaks it (and the fix): retrieved content flows into logs, so newlines and control characters forge log lines — redact at the logging boundary and cap sizes. The failed-runs admin view is itself an object lookup, so it inherits §3.2: per-tenant RBAC, authorised at use time. And the retention job is a deletion promise — monitor it, or "30-day retention" is a claim, not a control.

Q Model answer
What exactly do you log for a RAG request? Request id; hashed tenant/user; query hash + length; retrieved chunk ids/scores (not text); stage timings; model + version; guardrail verdict; error class. Raw text only in opt-in debug.
Debug a wrong answer in production? From the request id: which chunks were retrieved and their scores → right chunk ranked low (retrieval) vs ranked high but unused (generation). That fork is the point of stage-level logging.

2.5 Xlancr — multi-layer Redis/FastAPI cache (~50%, 70% coverage)

Hook: "The API recomputed expensive, mostly-identical responses. I added a layered cache — in-process for hot keys, Redis for shared — with explicit key construction and invalidation on write. Cost and latency dropped roughly half; coverage rose to 70%, gated in CI."

  • Problem: redundant expensive computation; no cache; no test gate.
  • Action: two-layer cache-aside (local TTL → Redis), key = endpoint + normalised params + user/tenant + version tag; invalidation on writes + bounded TTL; CI coverage gate.
  • Tradeoff: cache-aside tolerates Redis downtime (fall back to compute) but has a stale window; write-through is fresher but couples writes to Redis. A coverage gate can be gamed — a floor, not quality.
  • Attacker view: unkeyed input (header/cookie) shapes a response → cache poisoning; missing tenant in key → cross-user leak (same class as 2.3); stampede on expiry; caching auth decisions.
  • Rebuild: explicit key builder with allow-listed inputs + a test that two users never read each other's entry; single-flight locks; negative caching; hit/eviction metrics; never cache auth decisions.

Why this design: cache-aside over write-through because the read path had to survive Redis being down — falling back to compute degrades latency; write-through failure breaks writes. The cost is a stale window, bounded by TTL and version-tag invalidation. I accepted "correct with a small stale window" over perfect freshness at database load. The version tag makes invalidation cheap: a write bumps it and old entries age out.

How an interviewer attacks it: "What's in the key?" — "Endpoint, normalised params, user/tenant, version tag. If a header or cookie shapes the response and isn't in the key, that's poisoning; if the tenant isn't in the key, that's a cross-user leak — same bug class as the Siege session key, and I have a test that two users never share an entry." "Hot key expires — what happens?" — "Stampede: everyone recomputes at once. Single-flight means one goroutine recomputes and the rest wait; negative caching handles repeated misses on data that doesn't exist."

How an attacker breaks it (and the fix): unkeyed input shapes a cached response (poisoning), a missing tenant in the key leaks across users (the §3.2 class), deliberate misses force expensive recomputation (compute DoS), and caching auth decisions turns a stale entry into an access grant. Fix: allow-listed key builder, the two-users-never-share test in CI, single-flight, negative caching, hit/eviction metrics — and auth decisions stay out of the cache.

Q Model answer
~50% — measured how? (Be exact.) "Same workload before/after: p50/p95 latency and per-1k-request upstream cost over two weeks." If it was an estimate, say so — "roughly half from dashboard deltas" — and how you'd measure properly.
Invalidation? Writes bump a version tag for the affected entity and clear that key family in both layers; a bounded TTL is the safety net. "Correct with a small stale window" over perfect freshness at DB load.

2.6 PeerWatch — Go P2P full-mesh TCP, 10 message types, 512KB SHA-256 chunks

Hook: "Video isn't re-streamed — peers sync playback and fetch media chunks from each other over a full-mesh TCP topology. I hand-wrote the wire protocol: 10 message types, length-prefixed framing, 512KB chunks each SHA-256 verified."

  • Problem: centralised streaming is bandwidth-heavy; wanted peer media sharing + synchronised playback.
  • Action: Go TCP listeners per peer, full mesh, type + length framing, sync messages, hash-verified chunk transfer, mpv IPC playback.
  • Tradeoff: TCP gives ordering/reliability but head-of-line blocking (not NAT-friendliness — UDP hole punching is more reliable, ~82% vs ~64% of NATs, RFC 5128 / MIT PDOS); full mesh has no bottleneck peer but O(n²) connections (small rooms only); custom protocol is low-overhead but framing bugs are on you.
  • Attacker view: plaintext, unauthenticated — MITM reads/modifies; SHA-256 catches corruption, but a malicious peer can serve valid chunks of the wrong content; spoofed identity; crafted length → memory exhaustion; slow-chunk DoS.
  • Rebuild: TLS/Noise with per-peer identity keys; signed manifest binding hashes to a room/version; authenticated joins; read deadlines, max-frame validation, flow control; hybrid mesh + relay fallback.

Why this design: full mesh because a watch-together room has no bottleneck peer and every hop is one connection away — the cost is O(n²) connections, fine at room scale, wrong at broadcast scale. Hand-written framing because 10 message types and a length prefix are all the protocol needs — the cost is every framing bug is mine, so max-frame validation and read deadlines are part of the protocol. TCP for ordering on sync, accepting head-of-line blocking as the price.

How an interviewer attacks it: "What does the hash actually buy you?" — "Corruption detection in transit and on disk. Not authenticity: if the peer controls the hash list, valid chunks of the wrong video pass. Authenticity needs a signed manifest binding hashes to a room and version." "Why not QUIC?" — "Ordered sync mattered more than minimal latency and I wanted the standard library. QUIC's streams and built-in TLS are the right modern answer; for an internet-facing rebuild I'd take the dependency."

How an attacker breaks it (and the fix): the wire is plaintext and unauthenticated — a MITM reads and modifies playback sync; a malicious peer serves valid-but-wrong chunks; a crafted length field exhausts memory. Fix: TLS or Noise with per-peer identity keys, a signed manifest, authenticated joins, frame limits with read deadlines. | Q | Model answer | |---|---| | Why TCP, not QUIC/UDP? | Needed ordered sync and reliability more than minimal latency. Not for NAT — traversal needs hole punching or a relay regardless. QUIC (built-in TLS, streams, no head-of-line blocking) is the modern answer, but a heavier dependency. | | NAT traversal? | Honestly: it dials directly and works on LANs/known networks. Consumer NATs need STUN + hole punching with TURN relay fallback; today it fails visibly when peers aren't reachable. | | What does SHA-256 NOT catch? | Malicious replacement — if the peer also controls the hash list, valid-but-wrong content passes. Integrity needs a signed/anchored manifest from a trusted source. |

2.7 Coding Platform — Judge0 + Asynq + Gin + sqlc, 600+ users

Hook: "Gin API, sqlc queries, Redis-backed Asynq queue, Judge0 for sandboxed execution. The API only enqueues and returns a token; workers execute and persist verdicts — a slow or malicious submission never blocks an HTTP worker. A live contest ran on it with 600+ users."

Honesty line: "Judge0 is the sandbox I integrated, not one I wrote — my ownership is the pipeline: queueing, status, idempotency, limits, API." Claiming you built it then fumbling isolate/seccomp destroys credibility. Verify before the room: enable_network=false and allow_enable_network=false in judge0.conf (CE defaults are true — GHSA-q7vg-26pg-v5hr is why I checked).

  • Problem: untrusted code execution at contest scale; sync execution exhausts web workers; duplicate/lost jobs give wrong verdicts.
  • Action: submit → validate → enqueue (Asynq) → worker → Judge0 (timeout) → persist verdict → leaderboard; per-user rate limit; job keyed to (user, problem, submission).
  • Tradeoff: async (Asynq over SKIP LOCKED/Kafka: retries + visibility, small ops surface) isolates blast radius and absorbs spikes but adds polling latency and a backlog failure mode; Judge0 gives well-tested isolate-based sandboxing when configured, but is a dependency with less limit control.
  • Attacker view: fork/thread bomb; miner; SSRF to cloud metadata (169.254.169.254) or internal services; FS escape; submission flood starving the queue; zip-bomb tests; forged verdict callback.
  • Rebuild: verify + document controls (no network, read-only root, non-root, dropped caps, seccomp, cgroup PID/mem caps, wall + CPU timeouts); idempotent processing with state guards; queue-depth backpressure; verdict signing; CI suite of hostile submissions.

Why this design: the API only enqueues because HTTP workers should never wait on untrusted code — a slow submission occupies a queue slot, not a request thread. Asynq over Postgres SKIP LOCKED because retries and visibility come free and Redis was already running; over Kafka because a contest doesn't need a broker. The cost: polling latency, a backlog failure mode if workers die, and Judge0 as a dependency whose sandbox config I must verify.

How an interviewer attacks it: "What did you actually build?" — "The pipeline: queueing, status, idempotency, limits, API. Judge0 is the sandbox I integrated and configured — I didn't write isolate, and I won't claim I did." "One config you'd check first?" — "Network. The CE config can let a submission enable its own network, so I deny that and keep a drift check — submission-as-SSRF is the highest-value attack on a judge."

How an attacker breaks it (and the fix): fork bombs and miners (PID/CPU/mem caps, wall timeout), SSRF to cloud metadata or internal services (no egress), filesystem escape (read-only root, non-root, dropped caps), queue starvation (rate limits plus backpressure), forged verdict callbacks (job keyed to (user, problem, submission) plus state guards). Validated by a CI suite of hostile submissions asserting verdicts.

Q Model answer
Why Asynq? Redis-backed Go queue with retries, scheduling, and visibility (pending/active/failed), small ops surface; Redis was already running. Alternatives: Postgres SKIP LOCKED (simpler, polling latency) or Kafka (overkill).
Fork bomb prevention? PID cap, CPU time, memory cap, no network (deny allow_enable_network in config + drift check), non-root, dropped caps inside Judge0. I validated with hostile submissions (infinite loop, fork loop, socket connect) and asserted verdicts — not by reading docs.
A submission is stuck "Pending" — debug? Trace the id: DB state → Asynq task state → worker logs → Judge0 health. Classify: never-picked (worker down/backlog), picked-but-hung (timeout/socket), completed-no-persist. Add oldest-task-age + queue-depth dashboard.

2.8 Gravitas — seat-hold race, transaction leak, 3M+ visits / ₹50L+ / dashboards

"Eliminated booking race conditions via a Redis Sorted Sets seat-hold … Resolved a critical transaction leak on all code paths, preventing connection pool exhaustion; admin dashboards reduced support tickets by 95%."

Hook (race): "Two people could pass the 'is this seat free?' check and both pay — check-then-act. I made the hold atomic: seats are ZSET members scored by hold-expiry, so acquiring is one atomic op and expiry is a range query. Losers get 'held by someone else'." Hook (leak): "Error paths skipped transaction cleanup, leaking connections until the pool was exhausted. I made every path release — defer rollback + explicit commit — added pool-saturation monitoring, and validated by simulating failures. Dashboards cut tickets 95%."

  • Problem: TOCTOU oversell; leaked transactions → pool exhaustion; support blind.
  • Action: ZSET hold (ZADD score = expiry epoch, atomic acquire via Lua/NX, ZRANGEBYSCORE availability, ZREM release, TTL sweeper); confirm → durable Postgres row; defer tx.Rollback() on every path; admin dashboards.
  • Tradeoff: Redis gives speed and native TTL but creates dual-write consistency with the system of record (reconcile on confirm, expire on failure). Longer holds improve UX but cut inventory turnover.
  • Attacker view: hold hoarding; bypassing the hold at payment (payment must re-validate server-side, never trust the client); replay of hold/release tokens; clock skew breaking expiry; keys not namespaced per show → collisions; dashboard IDOR. Leak view: deliberately triggering error paths (bad payloads, cancel-then-retry) to drain the pool = application-layer DoS — pool-saturation monitoring is a security control.
  • Rebuild: per-user hold caps; payment needs a signed single-use hold token and re-checks atomically; keys show:{id}:seat:{id}; RBAC + audit on dashboards; N-goroutine test hammering one seat asserting exactly one winner.

Why this design: the ZSET hold collapses check-and-hold into one atomic operation because the score is the expiry — "what's still held" is a range query and expiry is free. The cost is dual-write consistency: Redis holds inventory state briefly while Postgres is the system of record, so confirm reconciles and failure expires. Longer holds improve UX but stall turnover, so hold TTL is a business knob. On the leak, the fix was structural: every path releases — defer rollback plus explicit commit — because "remember to clean up" is not a control.

How an interviewer attacks it: "Where can it still oversell?" — "At payment, if that path trusts the client or skips the atomic re-check. Payment re-validates against the hold with a single-use signed token and one atomic op." "How did the leak ship?" — "Error paths didn't own cleanup — happy path released, failure path didn't. I found it by simulating failures and watching the pool drain; the saturation monitor I added is also the DoS detector."

How an attacker breaks it (and the fix): hold hoarding (per-user caps), replayed hold/release tokens (single-use signed), clock skew breaking expiry (server-side clock only), key collisions across shows (namespaced show:{id}:seat:{id}), dashboard IDOR (RBAC + audit). And the application-layer DoS: triggering error paths to drain the pool — fixed by deferred rollback, watched by the saturation alert.

Q Model answer
Why a Sorted Set? The score is the expiry, so "what's still held" is one ZRANGEBYSCORE and expiry is cheap. A plain SET loses ordered range reads; a hash loses ordering entirely.
Is the ZSET trick atomic? Single commands are; the danger is check-then-ZADD in app code. Acquisition is one Lua script (membership + add) or ZADD NX semantics, so it's atomic server-side.
Prove the race is gone. Concurrency test: N goroutines attempt one seat → exactly 1 success, N−1 clean rejections, repeated. Plus a production invariant: oversell count is a monitored metric that must stay zero.
Explain it like a PM. "Two people paid for one seat because the system checked, then acted — I made checking and holding one atomic step."
3M / ₹50L / zero downtime — source? Analytics + payment-record totals; "zero downtime" = no payment-critical outage/error burn above threshold during the run window. (Have the exact window and source ready.)

2.9 CookOff 9.0 — 5-member team, 15k evals, 200/min, <10s

Hook: "A cooking-themed event portal; I led the 5-person backend team and built the submission/judging pipeline. 15,000+ evaluations, peaking around 200 submissions a minute, average verdicts under 10 seconds, no downtime."

  • Problem: short high-spike event; fast fair judging; auth + anti-abuse.
  • Action: service split (auth, submissions, judging queue, leaderboard, notifications); queue-based judging; backpressure; per-user rate limits.
  • Tradeoff: simple per-submission limits over aggressive anti-collusion detection (timeboxed event; false accusations worse than the risk); workers sized to peak (~3.3/s sustained) idling between events.
  • Attacker view: submission flood starves other teams; duplicate submissions game ranking; IDOR on submissions; shared team credentials; forged leaderboard writes.
  • Rebuild: fairness quotas + reserved capacity, content-hash dedupe, server-only leaderboard writes, load test at 3× observed peak, incident runbook (queue drain, worker restart, surge promotion).

Why this design: the event is short and spiky, so I sized workers to peak (~3.3/s sustained) with headroom and let the queue absorb bursts — over-provisioning a two-day event is cheap insurance. The cost: simple per-submission limits instead of anti-collusion detection, because the event was timeboxed and a false accusation is worse than the residual risk.

How an interviewer attacks it: "What unfairness did you leave unsolved?" — "Collusion inside a team — shared solutions. Quotas and dedupe bound the blast radius; for a timeboxed event I accepted the rest." "Hardest bug?" — Pick a real one from the table below.

How an attacker breaks it (and the fix): a submission flood starving other teams (fairness quotas, reserved capacity), duplicate submissions gaming ranking (content-hash dedupe), IDOR on submissions, forged leaderboard writes (server-only writes). Prove it with a load test at 3× observed peak and keep the runbook ready.

Q Model answer
200/min — how sized? 200/min ≈ 3.3/s sustained, bursty; workers = peak ÷ per-worker rate with ~2× headroom, queue absorbs spikes; validated in a dress rehearsal rather than guessed.
Hardest bug? (Pick a real one.) E.g., duplicate verdicts from at-least-once delivery corrupting the leaderboard → idempotent upsert keyed by submission id + state guard; or worker crash leaving submissions "judging" forever → reaper requeues past timeout.

2.10 CTF — 2nd place, Ghidra/GDB

Hook: "I placed 2nd in the CTF, solving reverse-engineering and binary-analysis challenges with Ghidra and GDB. Static first — Ghidra decompile, xrefs to interesting calls — then GDB: break on comparisons, dump the flag from memory."

  • Problem: obfuscated binaries, unknown logic, memory-corruption challenges.
  • Action: static (strings, imports, decompile, rename, xref to strcmp/crypto); dynamic (breakpoints, registers/stack, memory dump). Concept readiness only — say only if true: pwn offset via cyclic pattern, checksec, ROP/ret2win.
  • Tradeoff: static is safe but slow on packed/anti-analysis binaries; dynamic is faster but can be fooled and needs an isolated VM. Timebox: 15 min static, then rotate.
  • Attacker view: these tools ARE incident-response malware triage — same hypothesis-and-verify loop on an unknown binary.
  • Rebuild: scripted tooling (Ghidra scripts, pwntools templates), a writeup per challenge, snapshot/VM workflow.

Why this workflow: static first because reading decompiled code is safe and cheap; move to GDB when the question becomes "what is this value at runtime". The cost: static crawls on packed binaries and dynamic can be fooled by anti-debug checks — so it's a rotation (15 minutes static, then move) inside a snapshot VM, and I say the packed-binary case is at my boundary.

How an interviewer attacks it: "Walk one challenge end to end." — Prepare this story (template in the table above). "What if it's packed?" — "Identify the packer from headers and imports, run it in a VM, dump at the original entry point — honestly, that's at my boundary; my strength is the plain stripped-binary case, and I'd tell you that before you found it."

How an attacker breaks it (and the fix): this section is the attacker view — triage an unknown binary, hypothesise from strings/imports/behaviour, verify in a debugger. The rebuild is workflow: scripted tooling so analysis is repeatable, a writeup per challenge so the method compounds, snapshot VMs so hostile samples stay contained. | Q | Model answer | |---|---| | Walk one challenge end to end. | Prepare this story. Template: "A stripped ELF compared input byte-by-byte to an embedded constant. In GDB I broke on the compare, read the expected byte from a register each iteration, recovered the flag, then verified it." | | Buffer overflow basics? (concept readiness — answer only if you have actually done pwn) | Offset to return address via cyclic pattern → check mitigations (NX, PIE, canary, ASLR) → redirect: ret2win, else ROP, or leak-and-return to defeat ASLR/PIE. |

2.11 Skills table — depth line + boundary per tool

Skill Depth line Boundary (say it)
Go Goroutines/channels/context, net/http, interfaces, sync, testing Generics/reflect shallow; scheduler internals textbook-level
Rust Ownership/borrowing, Result/?, traits, Cargo, async basics Production Tokio at scale; complex lifetime generics need docs
TypeScript Types/generics, strict mode, Node backends Advanced mapped/conditional types
SQL Joins, indexes, plans, ACID, sqlc/Prisma/Drizzle Complex window functions — would look up
Flask / Django / NestJS Blueprints + factory / ORM + migrations + auth / modules + DI + guards Django async, NestJS GraphQL federation
Postgres / Mongo / Redis / DuckDB MVCC, EXPLAIN, pooling, upserts / aggregation, indexes / ZSET, TTL, Lua, caching / columnar OLAP, Parquet Sharding, cluster mode, replication internals — conceptual
Docker / Linux / Actions Multi-stage, limits, non-root / permissions, systemd, logs, ss/lsof, hardening / matrix, caching, secrets, gates Kubernetes is a real gap; eBPF; self-hosted runners
Burp Suite Proxy/Repeater/Intruder, login/search flow tests Scanner-only use; advanced extensions
Wireshark Display filters, follow TCP stream, cleartext-cred/DNS-tunnel spot Large-scale capture automation
Autopsy / ProDiscover File carving, deleted recovery, timeline basics Deep filesystem-internals forensics
Malware analysis Static (strings, hashes, VT, PE headers) vs dynamic (sandbox) Packed-binary unpacking, kernel-level — would look up

Rule: at a boundary: "I've used X but not that deep — nearest is Y; I'd learn it via Z." Calibration beats bluffing.

Pin the 3 repos you'll defend, make READMEs demo-ready, and keep sophic.dev open in a tab. Checklist in §4.

What "defend a repo" means: any file they open should answer "why is it written this way?" when pointed at. If a README states a number, it must match the numbers-consistency note above. Archive old repos rather than hiding them — an honest README with a limitations section reads better than a deleted one.


3. Cross-Cutting Themes (askable against any bullet)

§2.2 is the Tech-2 differentiator: "a good LLM rule keys on defender-controlled signals (tool-call sequences, egress, canary touches) — never prompt text."

3.1 Sandbox escapes

"Controls layer: filesystem (read-only root, per-run tmpfs, no docker.sock), process (non-root, dropped caps, PID cap, seccomp), resources (cgroup caps, wall + CPU timeouts), network (no egress or default-deny). I test host mounting, runtime-socket abuse, kernel bugs, and submission-as-SSRF-pivot to cloud metadata or internal services."

Attack Control Test
Fork/thread bomb; miner PID + CPU/mem caps; wall timeout Fork/busy loop → EAGAIN/killed
Metadata SSRF No egress; IMDSv2; blocked routes connect(169.254.169.254) denied
Host FS read; runtime socket Read-only root, tmpfs, no docker.sock, drop caps /etc/shadow fails; socket path absent
Queue starvation Rate limits, backpressure Flood → 429/delay, workers alive
Submitter enables own network (Judge0 allow_enable_network defaults true) Deny in judge0.conf + config drift check POST with enable_network=true → still no egress

3.2 IDOR / keying

"Any lookup taking an identifier must be scoped to the principal and authorised at use time, not just at lookup. The cross-tenant fix and cache-key design are the same lesson: the key must contain the tenant/user." Test matrix: missing key part, null/empty, type confusion, unicode normalisation, wildcard, stale entry, check-use race, cross-tenant enumeration.

3.3 Race conditions

Say TOCTOU and atomic. Find the check-then-act → collapse into one atomic op (Redis Lua/ZADD NX, Postgres ON CONFLICT, SELECT … FOR UPDATE) → prove with a concurrency test asserting exactly one winner. Seat-hold and session-attach are the same pattern.

3.4 Secrets

Never in code/git (pre-commit hooks + scanning; if leaked, rotate first, purge second); scoped per environment, least privilege, short TTLs; redacted in logs/errors; CI secrets masked and unavailable to fork PRs; never open .env or a password manager on screen-share.

Why rotate first: a leaked key's blast radius only grows in a history you can't fully purge — rewriting history is cleanup, rotation is the fix. Hooks and scanners detect, not prevent; assume anything ever committed is public.

3.5 PII in logs

"A log store is a data store: minimise by default (hashes/ids, not raw text), cap and truncate, redact at the logging boundary, enforce retention with a monitored deletion job, and RBAC readers. Audit trails are append-only." At Walmart scale — customer, payment, associate data — defaults must be conservative; LLM/agent logs are a new leak surface.


4. Live-Demo Checklist (screen-share)

# Item
1 Pin 3 repos you'll defend; archive the rest; each README has architecture, run steps, tests, limitations, 2-min demo path
2 Clean-clone test in a fresh dir; follow your own README literally; fix every gap
3 Secret scan: git log -p | grep -iE 'key\|token\|secret\|password'; always run gitleaks/trufflehog even if grep is clean (grep misses base64/hex)
4 Fallbacks: 90-sec recording (tested end-to-end: audio + share permissions), screenshots, architecture PDF, local build
5 Rehearse 10/5/2-min scripts out loud, timed; run an interruption drill ("why that choice?") and recover without restarting
6 Numbers sheet: every resume metric + source (dashboard/analytics/PR); attacker-view sheet: one line per project
7 Screen hygiene: notifications off, personal tabs closed, no .env/password manager/autocomplete, readable zoom
8 Join 5 min early; test A/V + share; phone fallback; pre-open repo file, terminal dir, running services
9 Show code only to prove a decision (a diff, a test, 20 lines); close with one honest limitation before they find it

5. Revision Table

Bullet Hook (first clause) Number Hardest follow-up Attacker view Rebuild
Siege — channels/graph "Single-prompt testing is obsolete; attacks chain" 4 channels, N runs Draw the executor Steer the chain; own the proxy Signed definitions + budgets
Siege — techniques/rules "Finding → draft rule, not a PDF" 19 techniques, N drafts What makes a good rule? Rule-syntax injection Templating + FP loop
Siege — cross-tenant "Registry keyed by pool, not user" 1 bug, N tests, 0 exploitation Explain like a PM Blank/unicode uid; stale reuse Signed tokens + authz at use
Aidan "A bad answer looked like a good answer" Time-to-diagnose down (qualitative unless you instrument MTTR) PII in logs? Log injection; admin IDOR Redaction + RBAC + monitored deletion
Xlancr "Recomputing identical expensive responses" ~50%, 70% "~50% measured how?" Unkeyed input → poisoning Key builder + single-flight
PeerWatch "Video served P2P, not re-streamed" 10 types, 512KB NAT traversal? Plaintext MITM; wrong-but-valid chunks TLS/Noise + signed manifest + relay
Coding platform "API enqueues; HTTP never blocks" 600+ users Fork bomb? Queue starvation; SSRF; forged verdict Hostile-submission CI suite
Gravitas — hold "Check-then-act oversell → one atomic hold" 3M+, ₹50L+, zero downtime Why ZSET? Hoarding; bypass at payment Caps + server re-check + race test
Gravitas — leak "Error paths drained the pool" Leaks on error paths → 0, tickets −95% How did it ship? Error-path flood = pool-exhaustion DoS defer + CI lint + saturation alert
CookOff "High-spike judging, 5-person team" 15k, 200/min, <10s p95? Hardest bug? Duplicates; IDOR; forged writes Quotas + dedupe + server-only writes
CTF "Static then GDB; RE is triage in miniature" 2nd place, N challenges Walk one challenge (this IS the attacker view) Scripted tooling + writeups
Skills Depth line + boundary per tool — Rate yourself with an example — Name the nearest transferable skill

Source rule: every number in this table needs a source tag in prep (dashboard, PR, analytics export, or estimate — never state an estimate as measured). Dates (resume): QNU 09/2026–Present; Aidan 08–09/2026; Xlancr 09/2024–02/2025; PeerWatch 08/2026; Coding Platform 09/2024; Gravitas 09/2025; CookOff 09/2024.


6. Mistakes That Kill This Round

Mistake Sounds like Fix
Vagueness "We improved performance" Baseline, delta, method, window
No numbers "A lot of users", "much faster" One number per bullet + source; label estimates
Bluffing / overclaiming "I built the Judge0 sandbox" "I integrated and verified it; I built the pipeline"; "haven't done X, nearest is Y"
Badmouthing the team "Other devs didn't test anything" "The failure path wasn't owned, so I added the check"
Blame-shifting "My manager rushed it" Own your part; describe the constraint like an engineer
Monologue 5 uninterrupted minutes 20-sec hook, stop, let them steer
Confidentiality leak Customer names, keys, unrepatched bugs Discretion clause; architecture/technique classes only
No attacker view / no limitation Happy-path only; "basically production-ready" One line per project: "how I'd break it"; volunteer one gap first
Demo improvising Searching folders on screen-share Pre-open artifacts + fallback recording + timed rehearsal
Arguing Defending against pushback "Fair point — the tradeoff I accepted was X; I'd change Y"

Final drill: for three bullets — cross-tenant fix, seat-hold race, caching — deliver the 6-beat answer in ≤90 seconds, then have someone interrupt with a random §2 follow-up. Three down, twelve mastered.

Saved in this browser only.