Running Cursor IDE in a Firejail Sandbox
Running Cursor Using Firejail on a Wayland Desktop
TL;DR? Already sold on the idea of sandboxing Cursor using firejail? Skip to the implementation. Want a script that does it for you? Jump to the script section.
However you may feel about the current LLM craze, one fact is indisputable: Assistive and agentic generative AI will have permanent effects on the software industry. Anyone who plans to remain in the industry needs to understand the way generative AI can be used and misused in the development of software and associated systems.
One of the more popular tools for AI-assisted development is the Cursor IDE. A fork of VSCode, Cursor integrates AI features directly into the IDE experience.
Why Bother?
The loudest proponents of GenAI for coding advocate for surrendering your entire terminal and user access to the agent (Claude Code, Cursor CLI, GitHub Copilot Terminal Chat, etc). As fun as it sounds to throw caution to the wind, I would not be able to respect myself as a security-minded professional if I was comfortable sharing every key and secret with an agent.
🗈 As an analogy: Although I trust my spouse completely, she does not know the password to my work laptop. Sharing that with anybody would be irresponsible and unethical as I, and not she, was the one entrusted with this access. Sharing my ~/.ssh/ folder, .env, .pgpass, etc. with a brilliant and mysterious stranger (e.g. an AI agent) would be even worse.
Cursor itself provides theoretical safeguards in the form of .cursorignore, but this is limited and easily circumvented.
The ignore file constrains access to files within the current project, and does not govern access to the broader system, which is the thing of greater concern.
An AI agent will quickly ask for access to interpreters such as bash
and python
in order to do the basics of their work, which can trivially be used to bypass the constraints configured in the IDE.
The first time I tried Cursor, my first prompt was a request for some basic project scaffolding.
My very first response was a request for bash
, access to run a complicated script it just made for my request.
While I appreciate that this at least comes in the form of source I could read before execution, the level of effort for due diligence is not trivial.
It is easy to imagine how quickly that discipline would degrade when such requests become a normal part of daily work.
I have heard many more moderate voices call for discretion in which projects are used with AI. This addresses some concerns about sharing private code with a SaaS model, but misses the question of system security. I am not worried about the sensitivity of project file nearly as much as I am worried about the sensitivity of the system housing the project.
This is not simply a matter of not trusting the AI itself, nor the risk from any number of plugins, but also the opportunities also for errors or accidents.
If due to a scripting error I was to accidentally run rm -rf $HOME/$APPDIR
when $APPDIR
was undefined, thereby deleting my entire userspace, I would blame myself for my inattention.
If it was not myself but an AI were to write such a bug, would I still blame myself?
Absolutely; the burden is still mine to bear, yet the chance of such mistakes are far higher when we’re habitually running code which passed only the briefest level of scrutiny (if any at all).
I want to experience the full power of the tool without giving it access to anything I explicitly grant it. I want to have my cake and eat it too. OCI had too many hurdles for use with a windowing system and OS integration. True virtualization (QEMU+KVM) proved too inefficient and isolated to use in any realistic/representative capacity. The answer which (so far) best balances risk mitigation with efficiency, for low-friction benefit, is firejail.
Install Firejail
Install firejail using the package manager of your choice, e.g.
sudo apt install firejail
or through any number of other methods documented on the Firejail website.
Download Cursor as an AppImage
This guide assumes that you will be running Cursor from an AppImage.
AppImage is currently the default installation method when downloaded from https://cursor.com/download . Go there and get a copy.
Save it as ~/.local/bin/Cursor.AppImage
.
Creating a Private Directory
Just make a directory in the user-writable place of your choice. This directory represents your sandboxed filesystem; this directory will appear to Cursor as if it was your user home.
The directions which following assume that you chose ~/cursor
.
If you chose something else, update matching instructions accordingly.
mkdir ~/cursor
Creating a Cursor profile
Add a new profile at $XDG_CONFIG_HOME/firejail/cursor.profile
(recommended) else /etc/firejail/cursor.profile
:
caps.drop all
nonewprivs
noroot
protocol unix,inet,inet6,netlink
private ~/cursor
include electron.profile
Logging In
Launch Cursor for the first time using the CLI like the following. Update the file name as necessary.
firejail --profile=cursor --appimage ~/.local/bin/Cursor.AppImage
Then click “Log In”.
This is the brief ugly part: The profile we defined blocks the dbus communication required to launch the browser directly. Although we want this restriction in general, it does complicate initial setup. Unfortunately, cursor currently provides no alternative method for authentication. Fortunately, we can solve this through a one-time workaround.
In the terminal where you launched cursor, you will see a message like the following:
gio: https://cursor.com/loginDeepControl?challenge=1CqZJXspB67qv8Vl0-0000O0000jK5ZbPjA8AJxPXds&uuid=7d3d2650-0000-0000-aaaa-6ed05e1e6b8a&mode=login: Operation not supported
Copy this URL to your browser and to complete the login to your Cursor account.
Creating a Desktop Entry
If you want to launch the app like any other, you will want a FreeDesktop (XDG) .Desktop
file.
Save this file as $XDG_DATA_HOME/applications/cursor-sandbox.desktop
, replacing the file path under the two Exec=
entries with your own file path.
Use absolute paths.
[Desktop Entry]
Name=Cursor (firejail)
Comment=The AI Code Editor.
Exec=firejail --appimage --profile=cursor /home/gspr/.local/bin/Cursor.AppImage %F
Icon=cursor
GenericName=Text Editor
Type=Application
StartupNotify=false
StartupWMClass=Cursor
Categories=TextEditor;Development;IDE;
MimeType=application/x-cursor-workspace;
Actions=new-empty-window;
Keywords=cursor;
[Desktop Action new-empty-window]
Name=New Empty Window
Name[de]=Neues leeres Fenster
Name[es]=Nueva ventana vacía
Name[fr]=Nouvelle fenêtre vide
Name[it]=Nuova finestra vuota
Name[ja]=新しい空のウィンドウ
Name[ko]=새 빈 창
Name[ru]=Новое пустое окно
Name[zh_CN]=新建空窗口
Name[zh_TW]=開新空視窗
Exec=firejail --appimage --profile=cursor /home/gspr/.local/bin/Cursor.AppImage %F
Icon=cursor
Supporting Icons
At this point, the IDE can be used with your launcher, but no icon will be available to identify the app in the task bar or other locations.
To support this, icons need to be extracted and imported. This extra step is not strictly necessary, but makes the desktop feel more complete like a “normal” installation would be. Icons are unlikely to change significantly across releases, so this step does not generally need to be repeated during upgrades.
mkdir /tmp/cursor-extract
cd /tmp/cursor-extract
~/.local/bin/Cursor.AppImage --appimage-extract
mkdir -p "$XDG_DATA_HOME/icons/"
cp -r squashfs-root/usr/share/icons/* "$XDG_DATA_HOME/icons/"
The icons will now be available in your desktop environment (menu, tray, etc).
Handling Upgrades
The IDE’s built-in updater will not work in a firejailed environment, but manual upgrade is easy; simply:
- Close the IDE (if open)
- Download the new AppImage
- Override the existing ~/.local/bin/Cursor.AppImage file
Putting It All Together (Script)
This script will handle the above and more: cursor-firejail-install.sh. It strictly runs and creates files in userspace. If any dependencies are missing, it will prompt you to install them yourself as a superuser.
In case you’re wondering, was this script written in the Cursor IDE? Yes! I thought this short process would be a nice, well-defined case to have an AI agent generate a little code based on a simple need with a provided solution. Did it work? Not at all! What was created looked like it would work, but was so riddled with bugs, by the end I had completely rewritten everything but the pretty little colorized logging functions at the top.
I have since learned to leverage it instead for things which are either in smaller, more focused chunks, or for larger things it can pull from its base knowledge or things readily available and repeated on the Internet. Something like this simple script is in-between: It provides all the information required to achieve the goal, but has limited external data to cross-reference, making it a poor fit for the AI agent to handle our our behalf. These are incredibly powerful tools, but learning when and how to employ them is a skill to learn like any other. 🤷
Happy (marginally safer) hacking!