A Fake Coding Interview Is an Execution Request: Developer Safety Checklist
A coding interview repo is a request to run unknown code on a machine that holds your browser sessions, SSH keys, GitHub tokens, and cloud credentials. This checklist covers what to check before the call, what to look for in the repo, and what to do if you already ran it.
A coding interview repo is not just a file to review.
It is a request to execute unknown code on a machine that may hold browser sessions, SSH keys, GitHub tokens, package credentials, cloud access, and wallet extensions.
That is why fake coding interviews work. They do not need to exploit a firewall first. They persuade the developer to run the attack from inside the workstation.
Why fake interview repos work
The social engineering fits inside a trusted workflow. A recruiter sends a repository. The developer reviews it. That review requires running it locally. The entire attack lives inside that sequence.
There may be no external payload delivery event in the traditional sense. The developer fetches the repository from a platform they already use and executes it inside a trusted workflow. There is no phishing URL to block. The telemetry that would catch a traditional payload drop is looking in the wrong direction.
A developer machine during a technical interview has everything an attacker needs already loaded: the IDE has project tokens, the browser is logged into GitHub and AWS and the company's internal tools, the SSH agent is forwarded, the package manager has registry credentials. The workstation is the perimeter. The developer opens the gate.
The trust model of the coding interview creates exactly the execution context the attacker needs. That is the only reason this works at scale.
Red flags before the call
The company is hard to verify. The job listing references a company with a thin web presence, a recently registered domain, or a name one word off from a real company. LinkedIn pages exist but have no history and no employees with verifiable tenure.
The recruiter contacted you out of nowhere. The role was not posted publicly, or the recruiter's profile is sparse, recently created, or has no mutual connections and no employment history that checks out.
They asked you to clone and run a repo before the call. A legitimate technical screen should not pressure you to execute unfamiliar code on your primary workstation before trust is established. If local execution is genuinely required, ask for a sandboxed environment, a public repo with real commit history, or an in-browser alternative. "Run this before our call so you can explain your approach" warrants that question.
The repo is hosted somewhere unusual. Not GitHub, GitLab, or Bitbucket, or it is on a GitHub account with no other public activity and no organisation affiliation.
The communication channel shifted early. Initial contact was on LinkedIn or email, but follow-up moved to Telegram or WhatsApp faster than is normal for a professional hiring process.
Any one of these is noise. Two together warrant caution. Three means treat the repo as hostile until you have read every line of it.
Red flags in the repo
Open the repository and read it before doing anything else.
package.json has a postinstall script. npm install runs it automatically. A postinstall that calls a shell command, fetches a URL, or executes a build script runs before you have reviewed a single line of application code. Read it first.
Dependencies do not match the stated project. A take-home challenge for a trading dashboard that requires node-pty, axios, and child_process usage in a utility file alongside what appears to be a React frontend is pulling from a different toolkit. Read the dependency list against the stated purpose.
Obfuscated or minified code is checked into the repo. Production minification belongs in build output, not source. A lib/utils.min.js or a helpers/index.js that is one dense line of character soup in a development repository is the part to read first, not skip.
The code fetches remote content at runtime. A URL resolved and executed at startup, a base64-decoded string passed to eval(), a subprocess that calls an external host during initialisation. Read every network call.
Environment variables are read early and sent somewhere. process.env.HOME, process.env.AWS_ACCESS_KEY_ID, os.environ.get('SSH_AUTH_SOCK'). These are fingerprints and credentials. Read what the code does with them before you let it run.
A .vscode/tasks.json or editor configuration file contains execution commands. Anything configured to run on folder open, workspace trust, build, debug, or terminal launch is execution logic, not project metadata. VSCode and Cursor workspace files can trigger arbitrary commands the moment you open the project.
Build config files have code appended after the legitimate export. Files like tailwind.config.js, postcss.config.js, and next.config.js are executed by build tools on every npm run or npm install. Malicious loaders are sometimes appended at the end of otherwise-normal config files as a single long line after the closing module.exports. Scroll to the bottom of every config file and look for anything that does not belong.
The repo's commit history has mismatched author and committer timestamps. A commit where the author date is days earlier than the committer date is a red flag for a rewritten commit, not an ordinary contribution. Git squashing and rebasing can cause this legitimately, but a merge commit whose parent files are both clean and whose merge result is kilobytes larger than either parent was not produced by a clean merge.
The GitHub account has no history and the repo was created recently. A repository created the week you were contacted, by an account with no prior activity, is not the same risk profile as a repository with six months of commit history from a team.
Safe workflow before running anything
Read it first. All of it. Package manifests, build scripts, install hooks, any file that runs without explicit invocation. A small repo takes fifteen minutes. Spend them.
Use a machine with nothing on it. No browser sessions. No saved credentials. No SSH agent. No cloud CLI configuration. An environment that has nothing worth taking is an environment the attacker gains nothing from compromising. A throwaway VM is safest. A container can help only if it has no mounted secrets, no Docker socket, no host credential paths, no SSH agent forwarding, and no cloud credentials in the environment.
Do not create accounts on interviewer-controlled platforms using reused credentials. Use a unique email alias, a unique password, and a clean browser profile. Lure paths sometimes run through fake SaaS tools or project dashboards, not only repositories.
Do not run npm install without checking the scripts block first. cat package.json | jq '.scripts' before anything else. For Python: read setup.py and any __init__ files that execute on import.
Cut network access while testing if you can. A process that cannot reach the internet cannot exfiltrate anything. Run it offline. If it fails in a way that seems intentional, that is itself a finding.
Do not run it while logged into anything you care about. Log out of browser profiles. Unload your SSH agent. If you have token exports in your shell history, clear them before starting the session.
What to do if you already ran it
Assume compromise and rotate. Do not wait to confirm.
Rotate all credentials immediately. GitHub tokens, npm tokens, AWS keys, cloud provider credentials, anything accessible from that machine at the time. Rotation is cheap. Waiting to confirm is expensive.
Revoke active browser sessions from a clean device. Most platforms let you terminate all active sessions. Log out of everything. Do it from a machine that was not involved.
Audit browser extensions for replacement, not just new installs. A trojanized extension can silently replace a legitimate one. The extension UI will show the same name and icon. Check the extension's manifest.json directly: any field named commands.__meta or similar non-standard metadata block is a build-pipeline marker that should not be there. Also look for core JS files with forged timestamps from years before the extension existed.
Check your SSH authorized_keys. cat ~/.ssh/authorized_keys. An attacker with code execution can add a persistent entry. If there are keys you did not put there, remove them and rotate your host credentials.
Treat the machine as untrusted until rebuilt. Reinstalling is the safest path. Forensics is an option but requires time and expertise. If the machine holds anything sensitive, a clean environment and rotated credentials is faster and more reliable than trying to confirm the scope of what ran.
Tell your security team. If a company machine was involved, the security team needs to know. If it was personal but you accessed work repositories or credentials from it, they still need to know.
This checklist is adapted from The Fake Interview, a Red Asgard narrative podcast about Lazarus-attributed fake coding interviews, malicious developer repositories, credential theft, and developer workstation compromise.
Share this article
Help spread the word about security best practices.
Related Articles
Hunting Lazarus Part IX: The Google Mirror
Five trojanized browser extensions extracted Google profile identity through chrome.identity and routed it through an Aptos blockchain dead drop. Before any wallet artifact moved, the extension asked Chrome who owned the browser.
Hunting Lazarus Part VIII: OtterCookie
OtterCookie is a separate JavaScript / Node.js RAT running beside BeaverTail in the Contagious Interview operation. Its Socket.IO control plane maintains a live roster of connected developer workstations and broadcasts it on a thirty-second clock. Part VIII breaks down the protocol, the collection profile, the uid/userKey batch labels, the npm and Vercel delivery layer, and the operational shift from stored-data theft to live surveillance of developer machines.
Need Security Help?
Our team can help secure your blockchain, web applications, and infrastructure.