DEV Community

Peter Mulligan
Peter Mulligan

Posted on

Your IDE is an Attack Vector

A new type of VSCode phishing attack is targeting freelancers via Upwork. Here’s how it works and how to protect yourself.


Hi, I'm Peter and I like to build things.

In recent years, we've seen a rise in high-profile attacks that use lifecycle hooks to run malicious code. The attack I am here to shine a spotlight on uses a similar "manipulate the tooling" mindset.

I work as a freelancer through a platform called Upwork. Over the last couple of months I have seen this platform turn into a hunting ground for a new type of phishing attack that uses VSCode as the attack vector.

It is an interesting attack because it sits somewhere between traditional phishing and spear phishing. It doesn't have a specific target but it doesn't cast a wide net. It uses a highly refined approach that enables the attacker to create a pool of self-selected high-value targets. Here's how it goes down...


The Tech

Visual Studio Code has the concept of tasks. They are defined in .vscode/tasks.json at the root of the project. Tasks are best thought of as an interface between the editor and your local tooling. They let you describe how commands should be run; what shell to use, what arguments to pass, what order to run them in. That kind of thing. VSCode has native bindings for npm, Gulp, Grunt, and Jake.

This is an auto-generated task for npm run build:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "npm: build",
      "type": "shell",
      "command": "npm",
      "args": ["run", "build"],
      "isBackground": false,
      "problemMatcher": ["$tsc"],
      "group": "build"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

If you need to run commands for a build tool that VSCode doesn't have native bindings for, you can do that using a custom task. These let you run commands with a variety of options as shown here in the schema. For now, we're only interested in the runsOn option.

From the docs:

Specifies when a task is run. Valid values are:

  • "default": The task will only be run when executed through the Run Task command.

  • "folderOpen": The task will be run when the containing folder is opened.

Now, if you're anything like me, the description of folderOpen is terrifying. Arbitrary code execution when the folder is opened? That can't be right. Surely there are protections in place?

Well yes, but actually no

There is a setting (task.allowAutomaticTasks) that lets you control the behavior. You can set it to always allow, always ask, or always deny. So we set it to "always ask" and we're good, right?

Unfortunately, as mentioned in this GitHub issue, when you clone a new repository and VSCode asks you if you trust the authors, that choice takes precedence over your settings. If you click "yes", you have told VSCode that you trust this project explicitly.

VSCode Trust Dialog


The Bait

The attacker posts a job on Upwork. On the surface, it looks the same as all the others. Tech stack and company description. Corporate jargon. Indistinguishable at first glance.

The jobs are almost exclusively in the crypto or DeFi domain. They are usually for between $75 and $150 per hour. They promise long-term work for the right candidate. They talk of technical excellence and team culture. It often mentions there will be a small take-home but not always.

Taken at face value, it looks like a good opportunity for a lot of developers. Developers in the crypto/DeFi sphere. Developers likely to have crypto wallets and api keys on their machine.


The Social Dynamics

It is free to post a job on Upwork. Freelancers then pay a small amount to submit a proposal for the project. The client reads the proposals and invites potential candidates into a chat room to interview. This allows both sides to define terms before initiating the contract.

For the freelancer, it's time to close the deal. Pressure is high and you can see from Upwork's UI that the client is interviewing multiple developers. Better bring your A-game.

The client sends a GitHub link.

It is cloned without question.


Do you trust the authors of the files in this folder?


The Payload

The handful of times I have encountered this attack (I stopped bidding on the jobs pretty quick!), the command being run was a lot of whitespace followed by a wget to a Vercel endpoint being piped into cmd. 😱

The whitespace confused me at first until I viewed the code on GitHub. Their code-viewer doesn't wrap lines, so I needed to scroll to the right about 3 times the width of the page to see the wget.


I didn't know I was going to turn this into an article at the time and I did not download the content of the URLs so I don't know what the malicious code itself did, but from context it downloads a crypto wallet exfiltration script. I still have one of the URLs if someone more skilled than me wants to go poking around. Just reach out.


Why This Attack Works

This attack is very well thought out. It puts itself in the target's shoes. I have no way of gathering data on its effectiveness but I can imagine it works wonders considering the amounts of these jobs posted daily.

The mechanics of Upwork do the heavy lifting of the social engineering. The ability to filter for crypto/DeFi developers and post a job for free provides frictionless target selection. The competitive and high-stress nature of the proposal system means you only interact with the target when they are already stressed. The platform’s social norm of running test repos, combined with the fact that freelancers are less likely to follow proper isolation protocols, makes this attack more robust.

The target selection is masterful, but combined with silent script execution when the project is opened in VSCode is devastating.


How To Stay Safe

Only open random repositories in an isolated environment (container or VM). Inspect them before running anything. Does it have a postinstall or preinstall script? What do they do? Does it have a .vscode folder? Why?

You can use VSCode's dev containers feature to open untrusted projects safely. From there, you can watch for any weird activity before it touches your main system.

You can also open projects with VSCode from the command line using the --disable-extensions flag. (code --disable-extensions .)

The key takeaway: Treat anything you didn’t personally create as untrusted by default.


Connect with me on LinkedIn or Upwork.

Top comments (47)

Collapse
 
maame-codes profile image
Maame Afua A. P. Fordjour

This is a real eye-opener. I usually think about security in my code, but I don't always think about the security of the editor itself. It is a great reminder to be more careful about which extensions I install on my Windows machine. Thanks for sharing these risks!

Collapse
 
fredbrooker_74 profile image
Fred Brooker

I am a web hoster for some 15 years. The internet is an attack vector by definition. It's a real Mobland. 😂 🍭

Collapse
 
aezur profile image
Peter Mulligan

The internet is an attack vector by definition.

Never was there a truer word said.

Thread Thread
 
fredbrooker_74 profile image
Fred Brooker

I see that my code works mostly by "client blocked, limited, incorrect password" in my logs... Neverending Story 😂

Collapse
 
egedev profile image
egeindie

This is a fantastic breakdown. The social engineering layer is what makes it so effective - the Upwork dynamics create the perfect pressure cooker where developers skip their usual caution.

I'd add another dimension: AI coding agents make this worse. Tools like Cursor, Windsurf, and Claude Code read project-level config files (.cursorrules, CLAUDE.md, etc.) that can influence what code gets generated. A malicious repo could include instructions that subtly introduce vulnerabilities through the AI's suggestions - and the developer would trust the output because "the AI wrote it."

The attack surface is expanding in directions most devs aren't thinking about:

  • .vscode/tasks.json (as you showed)
  • .husky/ and git hooks
  • postinstall scripts in package.json
  • AI agent config files
  • Docker compose files with host mounts

For freelancers specifically, I'd recommend: always open client repos in a disposable VM or container first. The 5 minutes it takes to spin up a dev container is nothing compared to losing your crypto wallet or SSH keys. Great article, Peter.

Collapse
 
ofri-peretz profile image
Ofri Peretz

The runsOn: "folderOpen" behavior overriding task.allowAutomaticTasks after clicking "Trust" is a genuinely bad design decision. That trust dialog trains you to click yes — it shows up constantly and blocking it means you can't work. So it becomes muscle memory, which is exactly what this attack exploits.

fwiw I've started grepping .vscode/ in any repo I clone before opening it. Takes two seconds and would catch this immediately.

Collapse
 
aezur profile image
Peter Mulligan

The runsOn: "folderOpen" behavior overriding task.allowAutomaticTasks after clicking "Trust" is a genuinely bad design decision.

I don't usually like to throw shade at decisions I don't know the constraints of, but yeah, this seems pretty unforgiveable.

fwiw I've started grepping .vscode/ in any repo I clone before opening it. Takes two seconds and would catch this immediately.

Grepping for what string exactly? I'm not sure I understand your process.

Collapse
 
vasughanta09 profile image
Vasu Ghanta

Thanks for the eye-opening post—this hits home as a fellow freelancer juggling Upwork and Jobbers gigs daily. I'm grateful you highlighted IDE vulnerabilities; it's already got me double-checking extensions and sandboxing my setup to protect client trust. Your practical tips are a game-changer for staying secure without slowing down the hustle!

Collapse
 
aezur profile image
Peter Mulligan

Thanks for your reply. I am very happy I managed to reach another Upworker!

Have you spotted the jobs I am talking about?

One other commonality they all seem to share is the jobs are usually posted from the Philippines or Ukraine. I didn't add it to the article because it's not a trait of the attack and could be misread. I only mention it here because you're on the frontlines.

Collapse
 
vasughanta09 profile image
Vasu Ghanta

Thanks Peter—spot on about vigilance with postings from those regions; scams thrive there. If I spot any suspicious jobs like that on Upwork/Jobbers, I'll report them immediately to keep the platform clean. Everyone who's seen this thread, please do the same—let's all protect freelancers from misuse and info theft!

Collapse
 
embernoglow profile image
EmberNoGlow

That's why I have a firewall. Everything that starts with microsoft is banned. Better use Notepad++ or a reliable firewall

Collapse
 
aezur profile image
Peter Mulligan • Edited

It would have been a HTTPS request originating with you and hitting a Vercel endpoint. Not much a Firewall would have done for you.

And Notepad++ was supply chain attacked 2 weeks ago. You should probably update your install.
Notepad++ Attack

Collapse
 
embernoglow profile image
EmberNoGlow

Ah, okay, looks like I'm switching to the standard Microsoft Windows Notepad. Although... No, let them attack me, there's no point in stealing my code anyway. Oh, I remembered, by the way, I don't have an antivirus.

Thread Thread
 
aezur profile image
Peter Mulligan

I still have a mini panic every now and then about not having a whole suite of AV software. It's just old hang-ups though. The truth is... Windows Defender got good. Like really good. Adding 3rd party AV is adding another attack surface.

Remember the days when 2 of your AV tools would decide they had beef with each other?

Thread Thread
 
embernoglow profile image
EmberNoGlow • Edited

I use antivirus software once a year, so I never really think about it. Its absence hasn't made any noticeable difference yet (at least I do think so...)

Collapse
 
itskondrat profile image
Mykola Kondratiuk

This is exactly why I built VibeCheck. When you're vibe coding with AI assistants, you often just accept whatever code gets generated, and now with AI doing file operations and creating configs, tasks.json becomes a huge blind spot. Most devs never even look at .vscode/ unless something breaks. The Upwork targeting is smart too, freelancers are under time pressure so they skip security checks. Curious if you've seen this extend beyond tasks.json? Like are settings.json or launch.json being exploited similarly?

Collapse
 
aezur profile image
Peter Mulligan

Not that I’ve seen in the wild.

In theory, settings.json or launch.json could be used to set the stage for an attack, but neither of them can silently execute code or pull in dependencies on their own. They’re declarative configs, not execution surfaces.

launch.json only runs anything if the user explicitly starts a debug session, and even then it’s just doing what it says on the tin. If malware is already present and intercepting that flow, you’re already compromised, so the file itself isn’t the meaningful attack vector.

Messing with these files would mostly just be a red flag with very little payoff compared to things like tasks, extensions, or social-engineering workspace trust.

Collapse
 
itskondrat profile image
Mykola Kondratiuk

Fair point about launch.json needing explicit debug session. I think the real danger isn't those declarative configs anyway, it's the combo of workspace trust + extensions like you said. When you're moving fast with AI generating code, you kinda get in the habit of just accepting workspace recommendations and trusting whatever pops up. That's where I've caught myself being sloppy tbh, not even reading what permissions I'm granting. The social engineering angle is probably way more effective than trying to get creative with config files.

Thread Thread
 
aezur profile image
Peter Mulligan

The social engineering angle is probably way more effective than trying to get creative with config files.

Exactly this. Humans are the weakest point in any system cos they get tired, they make mistakes, they don't know what they don't know.

More generally, I don't AI has changed our field at all. You review PRs the same whether it was generated by AI or a random coworker. You hold the mental model of "distrust by default". I kinda feel like it is similar to lumberjacks getting chainsaws. The safety protocols didn't change. The scope of the damage did if you don't follow them did.

Thread Thread
 
itskondrat profile image
Mykola Kondratiuk

The chainsaw analogy is perfect actually. The tool got way more powerful but the safety fundamentals are the same - you still need to know what you're cutting. I think the one thing that did change though is the volume. Like a junior dev with AI can generate 10x more code than before, which means 10x more surface area to review. The "distrust by default" mindset is right but now you need it at a scale most teams aren't used to.

Thread Thread
 
aezur profile image
Peter Mulligan

I don't disagree at all. A junior with a chainsaw is a scary thing. I think at the end of the day it is gonna come down to blast radius management. If your junior PRs a bug fix that hits 32 files, close the PR. Don't even waste your time reading it.

Essentially, complete removal of "the boy scout method".

Thread Thread
 
itskondrat profile image
Mykola Kondratiuk

blast radius management is such a good framing for this. the 32-file PR thing is real - I have seen AI agents do exactly that, you ask it to fix one test and it refactors half the codebase "while it was in there."

the boy scout method dying is interesting though. like in theory leaving code better than you found it is great, but when your junior is an AI that "improves" things based on vibes rather than understanding the system... yeah close that PR.

I wonder if the answer is just smaller blast radius by default - like sandboxing AI changes to only the files explicitly mentioned in the ticket. we have been experimenting with that approach and it catches a lot of the scope creep before review even starts

Thread Thread
 
aezur profile image
Peter Mulligan

Hope for the best, prepare for the worst! 😅

Thread Thread
 
itskondrat profile image
Mykola Kondratiuk

😄 basically the entire philosophy in one sentence. at least sandboxing the AI changes gives the "prepare" part some actual teeth

Collapse
 
pengeszikra profile image
Peter Vivo

Good to know about this attack vector. My instict are saved me, because on project which I work I add .vscode/ to .gitignore.
In other way my editor preference is vim, zed, nvim, VScode.
But for my focus on minimalism I am writing a cli based editor in rust, even a lot fever funcionality than vim

Collapse
 
aezur profile image
Peter Mulligan

I haven't checked if vim et al have lifecycle hooks that can be exploited, but I know what rabbit-hole I'm going down for the day!

My instict are saved me, because on project which I work I add .vscode/ to .gitignore.

Unfortunately, this won't save you from this particular attack because the creator of the repo didn't add it to the .gitignore so when you clone it is already in the project.

In general I agree though; saving the .vscode folder to the repo is something that only makes sense on a team repo, and even then I would prefer to just enforce anything I need to enforce in the CI pipeline. I don't care what is happening on the dev's local machine.

Collapse
 
pengeszikra profile image
Peter Vivo

I really rare clone repo from github for example, that why do not open in VSCode

Maybe I missing where this attack happen not under local development, when you open a repo with .vscode/task.json ?

Thread Thread
 
aezur profile image
Peter Mulligan

Oh okay. I misunderstood what you were saying. If you don't use VSCode, you are immune to this specific attack.

I think that "tooling as an attack vector" is the wider danger though. As @nedcodes pointed out, git hooks and husky also introduce this type of repository-level attack. They're not auto-initiated, but the risks are similar.

Collapse
 
nedcodes profile image
Ned C

Solid writeup. The folderOpen task execution is genuinely scary - most devs I know have never even looked at .vscode/tasks.json in repos they clone.

This extends to AI coding tools too. A malicious .cursorrules or CLAUDE.md in a repo could influence the AI to generate vulnerable code, disable security checks, or exfiltrate context through crafted suggestions. The trust boundary keeps expanding and most people aren't thinking about it.

Collapse
 
aezur profile image
Peter Mulligan

100%. Everything works on a "local is trusted" model that is no longer true and our machines hold valuable credentials in a way they just didn't a few years ago.

Your point about AI is very powerful. If you let an agent run wild, it's very hard to be sure nothing on the environment level was changed. Git doesn't track that.

Collapse
 
nedcodes profile image
Ned C

Exactly. And git hooks are another blind spot - most people run whatever is in .husky/ or .git/hooks/ without a second thought. The whole local dev environment is basically an honor system at this point.

Thread Thread
 
aezur profile image
Peter Mulligan

Lucky all the big players are trying to move local to the cloud. 💀

Collapse
 
gass profile image
gass

what a nasty attack that is.

Collapse
 
aezur profile image
Peter Mulligan

Yeah, I think it's scary because we are the targets. It's a dev hunting game for them.

Collapse
 
gass profile image
gass

Let the dev hunting games begin! .. just joking 😆

Thread Thread
 
aezur profile image
Peter Mulligan

🤓🎯🔫

Thread Thread
 
gass profile image
gass

😆

Collapse
 
dnszlsk profile image
DNSZLSK

Great article! Supply chain attacks on IDE extensions are underrated, most devs trust their editor implicitly.
I've been working on an open source scanner for npm/PyPI malware detection.
It's a personal project, not comparable to enterprise tools, but if anyone wants to test it:
github.com/DNSZLSK/muad-dib
Would be curious to hear if you've seen malicious VS Code extensions in the wild beyond the examples you mentioned.

Collapse
 
aezur profile image
Peter Mulligan • Edited

Oh that looks awesome. I was very amused when i opened the repo and it has .vscode folder and husky pre-commits! 😅

Have you considered integrating with the VirusTotal api to check hashes against? That way you could leverage an always-up-to-date definitions database and not have to invent new discovery techniques yourself.

Collapse
 
dnszlsk profile image
DNSZLSK

Ouuuuupss on the .vscode folder, I’ll clean that up. Thanks for the VirusTotal sugg : I’ve been thinking about external APIs but I’m hesitant to add runtime dependencies on third-party services for security reasons. Idk i’m student. Maybe an opt-in flag with user’s own API key could work. Appreciate the feedback!

Thread Thread
 
aezur profile image
Peter Mulligan

I’m hesitant to add runtime dependencies on third-party services for security reasons

Understandable, especially with a supply chain attack analyzer, but it's the central virus database for Google Security Operations. If your dependency on Google is supply chain attacked, you got bigger problems! 😅

Thread Thread
 
dnszlsk profile image
DNSZLSK

I'll look into it as an opt-in feature. Thanks!

Some comments may only be visible to logged-in visitors. Sign in to view all comments.