Walmart prep Campus Grad Cybersecurity
Core security8,217 words, about 41 min

Security design and automation

Walmart Campus Grad — Cybersecurity Intern Prep (Topic 6 of series) JD anchors: "Design and develop security solution for Security engineering team", "Design and test automation scripts and tools for security monitoring." Resume anchors: Siege attack-graph executor + 19 attack techniques → detection-rule drafts; Xlancr 70% coverage gate; Aidan observability logging.


1. Why Walmart Asks This

Walmart Global Tech is a software company wearing a retailer's apron: ~10,500 stores, 240M weekly customers, Black Friday traffic at 20–50× baseline. Security Engineering ships products (WAF rule packs, secrets platform, SIEM pipelines, SOAR playbooks), not tickets.

JD phrase What it means What's tested
"Design and develop security solution" Architect network zones, IAM, secrets, CI controls — and defend tradeoffs Layered defense-in-depth + constraints (latency, HA, cost)
"Design and test automation scripts and tools for security monitoring" Python/Bash that ingests, parses, enriches, alerts — and tests so it doesn't page humans for junk Production-grade automation: errors, secrets hygiene, dry-run, tests

Loop: recruiter screen → verbal technical/hiring-manager round (project deep-dive + design scenarios) → behavioral. Interviewers reward structure and metrics. Resume probes to expect: Siege's detection drafts → "finding → tested rule?"; Xlancr coverage → "what should block a merge?"; Aidan logging → "how do you know your pipeline went blind?"

How to use this chapter: each concept below follows the same study shape. WHAT names the control or pattern in one line. WHY explains what a Walmart interviewer hears when you state it — that you can reason about availability, latency, and cost at store scale, not just list tools. HOW walks through the request or event path hop by hop. The concrete example gives you a tradeoff you can say aloud. The interview sketch gives you a two-to-four-line model answer to rehearse.


2. Core Concepts + Examples

2.1 Secure 3-tier web app (edge → app → data)

Internet (TLS 1.2+/1.3)
  ▼
[DNS/CDN] → [WAF: SQLi/XSS/bot/rate] → [ALB in PUBLIC subnets, multi-AZ]
                                         │ SG: 443 from 0.0.0.0/0 only at ALB
                                         ▼
                        [App tier, PRIVATE subnets] SG: app port from ALB SG only
                        no public IP; egress via NAT with allowlist
                                         │ SG: 5432 from App SG only
                                         ▼
                        [DB, ISOLATED subnets] no IGW/NAT route, KMS at rest, TLS in transit

Admin: SSM Session Manager (no inbound port, identity-based, session-logged) — preferred
       Bastion in public subnet, key-only, allowlisted CIDR + MFA — fallback
Logs:  WAF + ALB access + VPC Flow + CloudTrail + OS/app → immutable S3 + SIEM
Layer Control Why it's liked
Edge CDN + WAF + L7 ALB Volumetric/L7 attacks absorbed before compute; TLS terminates centrally
Network Public LB / private app / isolated DB subnets, per AZ Route tables decide reachability — compromise can't hop tiers even if an SG is wrong
Firewall SG-to-SG references (stateful); NACLs stateless, coarse CIDR rules trust a subnet; SG references trust workload identity
Admin SSM/Bastion Removes always-on internet SSH; identity-based revocation + session logs
Data Isolation, KMS, TLS, least-privilege DB user Small blast radius from app tier
Detection Central logging from every layer "You can't detect what you don't collect" — say it unprompted

Tradeoffs: one NAT per AZ (shared NAT = cross-AZ SPOF, costs less); WAF starts in count mode, then blocks; NACLs can stay permissive if SGs are primary. K8s variant: private node groups, workload identity, NetworkPolicies, admission control (OPA/Kyverno) blocking privileged pods.

WHAT it is: a three-zone layout where routing, not just firewall rules, enforces separation. Public subnets have a route to the internet gateway. Private subnets have a route to NAT only. Isolated subnets have no internet route at all. Security groups then narrow who can talk within what routing already allows.

WHY it matters at Walmart scale: checkout and payment-adjacent services must survive a compromised web host and a Black Friday surge at the same time. If the app tier is breached, the attacker should still have no network path to the database except the single app-to-DB port, and no path out except a logged NAT allowlist. Interviewers listen for that blast-radius argument plus the cost and latency cost of doing it per AZ.

HOW a request flows: the client negotiates TLS 1.2 or 1.3 at the CDN or ALB so certificates live in one place. The WAF inspects the decrypted request for SQL injection, cross-site scripting, bot signatures, and rate abuse. The ALB forwards only healthy targets in private subnets. The app opens one pooled connection to the isolated database over TLS with a least-privilege user. The response returns the same way. Every hop emits a log — WAF, ALB access, VPC Flow, CloudTrail, OS and app — to an immutable bucket that feeds the SIEM.

Concrete tradeoff case study: a team proposes one shared NAT to save cost. You counter that a single-AZ NAT failure or saturation takes down egress for all AZs, including payment retries during peak. You keep one NAT per AZ and show the math: three NAT-hours cost more per month but remove a cross-AZ single point of failure. Similarly you run a new WAF rule in count mode for three days, compare counts against order-success rate, then switch to block only when false positives are near zero.

Likely interview question: "Walk me through securing a checkout service on AWS." Model sketch: draw the three subnet tiers, state the SG-to-SG rule at each boundary, name SSM over SSH for admin, name the five log sources and where they land, then close with two tradeoffs you chose and why.

2.2 Monitoring N hosts: agents → pipeline → detection → SOAR

[N hosts] agents: Splunk UF / Elastic Agent / Fluent Bit / Sysmon / auditd / cloud connectors
  ▼ disk-buffered, TLS, compressed
[Aggregation] → [Ingest: parse → normalize CIM/ECS/OCSF → enrich asset owner/geo/TI → index]
  ▼
[Detection: Sigma / SPL / KQL / correlation / ML jobs]
  ▼
[Alerting: severity, dedupe, suppression windows, tickets]
  ▼
[SOAR: enrich → decide → act (isolate, disable session, block IP) — audited + rollback]
  ▼
[Feedback: FP tuning, rule versioning, coverage metrics, agent-health alarms]

Sizing math to say aloud: 10,000 hosts × 5 EPS × ~1 KB ≈ 50 MB/s ≈ 4 TB/day raw (~2 TB/day indexed after ~50% compression); 90-day hot is hundreds of TB — so filter at the agent, tier hot/warm/cold. Sanity-check orders of magnitude aloud (EPS × 86,400 × bytes) before quoting a number. Agent health is a detection: alert on missing heartbeats, ingest lag, parser failures, because a silent agent is blind. Normalize fields early or every rule becomes vendor-specific. ELK equivalent: the same pipeline with Elastic Agent → Logstash/Ingest → ECS fields → EQL/KQL detection (e.g. process where process.name : "certutil.exe" and process.args : "*exportPFX*") — ECS is to ELK what CIM is to Splunk. SOAR actions: dry-run first, allowlist targets, audit, and rollback — irreversible automation is an outage waiting to happen.

WHAT it is: a telemetry supply chain. Agents collect and buffer locally. An ingest tier parses, normalizes to a common schema such as CIM, ECS, or OCSF, enriches with asset and threat data, and indexes. Detection, alerting, and response sit downstream and depend on each upstream contract holding.

WHY it matters for a security intern: at 10,000 hosts you cannot add headcount to read more logs. You have to cut volume without cutting signal, and you have to prove the pipe itself is healthy. Interns who say "alert when the agent stops sending" stand out because they show they have operated something, not just written a rule.

HOW it works end to end: Sysmon or auditd records a process event with command line. The forwarder compresses, encrypts with TLS, and spools to disk if the network drops. The ingest tier groks the timestamp, maps Image and CommandLine to standard fields, adds store ID, owner, and threat-intel verdict, then indexes. A Sigma-derived rule fires, dedupe groups ten identical host alerts into one ticket, and a SOAR playbook enriches the hash, checks allowlists, isolates the host behind approval, and writes every step to an audit index. A feedback job tracks precision and agent coverage weekly.

Concrete sizing example: start from 5 events per second per host. Multiply 10,000 × 5 × 86,400 seconds × 1,000 bytes to get about 4.3 TB per day raw. Halve it for compression to about 2 TB indexed per day. Ninety days of hot retention is then about 180 TB before replication. That is why you drop debug logs at the agent, keep only security outcomes in hot, and move the rest to warm or cold object storage. Say that arithmetic aloud in the interview before giving a number.

Likely interview question: "How would you know your monitoring went blind?" Model sketch: name three alarms — heartbeat gap by host group, ingest lag over ten minutes, parser-drop rate spike — and state where each pages and what runbook it triggers.

2.3 Secure API design

Control Practice
Transport TLS everywhere, HSTS, no secrets in URLs (they land in logs)
AuthN OAuth2/OIDC for users, short JWT (15 min) + revocable refresh; mTLS/API keys for services (headers, stored hashed)
AuthZ Deny by default, object-level checks every request (kills IDOR/BOLA, OWASP API #1); coarse at gateway, fine in service
Validation JSON Schema/Pydantic per field; allowlist types/lengths/enums; parameterized queries
Rate limiting Token bucket/sliding window per key and per-IP fallback; 429 + Retry-After + X-RateLimit-*
Audit who/what/when/where/result/request-id; never tokens or card data; immutable sink
Versioning /v1/, additive changes, Deprecation/Sunset headers, usage tracking
Writes Idempotency keys (POST), ETag + If-Match (optimistic concurrency)
Errors RFC 9457 (formerly RFC 7807) application/problem+json; no stack traces/hostnames

WHAT it is: a contract that assumes the network is hostile and clients retry. Every request carries identity, every object access is checked against that identity, every input is validated against a schema, and every response tells the caller how to back off without leaking internals.

WHY it matters at Walmart scale: an inventory or order API serves stores, partners, and mobile clients that share NATs and retry aggressively on timeouts. Without object-level checks a valid token for one store can read another store's data. Without idempotency, one retried payment creates two charges. Interviewers test whether you separate authentication (who is this) from authorization (can they touch this object).

HOW a call is handled: TLS terminates and HSTS forces HTTPS. The gateway validates the JWT or mTLS certificate, enforces coarse scope, and applies a per-key token-bucket limit with a per-IP fallback. The service loads the store object, compares its ID to the caller's authorized scope, and denies by default. Pydantic or JSON Schema rejects over-long or wrong-type fields. The handler uses an idempotency key for writes and ETag checks for concurrent updates. It writes a structured audit record with request ID and result, and returns a typed application/problem+json error on failure.

Concrete tradeoff case study: a partner asks for a higher rate limit for a flash sale. You keep the per-IP guard to protect shared infrastructure but raise the per-key quota for that partner with an expiry, return X-RateLimit-Remaining and Retry-After, and alert on 429 spikes that suggest scraping. You also cap page size so one caller cannot force a full-table scan.

Likely interview question: "How do you stop one retailer tenant from reading another's orders?" Model sketch: check the object owner on every request, not just at login; test with a cross-tenant negative test in CI; log the decision with request ID so a bypass is auditable.

2.4 Secrets management

Rule zero: a secret in git is compromised the moment it's committed — rotate it; deleting the file is not remediation.

Concern Answer
Storage Vault KV v2 / cloud secret manager, KMS-encrypted; never in code, images, committed config
Access Workload identity (K8s projected SA → Vault k8s auth; cloud instance role; OIDC) — no static token in config
Rotation Prefer dynamic secrets (Vault DB engine mints an hour-TTL user, DROP USER on expiry); static secrets rotate on schedule and on exposure
Scoping Per-env namespaces (secret/prod vs secret/dev); prod unreadable from dev; least-privilege path policies
Secret zero Bootstrap credential solved by platform attestation (K8s SA, cloud IAM, GitHub OIDC) — never a token planted in the AMI/Jenkins store
Leakage Pre-commit gitleaks, GitHub secret scanning, scrub env dumps, prefer mounted files over env vars (env leaks to child processes/dumps)
Audit Every read/write logged; break-glass is time-boxed, dual-control, alerted; revoke root token after setup

WHAT it is: a system where code never sees a long-lived secret directly. Secrets live in Vault or a cloud manager encrypted by KMS. Workloads authenticate with platform identity — Kubernetes service account, cloud instance role, or CI OIDC — and receive short-lived credentials at runtime.

WHY it matters for automation at Walmart scale: forty repos and hundreds of pipelines copy-paste credentials faster than humans can rotate them. One leaked deploy token can rewrite detection rules or push to production. Short-lived dynamic secrets shrink that window from months to minutes and make every read auditable.

HOW rotation works: for databases, the Vault DB engine creates a temporary user with a one-hour TTL and drops it on expiry, so there is nothing to rotate on a calendar. For static API keys, a scheduler rotates on a fixed interval and immediately on any exposure signal. The app reads the secret from a mounted file or memory at startup, never from a committed config. Every read is logged with workload identity and time.

Concrete example: gitleaks flags an AWS key in a PR diff. You treat the history as compromised, revoke the key first, check CloudTrail for use in the exposure window, issue a workload-scoped replacement via OIDC, then add the pre-commit hook so the next commit is blocked locally. Deleting the file alone would leave clones and CI caches with a live key.

Likely interview question: "What is secret zero and how do you fix it in CI?" Model sketch: name the bootstrap problem, solve it with GitHub OIDC to Vault or cloud IAM with bound claims on repo and environment, and state that no static token lives in CI settings.

2.5 Shift-left: SAST/DAST in CI + dependency scan + coverage gates

pre-commit   → gitleaks, lint
PR (always)  → SAST (Semgrep/CodeQL) · SCA (Dependabot/Snyk/Trivy) · IaC (Checkov) · tests + coverage gate
build        → container scan (Trivy) · SBOM (Syft) · sign (Cosign)
staging      → authenticated DAST (OWASP ZAP baseline)
production   → runtime telemetry, WAF, alerting
Tool Finds Blind spot
SAST Source-to-sink flows (SQLi/XSS), hardcoded secrets Logic/authz flaws, runtime config
DAST Runtime behavior: headers, redirects, reachable inputs Can't see code path; needs auth/state coverage
SCA/dep Known CVEs in OSS, licenses Zero-days, logic flaws
Container/IaC Base-image CVEs, misconfigured infra App logic
Manual + negative tests Cross-tenant IDOR, business abuse Doesn't scale as a gate

Gates: block the PR on new verified High/Critical with no approved disposition, a failed cross-tenant negative test, or an incomplete scan (green ≠ scanned). Don't block on the legacy backlog. Exceptions carry finding ID, reason, compensating control, owner, and expiry; expired exceptions re-enter triage. Coverage (Xlancr hook): pytest --cov=app --cov-fail-under=70 as a required branch-protection check. Keep DAST in warn mode initially (higher FP rate), and never let path filters skip secret/IaC jobs.

WHAT it is: shift-left means security checks move to the pull request where the author is still present, not to a quarterly scan nobody owns. SAST reads code, SCA reads dependencies, IaC scanning reads Terraform, container scanning reads images, and DAST exercises the running app in staging.

WHY it matters for building solutions: Walmart-scale teams merge hundreds of PRs per day. A gate that blocks legacy issues trains engineers to bypass it. A gate that blocks only new verified High or Critical findings with a clear exception path gets kept. Interns are expected to design that gate, not just run the scanner.

HOW a PR flows: pre-commit runs gitleaks and lint in seconds. The PR pipeline fans out to Semgrep or CodeQL, Dependabot or Snyk, Checkov, and unit tests with a 70% coverage floor. The build stage adds Trivy, an SBOM via Syft, and Cosign signing. Staging runs an authenticated OWASP ZAP baseline against seeded accounts. Each tool posts a typed finding with file, line, and CWE so the author can fix without leaving the PR.

Concrete tradeoff case study: DAST flags missing HSTS on every build because staging sits behind a different proxy than production. You do not mute the rule family. You fix staging fidelity so it mirrors prod headers, keep DAST in warn mode while precision is low, and run the active scan nightly instead of per-PR to control time and noise.

Likely interview question: "What blocks a merge in your pipeline?" Model sketch: new verified High/Critical, failed cross-tenant test, or incomplete scan; legacy backlog is tracked separately; every exception has owner and expiry.

2.6 Python for security automation

Library Use Gotcha
re Parse lines, extract IPs/URLs Precompile; unbounded regex on huge lines = ReDoS
requests SIEM/EDR APIs Always timeout=, verify=True; tokens from env/Vault; 429 backoff
socket Port scan/banner grab connect_ex returns errno, doesn't raise; settimeout or hang on filtered ports
subprocess Wrap CLI tools shell=False + list args (no injection), timeout=, check returncode
hashlib + hmac FIM/hash verify Stream 64 KB chunks for GB files; compare_digest for constant-time compare
scapy Packet craft/sniff/pcap Needs raw sockets; keep it defensive in interviews
argparse Typed CLI Default --dry-run; validate paths (no traversal)

Stream with generators (yield) for multi-GB files; Counter for top-N; logging + RotatingFileHandler; log who/what/when + counts — auditability is a feature. scapy in one line (defensive only, needs raw sockets/root): sniff(filter="tcp port 443", count=10) to observe, IP(dst="10.0.0.1")/ICMP() to craft in a lab — never scan outside scope.

WHAT it is: a small set of standard-library plus requests patterns that cover most SOC automation: parse text, call an API safely, probe a socket with a timeout, wrap a CLI without shell injection, hash a file without loading it, and expose typed flags with a safe default.

WHY it matters for monitoring automation: a script that pages at 2 a.m. must fail loudly, not silently, and must never become the incident. Timeouts, dry-run defaults, and structured logs are what separate an intern script from a production tool that a Walmart SOC will actually schedule.

HOW a safe script is shaped: argparse defines typed inputs and a --dry-run flag that defaults on for any state change. File input is streamed line by line with precompiled regex and errors="replace" so one bad byte does not kill the run. API calls use timeout, verify=True, tokens from the environment, and backoff on 429. Subprocesses use shell=False with a list of args and a timeout, and return codes are checked. Output is stable JSON plus a count log line so tests and cron can assert on it.

Concrete example: the auth.log parser in §4.1 reads a 5 GB file with flat memory, counts failures per IP with Counter, flags only IPs over --threshold, prints JSON for the ticket, and returns exit 1 when something is flagged so cron or SOAR can alert. A golden-fixture test feeds it normal, empty, and malformed lines to lock that behavior.

Likely interview question: "What makes a Python SOC script production-grade?" Model sketch: typed CLI, path validation, dry-run, no hardcoded secrets, timeouts everywhere, audit logging, fixture tests, and caps so it cannot hammer the vendor.

2.7 Bash: grep / awk / sed + cron

Task One-liner
Top 10 IPs awk '{print $1}' access.log \| sort \| uniq -c \| sort -rn \| head -10
Top failed-SSH IPs grep "Failed password" /var/log/auth.log \| awk '{print $(NF-3)}' \| sort \| uniq -c \| sort -rn \| head -20
Count 5xx (backend status, field 9) awk '$9 >= 500 && $9 < 600' elb.log \| wc -l
Extract all IPs grep -oE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' syslog \| sort \| uniq -c \| sort -rn
p99 latency (JSON) jq -n '[inputs.latency_ms] \| sort \| .[((length*0.99)\|ceil)-1]' app.log
Rewrite a field sed -E 's/user=([a-z0-9]+)/user=\1@corp/'

Cron discipline: set -euo pipefail; flock -n /var/lock/scan.lock (no overlapping runs); rotating logs; non-zero exit on failure so monitoring sees it; dry-run default for state changes; parameterize thresholds. One-pass awk beats grep|sort|uniq on huge files (memory = distinct keys), and sort -rn needs -n — the most common one-liner fail.

WHAT it is: Bash as a first-pass triage layer. grep filters, awk selects and compares fields, sort plus uniq -c ranks, sed rewrites, and jq handles JSON logs. Cron turns a proven one-liner into a scheduled check with locking and logging.

WHY it matters: during an incident you need the top talker or the 5xx spike in seconds, before a notebook or SIEM job finishes. Interviewers use the one-liner to test whether you know field positions, numeric sort, and that uniq only dedupes sorted input.

HOW a safe cron job runs: the script starts with set -euo pipefail so any failure stops the run, takes flock -n so a slow run does not stack, uses absolute paths so cron's minimal PATH cannot pick the wrong binary, appends to a rotated log, and exits non-zero on failure so the scheduler pages. A blocking action such as a firewall drop stays behind --dry-run and an allowlist until precision is proven.

Concrete run: against a 2 GB ELB log, awk '$9 >= 500 && $9 < 600' elb.log | wc -l counts backend 5xx by testing field 9 numerically, while the failed-SSH pipeline extracts the IP field, sorts, counts, reverse-numeric sorts, and keeps the top ten. On a hundred-million-line file you would rewrite the second as single-pass awk '{c[$11]++} END {for (k in c) print c[k],k}' to avoid three extra passes.

Likely interview question: "Top ten failed-SSH IPs, and how would you schedule it?" Model sketch: give the grep | awk | sort | uniq -c | sort -rn | head chain, explain the field and the -n flag, then add flock, pipefail, and exit-code alerting.

2.8 Detection-as-code: Sigma/SPL + FP testing

title: Suspicious Certificate Export with Certutil
id: 8b0e7b6e-3c1a-4c9a-9f0e-6f2c4a1d5e77
status: test
description: Detects certutil exporting a PFX, a common credential-theft staging step.
references: [https://attack.mitre.org/techniques/T1552/004/]
author: Vaibhav Sijaria
date: 2026-10-13
tags: [attack.credential_access, attack.t1552.004]
logsource:
  category: process_creation
  product: windows
detection:
  selection_img:
    Image|endswith: '\certutil.exe'
  selection_args:
    CommandLine|contains|all: ['-exportPFX', '-p ']
  condition: selection_img and selection_args
falsepositives:
  - Legitimately packaged certificates by IT provisioning
  - Backup agents that export PFX files
level: high

Never hand-edit rules in the SIEM UI (console drift is invisible). Convert in CI:

sigma convert -t splunk -p sysmon rules/credential_access/export_pfx.yml
# → index=windows source=XmlWinEventLog:Microsoft-Windows-Sysmon/Operational
#   EventCode=1 Image="*\\certutil.exe" CommandLine=("*\\-exportPFX*" OR "*\\-p *")

FP testing is the differentiator. Every rule ships a malicious fixture that must match and a benign near-miss that must not; add boundary cases (exactly at threshold) and missing-field cases (parent process absent). Run in shadow mode ~7 days; promote only if precision ≥80–90% after triage, volume is stable, and there's no unexplained drift. FPs get the smallest reliable exception (hash + signer + path + host group + expiry), never a global mute — fix logic where possible. Map to ATT&CK and state blind spots plainly.

WHAT it is: detection-as-code treats a Sigma rule like application code. The YAML in Git is the source of truth. CI validates schema, tests fixtures, and compiles to each SIEM backend such as Splunk SPL or Elastic KQL. No one edits the rule in the UI.

WHY it matters for Walmart-scale monitoring: hundreds of rules across Splunk, Elastic, and cloud SIEMs drift quickly if analysts click-edit in the console. Code review, version history, and fixture tests are how a small security-engineering team keeps precision high and proves coverage to auditors.

HOW the certutil rule works: selection_img matches any process image ending in \certutil.exe to catch renamed paths. selection_args requires both -exportPFX and -p in the command line, which narrows it to an export with a password rather than any certutil use. The condition ANDs both selections. The falsepositives list documents the two known benign sources — IT provisioning and backup agents — so tuning starts from evidence, not guesses.

Concrete test run: the malicious fixture contains Image: C:\Windows\System32\certutil.exe and CommandLine: certutil -exportPFX -p S3cret out.pfx, which must match. The benign fixture contains an IT provisioning user exporting with a corporate signer and path, which must not match. CI runs sigma check, both fixture assertions, and a compile to SPL. On merge the rule deploys in shadow mode that logs only, and after about seven days with precision above 80% and flat volume it is promoted to alert.

Likely interview question: "How do you ship a new detection without spamming the SOC?" Model sketch: fixtures first, shadow for a week, promote on precision and volume gates, and scope exceptions by hash plus path plus host group with expiry.

2.9 Retail-scale thinking

Domain What breaks Engineering/security angle
Inventory truth Physical on-hand drifts (theft, misplacement) Available-to-sell = on-hand − reserved − safety buffer; reconciliation tightens buffers
Cart/checkout Last unit wanted online and in-aisle simultaneously Atomic conditional decrement (WHERE sellable > 0) or Redis DECR; idempotency keys; waiting-room queue
Hot SKUs Single-row contention on a doorbuster Shard per item or token-allocate a fixed sellable count
Low-stock alerts Polling every SKU is wasteful and noisy Event-driven threshold crossing + hysteresis (alert ≤5, clear ≥10) + dedupe by (store, SKU)
ETA/fulfillment Promising a date a node can't meet Node selection by distance + stock + labor; degrade to "estimate unavailable"
Search ranking Showing unpurchasable items Availability/price/fulfillment as features with bounded staleness (<60 s local); browse may be eventual, checkout must be correct
Abuse Price-scraping bots, inventory-API abuse Per-key/IP limits, edge bot management, fraud signals on checkout, authz on store-scoped endpoints

One-liner to remember: search and browse must stay up; checkout must stay correct. WHAT it means: browse can serve cached stock with bounded staleness under 60 seconds, while checkout must use atomic truth — on-hand minus reserved minus buffer — so the last unit cannot sell twice. WHY it matters: doorbuster contention and bot hoarding turn a correctness bug into oversell and refunds. HOW it works: first shopper gets a short TTL hold via conditional decrement WHERE sellable>0, second sees out-of-stock, expired holds release, and low-stock alerts fire once per (store, SKU) with hysteresis. Likely Q: checkout saw stale stock? Sketch: keep browse eventual, make checkout conditional, add idempotency and correction events.

2.10 Case study: phishing-reporting pipeline (idea to deployed rule)

WHAT it is: report button → mailbox → parser extracts URLs and attachments → §4.6 lexical features score entropy, punycode, IP-host, brand signals → SIEM rule update via PR. WHY it matters: thousands of weekly reports cannot be triaged by hand, and a noisy auto-block teaches analysts to ignore the queue. HOW it runs: parser normalizes URLs, enriches against allowlisted senders, fires only on high-precision combos such as punycode plus brand string, tests a malicious fixture that must match and a newsletter fixture that must not, ships in shadow mode seven days, then promotes to block with 30-day expiry and audit log.


3. Q&A (17)

Q1. Design a secure 3-tier web app on AWS. Internet → CDN/WAF → ALB public subnets → app private subnets (SG allows only ALB's SG) → DB isolated subnets (only app SG, no internet route). Admin via SSM, not public SSH. Central logs: WAF, ALB access, VPC Flow, CloudTrail, app/OS → immutable bucket + SIEM. TLS in transit, KMS at rest. Justify each hop as a trust boundary; name tradeoffs (NAT per AZ, WAF count mode first, multi-AZ cost).

Q2. Monitor 10,000 hosts end-to-end? Agents with disk buffering → TLS aggregation → parse/normalize (CIM/ECS) + enrich → detection (Sigma/SPL/correlation) → dedupe + alerting → SOAR enrichment/containment. Then monitor the monitor: heartbeat, ingest-lag, parser-failure alarms. Estimate ~50 GB/day at 5 EPS/host, tier retention, filter noise at the agent.

Q3. What do you log and how do you control SIEM cost? High-signal security events: authn/authz outcomes, privilege/admin changes, process command lines (Sysmon 1), network denies, WAF/API abuse, secret access, data exports. Drop debug/health noise at source, use structured JSON, metrics/summaries for high-volume patterns, hot/warm/cold tiering.

Q4. Design a secure inventory-lookup API. TLS; OAuth2/OIDC for users, mTLS/API key for partners; object-level authz verifying the store ID against the caller's scope (BOLA). Schema-validate store/SKU/limit, cap page size; rate limit per key and IP with 429 + Retry-After; structured audit logs with request IDs, no PII in URLs; /v1/ versioning with Sunset headers; RFC 7807 errors without internals.

Q5. Rate limit fairly? Token bucket/sliding window (fixed window bursts 2× across its boundary). Layer: coarse per-IP for unauthenticated traffic, finer per-key/user behind auth (fair to shared corporate NATs). Return limit/remaining/reset + Retry-After so good clients self-throttle. Internal service-to-service: circuit breakers + per-consumer quotas.

Q6. Remove hardcoded credentials from 40 repos? Detect (gitleaks/trufflehog history + GitHub scanning) → rotate everything found first (assume compromise; history is forever) → centralize in Vault/Secrets Manager with workload identity → migrate code to runtime injection → add pre-commit hooks + CI scanning + branch protection → alarm on secret reads. Order matters: rotate before rewriting history, because clones and CI caches still hold the old value.

Q7. What is secret zero, and how do you solve it in CI? The bootstrap credential needed to fetch other secrets. Solve with platform attestation: GitHub OIDC → cloud IAM/Vault JWT auth with bound_claims on repo, branch, and environment, issuing minutes-long tokens. Nothing long-lived sits in CI settings; forks/feature branches can't assume prod roles. Never store a static Vault token in Jenkins credentials.

Q8. Where do SAST/DAST/SCA sit, and what blocks a merge? Secrets pre-commit; SAST + SCA + IaC + tests/coverage on every PR; container scan + SBOM + signing at build; authenticated DAST in staging. Block on new verified High/Critical without an approved expiring exception, failed cross-tenant test, or incomplete scan. Legacy findings go to a tracked backlog. My Xlancr project enforced a 70% coverage gate as a required check.

Q9. DAST fails every build on missing headers. What now? Triage before tuning: verify production-equivalent proxy/CDN behavior — if prod adds HSTS/CSP, fix staging fidelity instead of waiving. Fix real gaps centrally. Reduce noise correctly: passive baseline in PR, active scan nightly, seeded authenticated accounts, warn mode with a tracked count until precision is credible. Never suppress whole rule families — that hides the finding when it reappears on a new endpoint.

Q10. Write a brute-force login detection and test its FPs. Count failed authn per source IP and per username in 5-minute windows; threshold the IP (≥20) plus a spray signal (many usernames, few attempts each). Flag success-after-failures as high severity. FP tests: corporate NAT, retrying mobile client, password-expiry sweep, authorized pentest. Shadow mode, baseline volume, tune thresholds, scoped expiring exceptions only.

Q11. Finding → Sigma rule → Splunk? Start from a threat hypothesis + telemetry contract (field, source, latency). Write Sigma with logsource/selection/condition/falsepositives; convert via sigma convert -t splunk -p sysmon; store in Git. CI runs sigma check, malicious/benign fixture tests, and conversions for each backend. Deploy via audited idempotent API, run in shadow/notify mode, enable alerting when precision/volume gates pass. This mirrors Siege: findings generated draft detections, which then need fixtures and tuning to be trustworthy.

Q12. Python script for a recurring SOC task — and make it safe. A streaming log parser/enricher: line-by-line regex extraction, Counter top-N, optional enrichment against an allowlist/approved API (timeout, verify=True, token from env, cache + 429 backoff), ticket-ready summary. Safety: argparse typed args + path validation, --dry-run default for state changes, secrets from Vault/env, structured audit logging, golden-fixture tests (normal/empty/malformed), and caps/stop conditions so it can't hammer a vendor.

Q13. connect_ex over connect? Threads or asyncio? connect_ex returns an errno (0 = open) instead of raising — cleaner and faster across thousands of ports, but only with settimeout or filtered ports hang. Scanning is I/O-bound: threads work despite the GIL (released during socket I/O); asyncio scales to thousands of connections with less overhead. Bound concurrency with jitter — an unbounded scanner is a DoS.

Q14. Bash: top 10 failed-SSH IPs, and cron it safely. grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -10. Cron: set -euo pipefail, flock -n to prevent overlap, absolute paths, rotating logs, non-zero exit on failure. If it acts (firewall block), dry-run default, allowlist, audit trail.

Q15. How does your Siege/Aidan work apply here? Aidan: request logging, failed-run admin views, retention — the same discipline as SIEM engineering: instrument the pipeline so "why did this fail?" is answerable, and know when telemetry stops. Siege: attack-graph executor + 19 techniques → detection-rule drafts; the lesson is that offensive work only counts when the loop closes — every technique needs a detection, every detection needs a benign fixture. I also fixed a cross-tenant session-takeover flaw with fail-closed UID resolution + regression tests: object-level authz thinking applied to a registry.

Q16. Cart item goes out of stock during checkout — design and secure it. Reserve at checkout start with a short TTL hold + atomic conditional decrement so two shoppers can't claim the last unit; expire holds to release abandoned carts. Idempotency keys because clients retry. On pick failure: substitution/refund + inventory-correction event that tightens the SKU buffer. Security: reservation APIs attract inventory-hoarding bots — auth, rate limits, fraud signals, audit every hold/release. Browse may show stale availability; checkout must never oversell silently.

Q17. A security-tool API key leaked in a public repo — response? Treat as compromised: revoke/rotate immediately, then review audit logs for unexpected use. Scope the blast radius (a read-only scan key ≠ a deploy token that can edit detection rules). Check for other exposed secrets and history, add pre-commit scanning, write a postmortem with preventive action — then redesign so the credential isn't long-lived: OIDC/short-lived or vault-brokered.


4. Hands-On Code

4.1 Python log parser (streaming, argparse, top-N)

#!/usr/bin/env python3
"""Parse auth.log for failed logins; stream + top-N; exit code drives alerting."""
import argparse, json, re, sys, logging
from collections import Counter

PATTERNS = {
    "ssh_fail": re.compile(r"Failed password for (?:invalid user )?(?P<user>\S+) "
                           r"from (?P<ip>\d{1,3}(?:\.\d{1,3}){3})"),
    "accepted": re.compile(r"Accepted \w+ for (?P<user>\S+) from (?P<ip>\S+)"),
}
log = logging.getLogger("parser")

def parse(path):
    """Generator: one event at a time -> flat memory on multi-GB files."""
    with open(path, encoding="utf-8", errors="replace") as fh:
        for lineno, line in enumerate(fh, 1):
            for name, rx in PATTERNS.items():
                m = rx.search(line)
                if m:
                    yield {"line": lineno, "event": name, **m.groupdict()}
                    break

def main():
    p = argparse.ArgumentParser(description="auth.log failed-login triage")
    p.add_argument("logfile")
    p.add_argument("--top", type=int, default=10)
    p.add_argument("--threshold", type=int, default=20, help="flag IPs above this")
    args = p.parse_args()
    logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")

    fails, users = Counter(), Counter()
    for ev in parse(args.logfile):
        if ev["event"] == "ssh_fail":
            fails[ev["ip"]] += 1
            users[ev["user"]] += 1

    flagged = {ip: c for ip, c in fails.items() if c >= args.threshold}
    print(json.dumps({"total_failed": sum(fails.values()),
                      "unique_ips": len(fails),
                      "top_ips": fails.most_common(args.top),
                      "top_usernames": users.most_common(args.top),
                      "flagged_ips": flagged}, indent=2))
    log.info("processed=%d flagged=%d", sum(fails.values()), len(flagged))
    return 1 if flagged else 0

if __name__ == "__main__":
    sys.exit(main())

Points: generator not .read() so a 5 GB file uses flat memory; precompiled regex plus errors="replace" survives one bad byte; stable JSON stdout is testable and exit code drives cron/SOAR. Logic walk: parse() yields one event, main() counts with Counter, flagged applies threshold, exit 1 signals the scheduler.

4.2 Python TCP port scanner (socket, bounded threads)

#!/usr/bin/env python3
"""Bounded threaded TCP connect scanner. Authorized targets only."""
import argparse, socket, threading
from queue import Queue

def worker(target, timeout, q, out, lock):
    while True:
        try:
            port = q.get_nowait()
        except Exception:
            return
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        try:
            if s.connect_ex((target, port)) == 0:      # errno, no exception
                try:
                    s.sendall(b"\r\n")
                    banner = s.recv(128).decode(errors="replace").strip()
                except OSError:
                    banner = ""
                with lock:
                    out[port] = banner
        finally:
            s.close()
            q.task_done()

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("target")
    ap.add_argument("-p", "--ports", default="22,80,443,3306,3389,5432,8080,8443")
    ap.add_argument("--timeout", type=float, default=1.0)
    ap.add_argument("--workers", type=int, default=100)
    a = ap.parse_args()
    q = Queue()
    for p in (int(x) for x in a.ports.split(",")):
        q.put(p)
    out, lock = {}, threading.Lock()
    for _ in range(min(a.workers, q.qsize())):
        threading.Thread(target=worker, args=(a.target, a.timeout, q, out, lock),
                         daemon=True).start()
    q.join()
    for port in sorted(out):
        print(f"{port}/tcp open  {out[port]}")

if __name__ == "__main__":
    main()

Points: connect_ex + settimeout so filtered ports time out instead of hanging; threads suit I/O-bound scans since the GIL releases during socket I/O; bounded queue plus worker cap prevents self-DoS; scan only in-scope assets. Logic walk: queue holds ports, connect_ex==0 means open, banner grab is best-effort, q.join() waits.

4.3 Hash checker / file-integrity verifier

#!/usr/bin/env python3
import argparse, hashlib, hmac, json, sys
from pathlib import Path

CHUNK = 64 * 1024

def sha256_file(path):
    h = hashlib.sha256()
    with open(path, "rb") as fh:
        for chunk in iter(lambda: fh.read(CHUNK), b""):   # constant memory
            h.update(chunk)
    return h.hexdigest()

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--manifest", required=True, help='JSON {"rel/path": "sha256"}')
    ap.add_argument("--root", default=".")
    args = ap.parse_args()
    manifest = json.loads(Path(args.manifest).read_text())
    bad = 0
    for rel, expected in manifest.items():
        actual = sha256_file(Path(args.root) / rel)
        ok = hmac.compare_digest(actual, expected)       # constant-time
        print(f"{'OK  ' if ok else 'FAIL'} {rel} {actual}")
        bad += not ok
    return 1 if bad else 0

if __name__ == "__main__":
    sys.exit(main())

Points: chunked hashing keeps memory flat for GB files; compare_digest avoids timing leaks; manifest should come from a signed CI artifact and the checker should run from read-only storage. Logic walk: read 64 KB chunks, compare in constant time, print OK/FAIL per file, return non-zero on drift so CI blocks release.

4.4 Splunk API: search jobs + HEC (requests)

#!/usr/bin/env python3
import os, time, requests

SPLUNK    = os.environ["SPLUNK_URL"]          # https://splunk.internal:8089
TOKEN     = os.environ["SPLUNK_TOKEN"]        # from Vault/env, never source
HEC       = os.environ["SPLUNK_HEC_URL"]      # https://...:8088/services/collector
HEC_TOKEN = os.environ["SPLUNK_HEC_TOKEN"]
H = {"Authorization": f"Bearer {TOKEN}"}

def run_search(spl, earliest="-24h", poll=2.0, tries=30):
    if not spl.startswith(("search", "|")):  # REST requires leading 'search'
        spl = "search " + spl
    r = requests.post(f"{SPLUNK}/services/search/v2/jobs", headers=H, verify=True,
                      timeout=30, data={"search": spl, "earliest_time": earliest,
                                        "latest_time": "now", "output_mode": "json"})
    r.raise_for_status()
    sid = r.json()["sid"]
    for _ in range(tries):                                  # poll, don't sleep blind
        st = requests.get(f"{SPLUNK}/services/search/v2/jobs/{sid}", headers=H,
                          verify=True, timeout=30,
                          params={"output_mode": "json"}).json()
        state = st["entry"][0]["content"]["dispatchState"]
        if state == "DONE":
            break
        if state in ("FAILED", "PAUSE", "INTERNAL_CANCEL"):
            raise RuntimeError(f"search {sid} ended in {state}")
        time.sleep(poll)
    else:
        raise TimeoutError(f"{sid} still running")
    res = requests.get(f"{SPLUNK}/services/search/v2/jobs/{sid}/results", headers=H,
                       verify=True, timeout=60,
                       params={"output_mode": "json", "count": 1000}).json()
    return res["results"]

def send_hec(event, sourcetype="automation:audit"):
    requests.post(HEC, verify=True, timeout=10,
                  headers={"Authorization": f"Splunk {HEC_TOKEN}"},
                  json={"event": event, "sourcetype": sourcetype,
                        "index": "sec_eng"}).raise_for_status()

Points: management port 8089 (REST) vs HEC 8088; Bearer vs Splunk auth prefixes; jobs → poll dispatchState → results; always verify=True + timeouts; secrets from env/Vault. Logic walk: create job, poll until DONE or FAILED, fetch paged results, emit audit events to HEC.

4.5 Detection-as-code: fixture tests + CI

# tests/test_detections.py — runs on every PR
import json, subprocess
from pathlib import Path
RULES, FIX = Path("rules"), Path("fixtures")

def test_sigma_schema():
    subprocess.run(["sigma", "check", str(RULES)], check=True)

def test_every_rule_has_benign_fixture():
    for rule in RULES.rglob("*.yml"):
        assert (FIX / f"{rule.stem}.benign.json").exists(), f"{rule.name}: no FP fixture"

def test_malicious_fixture_matches():
    ev = json.loads((FIX / "export_pfx.malicious.json").read_text())
    assert "certutil.exe" in ev["Image"].lower() and "-exportPFX" in ev["CommandLine"]

def test_benign_fixture_does_not_match():
    ev = json.loads((FIX / "export_pfx.benign.json").read_text())
    assert "provisioning" in ev["User"].lower()    # IT cert-renewal must not alert
# .github/workflows/detection-ci.yml
name: detection-ci
on: [pull_request]
jobs:
  detections:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: '3.12' }
      - run: pip install sigma-cli pysigma-backend-splunk pytest
      - run: pytest tests/ -q
      - run: sigma convert -t splunk -p sysmon rules/ -o /tmp/rules.spl
      - run: python scripts/sync_splunk.py /tmp/rules.spl   # audited API deploy

4.6 Phishing URL feature extractor (feeds classifier → Topic 8)

#!/usr/bin/env python3
import math, re
from collections import Counter
from urllib.parse import urlparse

SHORTENERS = {"bit.ly", "tinyurl.com", "t.co", "goo.gl", "ow.ly"}
BRANDS = {"paypal", "amazon", "walmart", "apple", "microsoft", "bank"}

def shannon(s):
    c = Counter(s)
    return -sum(n / len(s) * math.log2(n / len(s)) for n in c.values()) if s else 0.0

def url_features(url):
    if "://" not in url:
        url = "http://" + url
    u = urlparse(url)
    host = (u.hostname or "").lower()
    return {
        "len": len(url),
        "dots": host.count("."),
        "hyphens": url.count("-"),
        "has_at": int("@" in url),
        "ip_in_host": int(bool(re.fullmatch(r"(\d{1,3}\.){3}\d{1,3}", host))),
        "punycode": int("xn--" in host),
        "path_depth": u.path.count("/"),
        "entropy": round(shannon(host), 2),
        "https": int(u.scheme == "https"),
        "shortener": int(host in SHORTENERS),
        "brand_in_subdomain": int(any(b in host and not host.endswith(b + ".com") for b in BRANDS)),
    }

Points: lexical features → row vector for a logistic-regression/RF classifier (Topic 8); @/IP-host/punycode/shortener are high-precision interview answers. Logic walk: normalize scheme, lowercase host, count structural signals, score entropy, return a fixed dict so train and serve match.


5. Revision Table

# Item One-line answer Trap
1 3-tier AWS app WAF/LB public → app private → DB isolated, SG-to-SG, SSM admin, central logs "Private subnet = secure"; forgetting logs
2 SG vs NACL SG stateful, instance-level, SG refs; NACL stateless, subnet-level NACL needs explicit ephemeral return ports
3 Bastion vs SSM Prefer SSM (no inbound port, session logs); bastion fallback Bastion open to 0.0.0.0/0
4 Monitoring N hosts Agents → buffer → normalize → detect → alert → SOAR → feedback Forgetting agent-health alarms
5 SIEM cost Source filtering, metrics > raw events, hot/warm/cold "Index everything"
6 Normalization CIM/ECS/OCSF fields before rules Rules break on schema change
7 Secure API TLS, OIDC/JWT/mTLS, object-level authz, validation, limits, audit, versions AuthN ≠ AuthZ; secrets in URLs
8 Rate limiting Token bucket/sliding window; per-IP + per-key; 429 + Retry-After Fixed-window 2× burst
9 Secrets Vault/manager, workload identity, dynamic short TTLs, rotation, audit "Env vars are fine" — they leak
10 Secret zero K8s SA / cloud IAM / OIDC attestation → Vault Long-lived token planted in CI
11 CI gates gitleaks pre-commit; SAST/SCA/IaC on PR; container+SBOM; DAST staging; coverage ≥70% Blocking legacy backlog; path-filter skips
12 SAST vs DAST Code/fast/more FPs vs running app/behavioral/needs auth Union ≠ full coverage
13 Python libs re, requests(timeout/verify), socket connect_ex, subprocess shell=False, hashlib stream, argparse No timeout; verify=False; hardcoded token
14 Bash top-N awk '{print $1}' log \| sort \| uniq -c \| sort -rn \| head sort without -n; uniq needs sorted
15 Cron safety set -euo pipefail, flock -n, abs paths, rotate logs, non-zero exit Overlap; silent failure
16 Detection-as-code Sigma → convert → Git → CI fixtures → shadow → alert Editing rules in SIEM UI (drift)
17 FP testing Malicious must match; benign must not; boundary + missing field Global mutes, no expiry
18 Rollout gates Precision ≥80–90%, stable volume, no drift over ~7 days Alerting on day one
19 Available-to-sell on-hand − reserved − buffer; TTL holds; atomic decrement; idempotency Oversell/last-unit race
20 Degradation Browse may be stale; checkout must be correct Treating all reads the same
21 Low-stock alerts Event-driven threshold + hysteresis + (store, SKU) dedupe Polling every SKU; alert storms
22 Resume: Siege Findings → rule drafts → fixtures + tuning Calling a draft rule "done"

6. Common Mistakes

Read each item as what goes wrong, why a Walmart interviewer marks it down, and the one-sentence fix to say aloud.

  1. Tool-dropping without reasoning. Name the threat each control addresses and the tradeoff accepted.
  2. Confusing authN with authZ. A valid JWT ≠ permission for this object (BOLA/IDOR is OWASP API #1).
  3. Forgetting to monitor the monitor. Dead agent/parser = silent blindness; name heartbeat, lag, parse-failure alerts.
  4. Secrets in code / verify=False. Instant red flag: Vault/env tokens, verify=True, timeouts.
  5. Unbounded, un-tested automation. No timeout, no concurrency cap, no dry-run, no audit = a DoS or an outage.
  6. Reading whole files into memory. Stream with generators; chunk hashes; one-pass awk.
  7. sort without -n, uniq without sort. Classic one-liner fail.
  8. "SAST+DAST catches everything." State the coverage map and blind spots (logic, business rules, authz need negative tests).
  9. Blocking merges on the legacy backlog. Gate new verified findings; every exception has an owner and expiry.
  10. A firing rule as a finished detection. No fixtures, owner, shadow metrics, ATT&CK mapping, or documented blind spots = not engineered.
  11. Severity alone decides response. Add asset criticality, exposure, KEV/EPSS.
  12. Ignoring cost/scale. No SIEM volume math, no NAT-per-AZ reasoning = textbook-only.
  13. Retail blindness. "Design an API" often means "design inventory": mention reservations, idempotency, browse-vs-checkout correctness.
  14. No numbers. "Raised coverage to 70% and made it a required CI check" beats "improved coverage."
  15. Unbounded scope under questioning. State assumptions, prioritize the critical path, park the rest explicitly.

End of Topic 6. Cross-refs: Topic 1 (networking/VPC), Topic 3 (Linux/system scripting), Topic 4 (OWASP/API), Topic 5 (SOC/detection/IR), Topic 7 (DSA/OA).

Saved in this browser only.