EyalSec User Guide
What is EyalSec?
EyalSec is a secure Python. You install it next to your normal Python
and run your programs through it exactly as you do today; nothing in your code
has to change. We call the EyalSec runtime es-python, and you run it in place
of python.
While your program runs, es-python quietly watches for one specific danger: untrusted data (a.k.a. taint), meaning data that came from the outside world, reaching a risky action in your code. Where that data enters is called a source: a network socket, a file, standard input, the program's environment variables and command-line arguments, or foreign code (Python code on disk that another user can write to). The risky action it flows into is called a sink: things like running a system command, opening a file, or running a database query. Untrusted data reaching a sink is how most real-world hacks happen, and that is exactly the pattern es-python is built to catch.
When es-python sees untrusted data reach a sink, it can do one of two things, and you choose which:
- Report: let the program keep running, but record what happened on your dashboard so you can see it.
- Report and Raise: record it on your dashboard and stop the risky action
before it runs (the program gets a
RuntimeErrorinstead), so an attack is blocked, not just recorded.
You can read more about the difference, and when to use each, in Report mode vs Report and Raise mode.
You manage everything from one website: your dashboard. It shows which of your machines have es-python installed, what they have detected, and lets you change your settings, all in one place.
The three pieces
EyalSec is made of three parts that work together:
- The es-python runtime: the secure Python you install on each of your machines. It runs your code, watches for untrusted data reaching a sink, and sends what it finds back to your dashboard. See Running es-python.
- The web dashboard: the website where you sign in to add machines, watch events, set up filters, and change how each machine behaves. Start at Quick start: your first 10 minutes.
- The API: a JSON interface that does everything the dashboard does, so you can automate EyalSec or pull your data into your own tools. See the API reference.
Quick start: your first 10 minutes
This is the fastest path from zero to seeing your first event: create an account, add a machine, install es-python (the EyalSec runtime), run a script through it, and watch what it finds appear on your dashboard. Plan on about ten minutes.
1. Create your account
- Open EyalSec and go to the Register page.
- Pick a username, enter your email address, and choose a password (typed twice to confirm). Your password must be 8 to 72 characters. Tick the box to accept the Terms of Service and Privacy Policy.
- Submit. EyalSec emails a verification link to that address; click it to activate your account. That signs you in and drops you on your dashboard, and starts your 24-hour trial.
Verifying your email starts a 24-hour Trial: the defaults are 5 machines and a 5,000-events-per-month view, plenty for this walkthrough. After 24 hours the account goes read-only (you can still read everything) until an admin lifts the trial or adjusts your account. See Plans & roles for the details.
2. Log in (next time)
Clicking the verification link signed you in, so you can skip this for now. Next time you visit, go to the Login page and enter your username and password. If you later turn on two-factor authentication, you'll also be asked for a 6-digit code. See Accounts & signing in.
3. Add a machine
A machine is any host where you'll run your code through es-python: your laptop, a server, a container.
- Go to the Machines page and choose Add machine.
- Give it a Name (anything memorable, 1 to 128 characters).
- Pick the Distro (for example Ubuntu 24.04, Debian 12, RHEL 9, Amazon
Linux 2023, Fedora, or Arch) and the Arch (
x86_64oraarch64) that match the host. - Pick the Python version for this machine (3.13 is the default).
- Save. The machine appears with a created status pill, waiting for you to install es-python on it.
More on the full machine lifecycle is in Machines.
4. Copy the install command
With the machine selected (status created, OS set), the page shows a single install command. Select Copy to grab it. It looks like this:
source <(curl -sSL -d "secret=<token>" https://eyalsec.com/install.sh)
The <token> is a one-time secret that's valid for 10 minutes, so run it
soon. If it expires, just copy a fresh command from the same panel.
5. Run the installer on the host
Paste the command into a terminal on the machine itself and run it. It:
- installs the es-python runtime under
~/opt/eyalsec/, - drops
es-python(plus thees-pipandes-uvpackage-install helpers) into~/.local/bin/, and - adds that directory to your
PATH(in the current shell and in~/.bashrc).
The machine's status moves created → pending → installed. Once it shows
installed (a green, blinking pill) on the dashboard, you're ready to go:
es-python works immediately, in that same terminal, with no extra step.
If the download is interrupted, you can re-run the command to resume where it left off.
Note: the
source <(...)form is what makeses-pythonavailable in the current shell. If you instead pipe the script (curl ... | bash), the install still works, but you'll need to open a fresh terminal (or runsource ~/.bashrc) afterwards.
Privacy notice: es-python may capture values from your program's memory (including secrets and personal data) when untrusted data reaches a monitored sink. Only run it on systems and data you are authorized to monitor.
6. Run your script through es-python
Anywhere you'd normally type python, type es-python instead:
es-python your_script.py
Your program runs exactly as before, but now es-python is watching for untrusted data reaching a risky action and reporting what it sees. You don't need to change a single line of code. For more ways to run it, see Running es-python.
7. Watch events appear
Back on the dashboard, open the Events page.
As your program runs, detections show up here, newest first. Each row is one kind of event; the count rises each time the same thing happens again, and the severity column tells you at a glance how serious it looks. Click a row to expand it and see the details. Full coverage of the table, filters, and the detail pane is in Events.
If nothing shows up yet, give it a few seconds (events are sent in the
background), make sure you ran your script with es-python and not python, and
confirm the machine reads installed on the Machines page.
8. (Optional) Turn on Report and Raise mode
So far es-python is in Report mode: it records events but never interferes
with your program. When you're ready to actually block a risky action, switch
that machine to Report and Raise mode; then es-python stops the action (your program
gets a RuntimeError) instead of just recording it.
You set this up per machine in the Configure modal by adding a Raise rule. The how-to is in Machine configuration, and the difference between the two modes is explained in Report mode vs Report and Raise mode.
Note: Raise rules must be enabled on your account. If they aren't, adding one is
rejected with "Raise rules are not enabled for your account. Ask an
administrator to enable them." To have it turned on, contact sales@eyalsec.com.
That's the whole loop: install once, run with es-python, watch your events. From
here, explore Settings to secure your account,
Filters to cut noise, and the Dashboard overview
for a high-level summary. If you get stuck, see Getting help.
Accounts & signing in
Everything in EyalSec lives behind your account. This page walks through creating an account, signing in, the two-factor (2FA) check at login, signing out, and what to do when you forget your password.
Register
New here? Create an account from the Register page.
- Go to
/register/. - Pick a username and enter your email address.
- Enter a password, then type it again to confirm (it must follow the Password rules below).
- Tick the box to accept the Terms of Service and Privacy Policy. Depending on how the site is configured, a background human check (CAPTCHA) may also run.
- Submit.
You're not signed in yet: EyalSec emails a verification link to the address
you gave, and your account stays inactive until you click it. Following that link
activates the account, signs you in, and drops you on your dashboard at
/scanner/, so keep a working email handy at signup. See
Quick start: your first 10 minutes.
Verifying your email starts a 24-hour Trial with the default limits (5 machines, a 5,000-events-per-month view). After 24 hours the account becomes read-only: you can still view everything you've collected, but you can't make changes until an admin lifts the trial or adjusts your account. See Plans & roles for what your account includes. To have limits raised, email sales@eyalsec.com.
Some accounts are created from an invite link instead. An invite link opens a special registration page that works the same way (username, password, and accepting the terms); it simply starts the account with the settings an admin chose for you, skips the trial, and the link expires 24 hours after it was created.
Log in
Already have an account? Sign in from the Login page.
- Go to
/login/. - Enter your username and password.
- Submit.
If two-factor authentication is turned on for your account, you'll be sent to a second screen to finish signing in (see The 2FA challenge below).
For your protection, repeated failed sign-in attempts from the same place are slowed down. If you've been locked out by too many wrong guesses, wait a little and try again, or use Forgot password to reset.
The 2FA challenge
If you've enabled two-factor authentication, logging in takes one extra step.
After your username and password are accepted, you land on the 2FA page
(/login/2fa). There you can enter either:
- The current 6-digit code from your authenticator app, or
- One of your backup codes (in the form
xxxxx-xxxxx). Backup codes are not case-sensitive, and each one works only once.
Enter one of those and you're in. Backup codes are your safety net for when you don't have your phone, so keep them somewhere safe. You can turn 2FA on, see how many backup codes you have left, and generate fresh ones from Settings: your account.
Log out
To sign out, use the Log out link (/login/logout). This clears your
session and returns you to the login page. Signing out is a good idea on shared
or public computers.
Forgot password
Can't remember your password? You can reset it by email.
- Go to
/login/forgot. - Enter your username or email address and submit. For privacy, EyalSec always shows the same neutral message here; it won't tell you whether an account exists, so nobody can use this page to fish for valid accounts.
- If the account exists and has an email on file, a 6-digit code is emailed to it. That code is valid for 15 minutes.
- On the verify page (
/login/forgot/verify), enter your username or email, the 6-digit code, and your new password twice. The new password must meet the Password rules; that's checked before the code is, so fix any password problem first. - On success, your old codes are used up and you're sent to the login page.
Note that resetting your password does not sign you in automatically. Once the reset succeeds, log in normally with your new password.
To set or update the recovery email used here, see the email card in Settings: your account. Without an email on file there's nowhere to send the code, so add one before you need it.
Password rules
The same rules apply everywhere you set a password: when you register, change it in Settings, or reset it after a forgot-password. Your password must:
- Be 8 to 72 characters long.
- Not be a well-known common password (a blocklist of weak, easily guessed passwords is rejected).
If a password is turned down, you'll see a message telling you which rule it broke; adjust it and try again.
Still stuck signing in? Email support@eyalsec.com.
Settings: your account
The Settings page (/scanner/settings) is where you manage your own account:
your recovery email, your password, two-factor authentication, and your API
key. Each area is a separate card, described below.
Change your email
Your email is the address EyalSec uses to send you a password-reset code if you ever forget your password (see Forgot password).
- Open the Account card.
- Type a valid email address, or leave the field blank to remove the one on file.
- Click Save.
Setting or changing the address takes one extra step: EyalSec emails a confirmation link to the new address, and the change only takes effect when you click it. Until then the old address stays in place. This proves you control the inbox before it becomes your recovery address; if you had an old address on file, it also gets a heads-up notice about the requested change. The confirmation link expires after a short while, and only one change can be pending at a time.
Removing the address (saving an empty field) happens immediately, and any pending confirmation link is cancelled.
We recommend keeping a working email here so the Forgot password flow can reach you. Without one, there's nowhere to send a reset code.
Change your password
To change your password from the Account card:
- Enter your current password.
- Enter your new password, then type it again to confirm.
- Save.
The new password must follow the Password rules: 8 to 72 characters, and it can't be a common password.
Two-factor authentication
Two-factor authentication (2FA) adds a second step at login: after your password, you also enter a one-time 6-digit code from an authenticator app on your phone. It's the single best thing you can do to protect your account. The Two-factor card shows your current status and the actions below.
Enable 2FA
- Click to start setup. EyalSec shows a QR code (and a base32 secret you can type in by hand if you can't scan).
- Scan the QR code with an authenticator app (such as Google Authenticator, 1Password, Authy, or similar).
- Enter the current 6-digit code the app shows, to prove the setup worked.
- Confirm to enable.
The moment 2FA turns on, you're shown 10 one-time backup codes. These are shown only once; copy them and store them somewhere safe (a password manager is ideal). Each backup code works a single time and is your way back in if you ever lose your phone. At login you can use either a 6-digit code or a backup code; see The 2FA challenge.
Status card
When 2FA is on, the card shows Enabled along with Backup codes remaining: N so you can tell at a glance how many you have left. When it's off, the card prompts you to turn it on.
Regenerate backup codes
Running low on backup codes, or think they may have leaked? Regenerate them to get a fresh set of 10, again shown only once. Generating a new set replaces the old one, so your previous backup codes stop working.
Disable 2FA
You can turn 2FA off from the same card. Disabling it clears your saved authenticator secret and your backup codes, so your account goes back to password-only login.
Email sign-in codes
Besides the authenticator app, the card has an Email sign-in codes option: a one-time 6-digit code sent to your recovery email at sign-in. If you also use an authenticator app, the emailed code is an extra fallback; if you don't, the emailed code becomes your second factor on its own. It needs a recovery email on file (set one in the Account card first), and you can turn it on or off here at any time.
API access
An API key lets your own scripts and tools talk to the EyalSec dashboard API directly, without logging in through the website. It gives the same access your account has, so treat it like a password. You can have one key at a time. For the endpoints you can call with it, see the API reference.
Generate a key
- Open the API access card.
- Optionally set an expiry date (in
YYYY-MM-DDform), or leave it blank for a key that doesn't expire. - Generate.
You're shown the full key once. It starts with es2_, like this:
es2_3f9a8c2b7d1e4a6f0b5c9d8e7a2f1c3b
Copy it now; the full key is shown only once. If you lose it, you'll have to make a new one. The first time you generate a key, you don't need to re-enter your password.
View your key's details
After a key exists, the card shows its metadata, never the full secret again. You'll see:
- A short prefix so you can recognize it (for example
es2_3f9a…). - When it was created.
- When it expires (if you set an expiry).
- A sample
curlcommand showing how to send it.
You send the key in a request header named X-API-Key:
curl -H "X-API-Key: es2_3f9a8c2b7d1e4a6f0b5c9d8e7a2f1c3b" \
https://eyalsec.com/api/get_all_machines/
See the API reference for the full list of endpoints, request bodies, and example responses.
Regenerate or revoke a key
- Regenerate replaces your current key with a brand-new one (shown once). The old key stops working immediately, so update anything that was using it.
- Revoke turns off API access entirely until you generate a new key.
A note on re-entering your password
Some sensitive actions ask you to re-enter your current password even though you're already signed in. This is a deliberate extra check so that nobody who walks up to your open session can quietly weaken your account. You'll be asked for your password to:
- Disable two-factor authentication
- Regenerate your backup codes
- Regenerate your API key
- Revoke your API key
(Generating your very first API key doesn't require the password; only later regenerate/revoke actions do.)
Need a hand with any of this? Email support@eyalsec.com.
Machines
A machine is one computer (a laptop, a server, a container) where you run your code through es-python, the EyalSec runtime. The Machines page is where you add a machine, install EyalSec on it, and keep it up to date. Everything a machine detects shows up under Events, attributed to that machine.
You manage machines from Machines in the left sidebar.
What you can do here
- Add a new machine and pick its operating system and Python version.
- Get a one-time install command and run it on the host.
- Watch a machine move through its status lifecycle as it installs.
- Rename, reinstall (to take a newer build), configure, or delete a machine.
Add a machine
- Click Add machine.
- Give it a Name: anything from 1 to 128 characters that helps you
recognize the host (for example
prod-web-1ormy-laptop). - Pick the Distro (the Linux distribution it runs):
- Ubuntu 24.04 LTS
- Ubuntu 22.04 LTS
- Ubuntu 20.04 LTS
- Debian 13 (trixie)
- Debian 12 (bookworm)
- Debian 11 (bullseye)
- RHEL / Rocky / Alma 9
- Amazon Linux 2023
- Fedora (latest)
- Arch Linux (rolling)
- Pick the Arch (the processor architecture):
x86_64(most servers and desktops) oraarch64(ARM machines, such as AWS Graviton or a Raspberry Pi). Each distro is built for both, so there are 20 build targets in all. - Pick the Python version the machine should run. The default is 3.13; 3.14 is also available. This is the version of the es-python runtime the machine installs.
- Click Add.
The new machine appears in the table with a created status, ready to install.
How many machines you can add is a per-account limit (5 by default). If you hit
the cap you will see a message like "Machine limit reached (N). Contact
sales@eyalsec.com to upgrade your plan." See Plans & roles for
the limits, and email sales@eyalsec.com to raise yours.
A note on socket-only machines
You may still see a socket-only badge on some older machines. A socket-only machine runs a slimmer build of the runtime that watches network data only: the file, stdin, foreign, and env instrumentation is compiled out, so on such a machine only the socket source is live and the other four toggles do nothing. See Running es-python for what each source watches.
Socket-only is no longer a choice you make when adding a machine: new machines are always created with the full-instrumentation build. The badge and behavior remain only for machines created under the older option.
Status lifecycle
Each machine moves through three states, shown as a colored pill in its row:
- created (cyan): the machine exists in your account but EyalSec is not yet installed on the host. You add a machine in this state.
- pending (amber): the install command has been handed out and the machine is in the middle of installing. If an install is interrupted it stays here, and you can resume it (see Run the installer).
- installed (green, gently blinking): EyalSec is installed and the machine can report events.
created → pending → installed
Row badges
Once a machine is installed, its row can show a few badges next to the status pill:
- Build badge: whether the machine is running the latest EyalSec build. up to date means it is on the current build; update available means a newer build exists, and reinstalling the machine picks it up (see Reinstall).
- Python badge: the Python version this machine runs (for example 3.13).
- Socket-only badge: shown when the machine is a socket-only build.
- Stale agent (warning): an old copy of es-python, from a previous install of this machine, is still running somewhere and phoning in with a credential that has since been revoked. Its events are not accepted. Restart those programs under the latest install to clear it.
- Clone / tamper (warning): the machine's runtime reported an identity mismatch, for example the same install appearing from a second computer, or signs of tampering. Worth investigating; reinstalling issues a fresh credential.
Set the OS if it is missing
If a machine was added without an operating system, you will see an amber set OS chip on its row. Click it, pick the Distro and Arch (the same lists as when adding a machine), and save. You need the OS set before you can install, because EyalSec ships a build matched to that exact distro and architecture.
Get the install command
For a created machine that has its OS set:
-
Click Install on the machine's row.
-
EyalSec shows a one-line install command that looks like this:
source <(curl -sSL -d "secret=<token>" https://eyalsec.com/install.sh) -
Click Copy to copy it.
The <token> is a one-time secret that is valid for 10 minutes. It can
only be used once. If it expires or you need a fresh one, click Install
again to get a new command.
Run the installer
Run the copied command on the host itself (in a terminal on that machine). The installer:
- installs the EyalSec runtime to
~/opt/eyalsec/, - drops the
es-pythonlauncher (pluses-pipandes-uv, see Installing packages) into~/.local/bin/, - adds
~/.local/binto yourPATH: in the current shell (that's what thesource <(...)form is for) and persistently via~/.bashrc.
As it runs, the machine's status moves from created to pending and then to installed.
It installs into your home directory, so you do not need root or sudo.
If the download is interrupted, the machine stays in pending and the install
is resumable: run the command again (the installer continues with
curl -C - from where it left off) and it picks up where it stopped.
Once the status turns green, run your programs with es-python instead of
python, right away, in the same terminal. (Only if you ran the installer by
piping it to bash instead of the source <(...) form do you need a fresh
terminal or source ~/.bashrc first.) See
Running es-python for how to use it.
Rename
To rename a machine, open its kebab menu (the ⋮ icon on the row) and choose
Rename. The new name can be up to 128 characters. Renaming is purely
cosmetic; all of the machine's existing events stay attributed to it.
Reinstall (taking a newer build / "update available")
Reinstalling is how you update a machine to a newer EyalSec build. When a machine's build badge shows update available, reinstall it to pick up the latest build.
- Open the machine's kebab menu and choose Reinstall.
- Confirm within 3 seconds.
This resets the machine to created and mints a fresh install command. Run that command on the host (exactly like a first install) to lay down the new build. Your old events are safe: the machine's internal identity is unchanged, so everything it reported before stays attributed to it.
Configure
To change what a machine watches for and how it reacts, open its kebab menu and choose Configure. This opens a modal with Taint and Rules sections. This is also where you turn on Report and Raise mode for a machine. See Machine configuration for the full walkthrough.
Delete
To remove a machine, open its kebab menu, choose Delete, and confirm.
Warning: deleting a machine removes the machine and all of its events. This is irreversible; there is no undo. If you only want to stop using a machine, you can leave it in place instead; deleting is permanent.
Report mode vs Report and Raise mode
When EyalSec spots something dangerous (untrusted data, that is, data that came from the outside world, also called taint, about to be used in a risky action) it can do one of two things, and you choose which. We call these Report and Report and Raise.
The difference is simple: Report watches; Report and Raise watches and blocks.
Report mode
In Report mode, EyalSec lets your program keep running, but records what it saw on your dashboard. Nothing about your program's behavior changes; it runs exactly as it would under normal Python, and you get a full account of the risky moment under Events: what data was involved, where it came from, and the line of code that triggered it.
Report mode is the default. It is the right starting point because it can never break a working program. You get full visibility with zero risk, so you can see what EyalSec finds before deciding whether to block anything.
Use Report mode when you want to:
- see what's happening in an app without changing how it runs,
- watch a new or unfamiliar codebase,
- collect evidence before turning on blocking.
Report and Raise mode
In Report and Raise mode, EyalSec stops the risky action before it runs. Instead of
letting the dangerous operation happen, it aborts it by raising a Python error,
a RuntimeError, at the exact spot. The dangerous thing never happens, so an
attack is blocked. The event is still recorded on your dashboard, exactly as
in Report mode, so you see what was blocked. Report and Raise does both: it
reports and raises.
Here's what that looks like when a program tries to run a system command built from untrusted data:
import os
user_input = get_data_from_the_internet() # untrusted (tainted) data
os.system(user_input) # risky action: a "sink"
Under Report mode this runs and is recorded. Under Report and Raise mode it stops with an error like:
RuntimeError: EyalSec blocked a dangerous operation: untrusted data reached a
system command
Because Report and Raise mode throws a real Python error, your program sees it like any other exception. If your code already catches errors around that operation, the attack is blocked and your app can recover gracefully. If it doesn't, the program stops there, which is usually exactly what you want when something genuinely malicious is in flight.
Use Report and Raise mode when you want EyalSec to actively defend an app, not just observe it: typically once you've watched it in Report mode and trust what it's detecting.
How you turn each one on
- Report is on by default. Install EyalSec on a machine and run your code with
es-python, and you're reporting. Nothing else to do. - Report and Raise is set up by adding a mode-3 rule to a machine in the Configure modal. A mode-3 rule tells the runtime to block (raise) on matching events. With no raise rules in place, a machine only reports and never blocks.
See Machine configuration for how to add a mode-3 rule, choose which events it covers, and whitelist specific events out of raising.
Account note: Report and Raise mode (mode-3 rules) needs to be enabled on your account. If it isn't, adding a mode-3 rule is rejected with "Raise rules are not enabled for your account. Ask an administrator to enable them." Reporting works on every account. See Plans & roles, and email
sales@eyalsec.comto have it turned on.
Machine configuration
The Configure modal is where you tune what a machine watches for and how it
reacts. Open it from a machine's kebab menu (⋮) on the
Machines page → Configure. The modal has two sections:
- Taint sources: which kinds of untrusted data this machine tracks.
- Rules: what happens with the events a machine detects, from shaping what you see on the dashboard, to dropping noise at the machine, to (when enabled on your account) actually blocking the action on the host.
Changes in the Configure modal auto-save as you make them; there is no Save button.
Global vs per-machine scope
Configuration lives at two levels:
- Global: your account-wide defaults, which apply to every machine. You set these on the Filters page.
- Per-machine: settings for one specific machine, set here in the Configure modal.
A per-machine setting overrides the global default for that machine. So you can set a sensible baseline globally and then tighten or loosen it on individual machines.
The Taint sources section
Taint is untrusted data, meaning data that came from the outside world. A source is where that data enters your program. EyalSec tracks five sources, and each one has its own dropdown:
- socket: data arriving over the network.
- file: data read from files.
- stdin: data piped into the program on standard input.
- foreign: foreign code, meaning code in a file that is writable by another user (a common sign that something untrusted slipped into your code path).
- env: environment variables and command-line arguments (
sys.argv).
Each source shows a short description and a Read more link into The taint sources, the full reference for what each source watches and the event origins it produces. On a socket-only machine only socket is live (the other four are compiled out; see Machines).
Each source can be set to one of three states:
- On: track this source on this machine.
- Off: do not track this source on this machine.
- Unset (use flag): inherit the global default (or the runtime's command-line setting). This is the default, and it's what lets your global baseline flow through.
Because unset = inherit, you only flip a source to on or off when you want this machine to differ from your global setting. The per-machine choice always wins over the global one. A Set all sources dropdown at the top applies On / Off / Unset to all five sources in one step.
When it takes effect: taint changes apply on the machine's next process restart. A program that is already running keeps the taint settings it started with; start it again (or start your next program) to pick up the new ones.
The Rules section
A rule decides what happens with a particular kind of event. Each rule has:
- List: Whitelist or Blacklist. This decides which side of the pattern the rule acts on; see the table below. For drop rules the labels read Send only matching (whitelist) and Drop matching (blacklist), which is the same choice in plainer words.
- Source: which taint source the rule applies to. This is
any, so the rule applies to every taint source. - Event (regex): a regular expression matched against the event, so one
rule can cover a whole family of related events. Leaving it empty means all
events (the same as
.*). - Mode: what the rule does (see below).
- On: an enable checkbox, so you can switch a rule off without deleting it.
There are four modes. The first two only change what you see; the last two change what the machine does:
- Mode 2, Show (UI): a display rule (the default for new rules).
- Mode 1, Hide (UI): a display rule.
- Mode 4, Don't send (drop): the machine never sends matching events at all. Dropped events are not recorded (and never count against your event quota). Available on every account.
- Mode 3, Raise (machine): the runtime blocks the matching action on
the host with a
RuntimeError. This is how you turn on Report and Raise mode. Needs Raise enabled on your account.
How List combines with each mode:
| Mode | Whitelist | Blacklist |
|---|---|---|
| Show (UI) | Show only matching events | Hide matching events, show the rest |
| Hide (UI) | Hide matching events | Hide everything except matching |
| Don't send (drop) | "Send only matching": drop everything else at the machine | "Drop matching": drop matching, send the rest |
| Raise (machine) | Block (raise on) matching events | Block everything except matching |
A few important notes:
- Display rules (modes 1 and 2) never touch your program or your data. Every event is still recorded; you're just filtering the view. They work on every account.
- Drop rules (mode 4) act at the machine: a dropped event is never sent, never stored, and can't be recovered later. The dashboard warns you before you save a rule that would drop everything (a whitelist "send only" rule, or a blacklist drop with an empty pattern), and shows a standing warning banner while a "send only" rule is active.
- A blacklist Raise rule is the way to say "block everything except this known-safe case": it raises on all events in scope and carves the matching ones out.
Your account starts with two default global drop rules; see Filters.
Plan notes: Mode 3 (Raise) needs the Report-and-Raise capability enabled on your account; without it, creating or updating a mode-3 rule is rejected. Modes 1, 2, and 4 work on every account. See Plans & roles, and email
sales@eyalsec.comto enable it.
When configuration reaches the machine
The machine's runtime checks for its latest configuration shortly after startup and then roughly every 30 seconds, so most changes reach a running machine within about half a minute:
- Rules (raise and drop rules) apply on the next event the running program produces.
- Taint changes need a process restart to take effect (as noted above).
So if you add a raise rule, a long-running program starts honoring it within about half a minute. If you change which sources are tracked, restart the program to apply it.
Events
The Events page is where you read what EyalSec has detected across all your machines. Every time your code uses untrusted data (data that came from the outside world, also called taint) in a risky way, an event lands here.
The events table
Events are listed newest first. The table loads 100 rows at a time and fetches more automatically as you scroll, so you can keep going back in time without clicking through pages.
EyalSec doesn't store one row per occurrence; that would flood the page. Instead it groups and counts identical events. Two events are treated as the same when they share the same combination of:
- the machine owner (you),
- the source, where the untrusted data entered (a socket, a file, stdin, the environment, or foreign code),
- where, the display label for what happened, and
- a detail field that further pins down some events (for example the exact file path or regular expression involved), so two otherwise-identical labels stay separate rows.
When a matching event happens again, EyalSec just bumps a counter instead of adding a new row. The count column shows how many times that exact event has fired. A high count is a strong signal: something is hitting that code path over and over.
Each row also carries a severity: one of critical, high, medium, low, or info, computed on the server from what the event shows (for example, how attacker-reachable the data source looks). It's shown as a colored word in its own column so the rows that deserve attention stand out at a glance.
Filtering the list
A toolbar above the table lets you narrow what you see. None of these filters change what's stored; they only change what's displayed.
- Whitelist Repr: show only events whose text representation matches a regular expression (regex) you type. Filtering happens as you type and the pattern is validated, so a broken regex won't silently do nothing.
- Blacklist Repr: the opposite: hide events whose repr matches your regex.
- Date From / To: enter dates as
YYYY-MM-DD(or a full RFC3339 timestamp). To is inclusive of the whole day, so2026-06-05includes everything up to the end of that day. - Quick ranges: one-click buttons for the last 1m / 10m / 1h / 24h / 7d when you don't want to type dates.
- Machine: a dropdown to focus on a single machine.
- Severity: a multi-select to keep only some severities (for example just critical and high).
- Source: a multi-select to keep only events from certain taint sources:
socket/network, file, stdin, environment, command-line args, manual
(
make_vuln), fuzzer, or foreign code.
Your filter choices are saved in the page's URL. That means you can bookmark a filtered view, share the link, or reload the page and land right back where you were.
Opening a row for detail
Click any row to expand it in place and see the full story behind the event. Only one row is open at a time; opening another closes the first. The detail pane can include:
- Where: the display label for what happened (visible to everyone).
- Repr created and Repr found: text representations of the data, from when it was created and where it was found being used.
- Stack trace: the call stack at the moment of detection.
- Origin: file metadata about where the data came from: file type, permissions, size, owner, and timestamps.
Every event shows its label (where), a text representation, the stack trace,
and the file origin. To turn noisy detections into standing rules, use the
Filters page.
Where this fits
Events are the raw detections. To turn them into standing rules, like always hiding a known-safe pattern, dropping noise at the machine, or blocking a dangerous action at runtime, use Filters for global rules and Machine configuration for per-machine rules and Raise behavior. For a high-level summary instead of the full list, see the Dashboard overview.
Filters
Filters are standing rules that shape what shows up across your whole account. Where Events lets you narrow a single view on the fly, the Filters page lets you save rules that apply everywhere, every time.
Saved whitelist and blacklist filters
A filter is a saved regular expression (regex) that EyalSec matches against your events. Each filter has:
- Pattern: the regex to match. This is required.
- Label: a short name so you remember what the filter is for.
- Mode: blacklist (hide matching events) or whitelist (show only matching events). The default is blacklist.
- Target: what the pattern is matched against: the event's display label (where).
- Enabled: a toggle, so you can turn a filter off without deleting it. New filters are enabled by default.
These filters affect display only: they change which events you see on the dashboard, never which events get recorded. Nothing is ever lost; a hidden event is still there if you turn the filter off.
Creating, editing, and deleting filters
- Create: fill in at least a Pattern, optionally a Label, pick a Mode and Target, then save. An empty or invalid regex is rejected.
- Edit: change any field on an existing filter and save.
- Delete: remove a filter you no longer need.
A quick way to make a blacklist filter is the Block this event action on a row in the Events page, which drops a ready-made rule onto this page for you.
Global taint and rules live here too
Besides whitelist/blacklist filters, the Filters page also hosts your global taint settings and global rules: the same controls you'd set on a single machine, but applied to all your machines at once.
- Global taint lets you turn the untrusted-data sources (socket, file, stdin, foreign, env) on or off account-wide.
- Global rules are the display rules (Show / Hide), the machine-side drop rules ("Don't send"), and, when Report and Raise is enabled on your account, Report and Raise rules that block a risky action at runtime.
These are exactly the controls described in Machine configuration; the only difference is scope. A setting on an individual machine overrides the global one; leave a machine's control as unset to inherit whatever you've chosen globally here. For the full explanation of taint sources, rule modes, and when Report and Raise applies, read Machine configuration.
The default rules
The first time you open this page, your global rule list starts with two
default rules instead of an empty table: two enabled "Send only matching"
drop rules on source any, one with the event pattern re and one with
write. Together they tell every machine to send only events whose name
matches re or write and to drop the rest at the machine, a conservative
starting point that keeps early noise down.
They are ordinary rules, and they are yours to change: edit their patterns, switch them off, or delete them to have machines send everything. Deleting them sticks; the defaults are seeded only once per account, so they never come back on their own. While any "send only" rule is active, the rules editor shows a warning banner reminding you that non-matching events are being dropped.
Dashboard overview
The dashboard is your landing page, the first thing you see after signing in. It gives you a read-only, at-a-glance summary of everything EyalSec has detected, so you can spot trends without digging through the full Events list.
What the cards show
The overview is built from a set of summary cards and a timeline:
- Total hits: the total number of events recorded across all your machines.
- Unique types: how many distinct kinds of events you've seen (events that group together by source and label count as one type).
- Last 1h / 24h / 7d: how many events fired in each of those recent windows, so you can tell whether activity is rising or quiet right now.
- Top events: the events with the highest counts, ranked so the noisiest detections rise to the top.
- Recent activity: the most recent detections, newest first.
- 14-day timeline: a chart of detections per day over the last two weeks, which makes spikes and quiet stretches easy to see.
- First detection and Latest detection: the timestamps of your very first recorded event and your most recent one.
Read-only by design
The dashboard doesn't let you change anything; it's a window, not a control panel. When you spot something worth investigating, follow it up elsewhere:
- Click into the Events page to read the full, filterable list and open individual detections.
- Use Filters to hide known-safe noise or block dangerous patterns account-wide.
- Adjust what each machine watches for, and turn on Raise, in Machine configuration.
If your dashboard is empty, your machines may not have detected anything yet. Check that EyalSec is installed and running on the Machines page.
Activity log
The activity log is your own audit trail. Every meaningful action on your account is recorded here: who did what, when, from where, and whether it worked. It's a read-only record that lets you review your history and spot anything you don't recognize.
You'll find it on the Activity page (/scanner/activity) in the dashboard.
What each row records
Every entry is one action you (or something using your credentials) took. The table has these columns:
- Time: when the action happened.
- Action: what was done, as a short dotted name like
machine.create,auth.login, orapikey.generate(see the action groups below). - Target: what the action acted on, when it makes sense, for example the machine name, or the affected setting.
- Source IP: the network address the request came from. If you see a request from an IP you don't recognize, that's worth investigating.
- Auth method: how the request was authenticated: session (you, signed in through the website) or apikey (a request made with your API key). This makes it easy to tell apart things you did in the browser from things your scripts or integrations did.
- Outcome: whether the action succeeded or failed. A run of failure rows
(say, repeated
auth.loginfailures) can be an early warning sign.
Filtering the log
The log is shown reverse-chronologically, newest first, and you can narrow it down with two filters:
- Action: filter to a single action or a whole group. Actions are grouped by
prefix:
machine.*: anything you did to a machine (add, rename, configure, reinstall, delete, and so on).account.*: account changes such as your email or password.apikey.*: generating or revoking your API key.auth.*: sign-in activity, including logins and logouts.api.request: calls made against the API, typically with your API key.
- Outcome: show only success or only failure entries.
Loading more history
The log shows 50 entries per page. When there are older entries, a Load more button appears at the bottom; click it to pull in the next batch. It keeps your current filters as you go, so you can page back through just the actions you care about.
How long entries are kept
The activity log keeps the last 30 days of history. Older entries are removed automatically, so this is a recent-activity view, not a permanent archive. If you need a longer record for compliance or an investigation, export or note down what you need before it ages out.
Where this fits
The activity log tracks what you did to your account and machines. To see what
EyalSec detected in your code, head to the Events page or the
Dashboard overview. If something in the log looks wrong
(a login or an API request you didn't make), change your password right away on
the Settings page, and consider turning on two-factor
authentication and revoking your API key there. For anything you can't explain,
contact support@eyalsec.com.
Running es-python
es-python is EyalSec secure Python: the EyalSec runtime that watches
your program as it runs and reports (or blocks) risky behavior. After you install
EyalSec on a machine, this is the program you run your code with instead of stock
python.
Running your code
Once the installer has finished, just swap python for es-python:
es-python script.py
It behaves like the Python you already know (same language, same standard library), but it's watching for the moment your code uses untrusted data (data that came from the outside world, also called taint) in a risky way. When that happens, EyalSec records an event, which you'll see on the Events page.
If es-python isn't found, make sure you've opened a new shell since installing,
or that ~/.local/bin is on your PATH. See Machines for the
install steps.
Report vs Raise at runtime
EyalSec has two ways to respond when it catches something:
- Report (the default): it records the event and lets your program keep running. Nothing is interrupted; you just get a detection in the dashboard.
- Report and Raise: it stops the risky action by raising a
RuntimeError, aborting that operation. This turns EyalSec from an observer into a guardrail.
Most of the time you'll run in Report mode. You normally turn on Raise from the
dashboard with a mode-3 rule, which is the recommended way and doesn't require
touching the command line; see Machine configuration
and Report mode vs Report and Raise mode. At the command line,
the legacy --raise (and --raise-on-found) flags switch the whole run into
Report and Raise mode. Raising on events must be enabled on your account; see
Plans & roles.
The taint sources
A source is a place untrusted data can enter your program. By default EyalSec is conservative about what it treats as untrusted, so each source is off in the standard build; you switch the ones you want on. There are three ways to enable a source, in increasing order of how you'll use them day to day:
- A CLI flag:
--make-<source>-vuln, handy for a quick local run. - An environment variable: the mirror
ES2_MAKE_<SOURCE>_VULN=1, the same switch for when you can't change the command line. - The dashboard: the per-source toggle on the Filters page (global) or the Machine configuration Configure modal (per machine). This is how you'll normally do it.
| Source | What it watches | CLI flag | Env mirror | Default |
|---|---|---|---|---|
socket |
data received over the network (incl. TLS) | --make-socket-vuln |
ES2_MAKE_SOCKET_VULN=1 |
off¹ |
file |
data read from files on disk | --make-file-vuln |
ES2_MAKE_FILE_VULN=1 |
off |
stdin |
data on standard input | --make-stdin-vuln |
ES2_MAKE_STDIN_VULN=1 |
off |
foreign |
code loaded from another user's files | --make-foreign-vuln |
ES2_MAKE_FOREIGN_VULN=1 |
off |
env |
environment variables and sys.argv |
--make-env-vuln |
ES2_MAKE_ENV_VULN=1 |
off |
¹ socket is on by default in the socket-only build (tainting a machine's
sockets is that build's whole purpose); it is off by default in the standard
build like every other source.
Two shortcuts apply to the whole set:
--make-everything-vuln(orES2_MAKE_EVERYTHING_VULN=1) turns onsocket,file,stdin, andenvat once. It does not includeforeign; enable that one explicitly.
Every source is off by default (except socket in the socket-only build), so
plain es-python app.py with no flags, env vars, or dashboard rules enabled runs
with no taint instrumentation at all, equivalent to stock Python.
For example, to treat everything coming off a socket as untrusted for one run, and combine that with Raise to block it:
ES2_MAKE_SOCKET_VULN=1 es-python app.py
es-python --make-everything-vuln --raise app.py
When a source catches untrusted data reaching a risky operation, the recorded
event carries an origin label naming where the data came from (for example
socket:fd or stdin:input). The sections below list each source's origins so
you can recognise them on the Events page.
Socket-only build: a socket-only machine runs a build where
file,stdin,foreign, andenvare compiled out. On such a machine those four toggles do nothing, and onlysocketis live. Socket-only is no longer selectable when adding a machine, but some older machines still run it. See Machines.
The socket source
What it is. Data arriving from the network: anything your program receives from another computer over TCP, UDP, or a local socket, including the plaintext read out of a TLS connection.
What's instrumented. The socket object is marked when it is created or
connected, and the data returned by socket.recv(), recvfrom(), recv_into(),
and recvmsg() (and the ssl.SSLSocket read methods) comes out tainted. The
C site is Modules/socketmodule.c.
Enable it. --make-socket-vuln / ES2_MAKE_SOCKET_VULN=1, or the socket
toggle on the dashboard. Off by default in the standard build; on by default in
the socket-only build.
Event origins. socket:fd, socket:connected, socket:unbound; TLS reads
carry ssl:fd; a memory-mapped socket fd carries mmap-socket:fd.
import socket
s = socket.create_connection(("example.com", 80))
s.sendall(b"GET / HTTP/1.0\r\n\r\n")
data = s.recv(4096) # tainted, origin socket:connected
Limitations. In a socket-only build, data read with a raw os.read(fd, n)
on a socket fd is not tainted: socket taint flows through the socket API
(recv*), which carries no per-read fstat.
The file source
What it is. Data read from a file on disk: the contents of any file your program opens and reads.
What's instrumented. The bytes returned by open(path).read(), os.read(),
os.pread(), and mmap of a file descriptor. The interpreter classifies the fd
with a per-read fstat(2) and taints reads from regular files. The C sites are
Modules/_io/fileio.c and Modules/posixmodule.c.
Enable it. --make-file-vuln / ES2_MAKE_FILE_VULN=1, or the file
toggle. Off by default.
Event origins. fd:posix_read, fd:posix_pread, fileio.read(candidate),
fileio.readall(candidate); a memory-mapped file fd carries mmap-file:fd.
ES2_MAKE_FILE_VULN=1 es-python -c 'print(open("/etc/hostname").read())'
# the file contents are tainted, origin fileio.readall(candidate)
Limitations. The per-read fstat adds cost on file-heavy workloads; that
overhead is exactly what the socket-only build removes by compiling this source
out.
The stdin source
What it is. Data fed to your program on standard input: text piped in or typed at the keyboard.
What's instrumented. The string returned by the input() builtin and reads
from sys.stdin (file descriptor 0). The C site is Python/bltinmodule.c (the
input builtin) plus the file-read path on fd 0.
Enable it. --make-stdin-vuln / ES2_MAKE_STDIN_VULN=1, or the stdin
toggle. Off by default.
Event origins. stdin:input (the input() builtin) and stdin (stream
reads via sys.stdin).
name = input("name? ") # tainted, origin stdin:input
blob = sys.stdin.buffer.read() # tainted, origin stdin
Limitations. Compiled out in a socket-only build.
The foreign source
What it is. Foreign code: Python loaded from a file that another user on the machine can write to. Loading code you don't fully control is a classic way for an attacker to slip in their own logic. This is the one source that is a detection rather than a taint flow: it fires on the act of loading the file, independent of any data flow, even during interpreter startup.
What's instrumented. Importing a .py/.pyc, running the main script, or
compile/exec/eval of file source, whenever the resolved path is writable
by another user.
Enable it. --make-foreign-vuln / ES2_MAKE_FOREIGN_VULN=1, or the
foreign toggle. Off by default. Note --make-everything-vuln does not
include it; turn it on explicitly.
Event. Loading such a file emits foreign-code:<path>, naming the exact
file; you get one event per resolved path per process, so a noisy import loop
won't flood you. Under Raise, EyalSec aborts the load with a RuntimeError.
Exemptions. If some paths are trusted on purpose (say, a shared tools
directory you maintain), exempt them by listing their absolute path prefixes in
the ES2_FOREIGN_CODE_ALLOW environment variable, separated by colons. There
are no automatic exemptions: even the standard library is checked unless you
add it here.
ES2_MAKE_FOREIGN_VULN=1 ES2_FOREIGN_CODE_ALLOW=/opt/team-tools:/srv/shared es-python app.py
# importing /tmp/evil.py (writable by another user) -> event foreign-code:/tmp/evil.py
Limitations. Compiled out in a socket-only build.
The env source
What it is. Data describing how the program was launched: its environment variables and command-line arguments. Both are influenced by whoever started the process.
What's instrumented. The values read from os.environ (and os.getenv)
and the elements of sys.argv. The environment value is tainted, not the key
(a fixed variable name is not attacker data). The C sites are
Modules/posixmodule.c (environment) and Python/sysmodule.c (sys.argv).
Enable it. --make-env-vuln / ES2_MAKE_ENV_VULN=1, or the env toggle.
Off by default.
Event origins. env (environment values) and argv (argument-vector
elements).
import os, sys
token = os.environ["API_TOKEN"] # tainted, origin env
target = sys.argv[1] # tainted, origin argv
Limitations. Only the value is tainted, not the variable name. Compiled out in a socket-only build.
Installing packages: es-pip and es-uv
The install also drops two package-manager helpers next to es-python:
es-pipises-python -m pip: pip, running under the EyalSec runtime.es-uvises-python -m uv: the same idea for the fastuvinstaller. uv itself isn't bundled; runes-pip install uvonce first if you want it.
Use them exactly like pip and uv:
es-pip install requests
es-uv pip install flask
Packages you install this way go into the es-python environment, so your code
finds them when it runs under es-python. Behind the scenes, es-pip/es-uv
first ask your EyalSec server (briefly, with a one-second budget) for packages
it has prebuilt for this exact runtime, and otherwise fall back to PyPI plus the
wheels bundled with the install. That means installs keep working even when the
server is unreachable. For a handful of database drivers (PostgreSQL, MySQL,
MariaDB, Oracle), EyalSec ships its own instrumented builds of the standard
packages, so SQL-injection detection works out of the box; they behave
identically to the stock drivers.
Your regular pip and python are untouched and keep working as before.
How the machine picks up your settings
You don't have to restart everything every time you change a setting in the
dashboard. When es-python starts, it fetches that machine's configuration from
EyalSec, then refreshes it in the background roughly every 30 seconds, so new
settings reach the running program on their own.
Two kinds of change behave a little differently:
- Rules (raise and drop rules from Machine configuration) apply on the next event; no restart needed.
- Taint changes (turning sources on or off) take effect on the next process restart, because they change how the interpreter is set up at startup. After changing a taint source, restart your program for it to take hold.
A note on fuzz mode
es-python also carries some --fuzz-* flags. These are experimental and are
not needed for normal use; you can safely ignore them.
Where this fits
The runtime is the part of EyalSec that lives on your host and produces the
events you read in the dashboard. To control its behavior from the web instead of
the command line, see Machine configuration; to
understand the two response modes, see
Report mode vs Report and Raise mode; and to read what it
detects, see Events. If something isn't behaving as expected, reach out
to support@eyalsec.com.
On-machine docs and AI assistants
Every install drops a small docs home at ~/.eyalsec/:
~/.eyalsec/manual/- a runtime guide (also reachable viaeyalsec manual).~/.eyalsec/AGENTS.md- a context file for AI coding assistants (Claude Code, Cursor, Codex, and similar). Runeyalsec agents-initinside a project to load it, so the assistant knows whates-python/es-pipare and uses them correctly.~/.eyalsec/builds/*.json- one record per installed build; seeeyalsec info.
es-python -h also prints these paths at the end of its help.
Uninstalling removes ~/.eyalsec/. Any AGENTS.md symlink or @-include you
added to a project with eyalsec agents-init stays in that project and becomes a
dangling reference; remove it by hand if you no longer want it.
Plans & roles
Your EyalSec account has a set of limits and capabilities that we set for you.
Most things (installing machines, viewing Events, running
es-python, Report mode) work on
every account. A few limits and capabilities vary per account; the table below
shows the defaults.
What your account includes
| Your account | |
|---|---|
| Machines | Limited (5 by default) |
| Events visible | Monthly quota (5,000/month by default) |
| Report and Raise | Only if enabled |
A few things to note:
- New accounts start on a 24-hour Trial. A brand-new self-signup gets the defaults above (5 machines, a 5,000/month event quota) plus 24 hours to try things out (the clock starts when you verify your email; more on it below). An account created from an invite link skips the trial and starts at those same defaults.
- Report and Raise, blocking a risky action at runtime instead of just recording it, needs to be enabled on your account. See Report mode vs Report and Raise mode.
What you'll experience as a regular user
Your account mostly stays out of your way. You'll only notice its limits at a few specific moments, each of which gives you a clear message.
The machine cap (409)
Every regular user has a machine limit (5 by default). Try to add one past your cap and EyalSec stops you with an error:
Machine limit reached (5). Contact sales@eyalsec.com to upgrade your plan.
The number in parentheses is your current limit. To get more machines, ask us to raise your machine limit (see below). Deleting a machine you no longer need also frees a slot; see Machines.
The event quota
Every regular user also has a monthly event quota (5,000/month by default) capping how many event rows are visible in Events, including during an active 24-hour Trial. It resets every calendar month: the first N events that arrive each month are shown, and anything past that is hidden. Nothing is deleted; each new month starts a fresh quota, and an event that fires again in a later month counts against (and can appear in) that month's quota.
When you scroll to the end of your events list, the footer reminds you of your quota. To see more events per month, ask us to raise it (see below).
The trial clock
A brand-new self-signup account has a 24-hour clock that starts the moment you verify your email (the confirmation step at signup). During those 24 hours everything works normally. After 24 hours the trial goes read-only:
- You can still read: browse Events, the Dashboard, the Activity log, and your settings.
- You cannot write: adding or changing machines, creating filters or rules, and similar actions are blocked. You'll see:
Your trial has ended…
Your events view also freezes at the moment the trial ended, so you won't see new detections come in until we update your account. Once that happens the trial clock clears and full write access is restored right away.
The Raise gate
Report and Raise mode (a "mode-3" rule that tells the runtime to block an action) needs to be enabled on your account. If you try to add a Raise rule before it's enabled, the request is rejected with:
Raise rules are not enabled for your account. Ask an administrator to enable them.
Email sales@eyalsec.com to turn it on. Reporting works for every account
either way; only blocking (Raise) needs the capability enabled. See
Machine configuration for how Raise rules work.
How to get more
There's no self-service upgrade button. To raise your machine limit or event quota, or to enable Report and Raise, email sales@eyalsec.com and tell us what you need; we set it for you.
Once your grants change, the new limits apply immediately: a higher machine cap unlocks, Raise becomes available the moment it's granted, and any grant change clears an active trial clock and restores write access. You don't need to reinstall anything on your machines.
If you're not sure what you need, or you have a question about your account, reach out to support@eyalsec.com. See also Getting help.
API reference
Everything you can do in the dashboard, you can also do over HTTP. The EyalSec JSON API lets you read your data (events, stats, machines, activity) and automate every action the web UI performs: adding machines, generating install commands, creating filters and rules, configuring taint, and more. It's the same backend the dashboard itself talks to.
Base URL. All endpoints live under your EyalSec origin; for the hosted
service that's https://eyalsec.com. If you run on your own domain, use that
origin instead. Every path below is relative to it (e.g. the full URL for
GET /api/get_all_machines/ is https://eyalsec.com/api/get_all_machines/).
Authentication
Every /api/* endpoint except the public GET /api/supported_os_types/
and GET /api/supported_python_versions/ needs you to prove who you are. There
are two ways:
-
API key (recommended for scripts). Send an HTTP header:
X-API-Key: es2_3f9a…Generate the key once in Settings: your account under the API access card. The token is shown only once at creation, so copy it then. The format is
es2_followed by a base64url encoding of 32 random bytes (es2_+ base64url(32 bytes)). API-key requests bypass CSRF entirely; there's no token to manage. -
Session + CSRF (what the browser uses). If you're already logged in with a session cookie, you can call the API the way the dashboard does: include the CSRF token either as an
X-CSRFTokenheader or as acsrf_tokenform field on every write. This is convenient from inside a logged-in browser session but awkward from a standalone script, which is why API keys exist.
A few rules that apply to API-key requests specifically:
- They always return JSON: you'll get a body like
{"error": "…"}and never an HTML page or a redirect to the login screen. - A bad, revoked, or expired key returns 401 Unauthorized.
- Read-only keys are judged per endpoint, not by HTTP method. If you tick
read only when generating a key, it can call anything marked read only in
the reference and nothing marked writes. Note that the events query is a
POST(its filters travel in the body) and is still a read, so a read-only key can use it. Anything under/api/account/is a write and is refused. - Writes are blocked for expired trials. If your account is on a trial and the 24-hour clock has run out, anything that changes data returns 403 while reads keep working.
A worked example
Fetch your event statistics with nothing but the API key:
curl -s https://eyalsec.com/api/get_event_stats/ \
-H "X-API-Key: es2_3f9a…"
A typical response:
{
"total_hits": 1842,
"unique_types": 27,
"last_1h": 12,
"last_24h": 305,
"last_7d": 1402,
"top_events": [],
"recent": [],
"timeline_14d": [],
"first_detection": "2026-05-20T09:14:02Z",
"latest_detection": "2026-06-05T11:48:31Z"
}
Add the same -H "X-API-Key: …" header to any request below.
Events & stats
Read your detected events, the aggregate statistics, and the list of known event names.
Get events (with counts)
POST /api/get_all_events_with_count/
The request body is JSON; every field is optional:
cursor: a base64url-encoded{t, id}pointer for paging (see below).whitelist_repr: regex; keep only events whose repr matches.blacklist_repr: regex; drop events whose repr matches.from,to: date strings,YYYY-MM-DDor full RFC3339.tois inclusive of the whole day.machine: limit to one machine.severity: a list of severities to keep, fromcritical,high,medium,low,info.sources: a list of taint sources to keep, fromsocket,file,stdin,env,argv,manual,fuzzer,foreign.
The response is a list of events plus a paging cursor:
{
"events": [
[
"2026-06-05T11:48:31Z", "socket.recv -> eval", "<str 'id'>",
"Traceback (most recent call last)…", "", 14,
"web-prod-01", "{...origin...}", "<str 'id'>", "critical"
]
],
"next_cursor": "eyJ0IjoiMjAyNi0wNi0wNVQxMTo0ODozMVoiLCJpZCI6OTl9",
"event_limit": 5000,
"event_limit_monthly": true
}
Each event is a 10-element array. The positions are fixed:
| Index | Field | Meaning |
|---|---|---|
| 0 | created_at |
When the event was first seen |
| 1 | where |
Display label for what happened |
| 2 | str_repr |
Text representation of the data |
| 3 | trace |
Stack trace at detection |
| 4 | (reserved) | Always blank on your account |
| 5 | count |
How many identical occurrences |
| 6 | machine_name |
Which machine reported it |
| 7 | origin |
File metadata of the data's source |
| 8 | repr_created |
Repr at the moment the data was created |
| 9 | severity |
Computed severity (critical/high/medium/low/info) |
Paging. The API returns up to 100 events per page, newest first. To fetch
the next page, send the next_cursor value from the previous response as
cursor in your next request. When next_cursor is empty there are no more
pages.
Your event quota. The API sees exactly what the dashboard sees, so your
account's event quota applies (event_limit in the response; -1 means
unlimited, and event_limit_monthly tells you whether it resets each calendar
month). Limits are set per account, not by a plan tier.
This endpoint is a read, so it still works for expired trials. Errors: 400 (bad JSON, bad cursor, invalid regex, or unparseable timestamp), 500 (server error).
curl -s https://eyalsec.com/api/get_all_events_with_count/ \
-H "X-API-Key: es2_3f9a…" \
-H "Content-Type: application/json" \
-d '{"from":"2026-06-01","severity":["critical","high"],"blacklist_repr":"^healthcheck$"}'
Get event statistics
GET /api/get_event_stats/
Returns the dashboard summary:
{
"total_hits": 1842, "unique_types": 27,
"last_1h": 12, "last_24h": 305, "last_7d": 1402,
"top_events": [], "recent": [], "timeline_14d": [],
"first_detection": "2026-05-20T09:14:02Z",
"latest_detection": "2026-06-05T11:48:31Z"
}
first_detection and latest_detection may be absent if you have no events yet.
Get event names
GET /api/event_names/
{ "names": ["socket.recv -> eval", "open -> exec", "…"] }
Machines
List your machines and perform every action on the Machines page.
List machines
GET /api/get_all_machines/
{
"machines": [
[
"2026-05-20T09:00:00Z", "web-prod-01", "installed",
"m_8f3c2a1b", "ubuntu_24_04_x86_64",
"bc0ef13-20260604T120000Z", "bc0ef13-20260605T080000Z",
"3.13", false, false, false, false
]
]
}
Each machine is a 12-element array:
| Index | Field | Meaning |
|---|---|---|
| 0 | created_at |
When you added the machine |
| 1 | machine_name |
The name you gave it |
| 2 | status |
created, pending, or installed |
| 3 | public_id |
Safe machine handle used in other API calls |
| 4 | os_type |
Distro + arch target |
| 5 | installed_build |
Build version currently installed on the host |
| 6 | current_build |
Latest build available for this machine's target, Python version, and variant |
| 7 | python_version |
The Python version this machine runs (e.g. 3.13) |
| 8 | socket_only |
Dormant. Machines created now are never socket-only, so this reads false; older machines may still carry true. |
| 9 | clone_locked |
true when a clone/tamper identity mismatch was flagged |
| 10 | stalled |
true when an install has sat in pending too long |
| 11 | stale_agent |
true when an old install's runtime (revoked credential) is still phoning in |
The machine's secret is never exposed by the API. You reference machines by
their public_id. When installed_build and current_build differ, an update
is available (see Machines).
curl -s https://eyalsec.com/api/get_all_machines/ \
-H "X-API-Key: es2_3f9a…"
Add a machine
POST /api/add_new_machine/
Form fields:
machine_name(required)distro,arch(required): combined into the machine'sos_type.python_version(optional): defaults to3.13; must be one of the versions fromGET /api/supported_python_versions/.
Returns 201:
{ "Name": "web-prod-02", "OsType": "ubuntu_24_04_x86_64",
"Status": "created", "PublicID": "m_a1b2c3d4",
"PythonVersion": "3.13", "SocketOnly": "false" }
Errors: 422 (bad/missing field, unknown target or Python version), 409 (machine
limit reached), 403 (CSRF). SocketOnly always comes back "false": machines
created now are never socket-only (the field is retained but dormant).
Set a machine's OS
POST /api/set_machine_os/
Form fields: machine_name, distro, arch. Returns 200:
{ "machine_name": "web-prod-02", "os_type": "debian_12_aarch64" }
Rename a machine
POST /api/rename_machine/
JSON body {"public_id": "m_…", "machine_name": "new-name"} → 200.
Delete a machine
POST /api/delete_machine/
JSON body {"public_id": "m_…"} → 204. This cascades: the machine and
all of its events are removed (see Machines).
Get the install command
POST /api/get_eyalsec_install_script_data/
JSON body {"public_id": "m_…"} returns a one-time install secret:
{ "secret": "…", "script_path": "/install.sh", "os_type": "ubuntu_24_04_x86_64" }
The secret is single-use with a 10-minute TTL. Errors: 422 (no OS set yet),
404 (unknown machine), 409 (machine is already pending or installed).
Reinstall (update) a machine
POST /api/reinstall_machine/
JSON body {"public_id": "m_…"} returns a fresh install command (the same shape
as above) and resets the machine to created. This is how you pull a newer
build onto a host. Errors: 409 if the machine isn't installed or pending.
curl -s https://eyalsec.com/api/reinstall_machine/ \
-H "X-API-Key: es2_3f9a…" \
-H "Content-Type: application/json" \
-d '{"public_id":"m_8f3c2a1b"}'
Get the uninstall command
POST /api/get_uninstall_script_data/
JSON body {"public_id": "m_…"} returns a one-time secret and the uninstall
script path, the same command the Machines page shows:
{ "secret": "…", "script_path": "/uninstall.sh" }
The secret is single-use with a short life.
Supported OS types (public)
GET /api/supported_os_types/
Needs no authentication:
{ "distros": [ { "id": "ubuntu_24_04", "label": "Ubuntu 24.04 LTS",
"archs": ["x86_64", "aarch64"] } ] }
Supported Python versions (public)
GET /api/supported_python_versions/
Also needs no authentication. Returns the versions you can pick when adding a machine, and the default:
{ "versions": ["3.13", "3.14"], "default": "3.13" }
Filters
Manage the global whitelist/blacklist filters from the Filters page.
List filters
GET /api/filters/
{ "filters": [ { "id": 7, "pattern": "^healthcheck$", "label": "noise",
"mode": "blacklist", "target": "where", "enabled": true } ] }
Create a filter
POST /api/filters/create/
JSON body:
pattern(required): a regex.label: your description.mode(required):blacklistorwhitelist.target:where. This is the default and the value your account can use; anything else is rejected with a 403.enabled: defaults totrue.
Returns 201. A bad, empty, or invalid regex returns 400.
curl -s https://eyalsec.com/api/filters/create/ \
-H "X-API-Key: es2_3f9a…" \
-H "Content-Type: application/json" \
-d '{"pattern":"^healthcheck$","mode":"blacklist","label":"noise"}'
Update a filter
PATCH /api/filters/{id}/update/
Send a partial JSON body with just the fields you want to change → 200. A missing filter returns 404.
Delete a filter
DELETE /api/filters/{id}/delete/
Returns 204.
Machine rules
Machine rules drive the dashboard display (modes 1 and 2), machine-side event
dropping (mode 4), and runtime Raise behavior (mode 3). For the full concept,
including how polarity (whitelist/blacklist) combines with each mode, see
Machine configuration. The machine field is a
public_id, or '' (empty string) to target the global scope.
List rules
GET /api/machine_rules/?machine=<public_id|>
{
"rules": [
{ "ID": 12, "Source": "any", "EventPattern": ".*eval.*",
"Mode": 3, "Action": "raise", "Polarity": "whitelist",
"Label": "block eval", "Enabled": true }
]
}
Note the capitalized field names in rule objects. Action is a legacy
field (always raise for new rules); Polarity is what matters.
Create a rule
POST /api/machine_rules/create/
JSON body:
machine: apublic_id, or''for global.polarity:whitelist(default) orblacklist. See Machine configuration for what each means per mode.event_pattern: a regex. Empty (or omitted) means all events (.*).mode:1(Hide, UI),2(Show, UI; the default),3(Raise, blocks on the machine), or4(Don't send: the machine drops matching events).source:any(the default). Rules apply to every taint source.label,enabled(defaults totrue).
Returns 201 with the created rule (same shape as the list).
Mode 3 (Raise) has to be enabled for your account. It is a per-account
setting rather than something you switch on yourself. Without it the request
returns 403 with a message saying Raise rules are not enabled for your
account, and who to ask. Check grant_raise on
GET /api/account/ to see where you stand before you try.
curl -s https://eyalsec.com/api/machine_rules/create/ \
-H "X-API-Key: es2_3f9a…" \
-H "Content-Type: application/json" \
-d '{"machine":"m_8f3c2a1b","polarity":"whitelist","event_pattern":".*eval.*","mode":3,"label":"block eval"}'
Update a rule
PATCH /api/machine_rules/{id}/update/
Partial JSON (any of polarity, event_pattern, mode, label, enabled)
→ 200. Changing a rule's mode to 3 re-checks that Raise is enabled for
your account.
Delete a rule
DELETE /api/machine_rules/{id}/delete/
Returns 204.
Taint
Read and set which taint sources are tracked, per machine or globally. For what the sources mean and when changes take effect, see Machine configuration.
Read taint settings
GET /api/machine_taint/?machine=<public_id|>
All five sources are always present:
{ "taint": { "socket": "on", "file": "off", "stdin": "unset",
"foreign": "unset", "env": "unset" } }
Set a taint source
PUT /api/machine_taint/
JSON body:
machine: apublic_id, or''for global.source(required):socket,file,stdin,foreign, orenv.state(required):on,off, orunset(inherit).
Returns 204. Remember: taint changes take effect on the next process restart
of es-python.
curl -s -X PUT https://eyalsec.com/api/machine_taint/ \
-H "X-API-Key: es2_3f9a…" \
-H "Content-Type: application/json" \
-d '{"machine":"m_8f3c2a1b","source":"socket","state":"on"}'
Activity
Read your own audit trail, the same data shown on the Activity log page.
GET /api/activity/?action=&outcome=&before_ts=&before_id=
Results are newest-first and keyset-paged, 50 per page. Filter with:
action: e.g.api.request,machine.*,auth.*,account.*,apikey.*.outcome:successorfailure.before_ts,before_id: the keyset pointer for the next page.
Activity is retained for 30 days.
curl -s "https://eyalsec.com/api/activity/?action=machine.create&outcome=success" \
-H "X-API-Key: es2_3f9a…"
Your account
Everything on the Settings page is here too: your profile, password, two-factor auth, API keys and billing.
These endpoints ask for the same proof the dashboard asks for. Changing your password over the API needs your current password, exactly as the form does. Generating a key needs your password and a 2FA code. An API key on its own is never enough. That is deliberate: the API should not be an easier way into your account than the browser, so a leaked key still cannot take it over.
Send the credentials as form fields alongside the rest of the request:
password, and totp when your account has two-factor auth on. A backup code
works anywhere a totp code does.
Three of these destroy the key you are calling with. Read the warnings.
Read your profile
GET /api/account/
{
"username": "alice",
"email": "alice@example.com",
"timezone": "Asia/Jerusalem",
"email_verified": true,
"totp_enabled": true,
"email_2fa_enabled": false,
"machine_limit": 5,
"event_limit": 5000,
"event_limit_monthly": true,
"grant_raise": false
}
This is where you check what your account is actually allowed to do:
machine_limit, event_limit (-1 means unlimited) and grant_raise. It never
returns your password, your two-factor secret, or any key.
Set your timezone
POST /api/account/timezone
Form field timezone: an IANA name such as Asia/Jerusalem. Send it empty to go
back to automatic (your browser's zone). No password needed, same as the
dashboard. Returns 200 with {"timezone": "Asia/Jerusalem"}; an unknown zone
returns 422 and changes nothing.
Change your recovery email
POST /api/account/email
Needs password, plus totp when 2FA is on. Form field email.
A new address is not applied immediately. A confirmation link is emailed to it, and the change only lands once you click that link, exactly like the dashboard. This is what stops someone with a stolen session pointing your recovery address at themselves.
{
"status": "confirmation_sent",
"email": "new@example.com",
"note": "not applied yet: click the link sent to the new address to confirm"
}
Sending an empty email removes your recovery address straight away. That
also disables email 2FA (there would be nowhere to send the code) and signs out
every session.
Change your password
POST /api/account/password
Needs current_password. Then new_password, and optionally confirm (if you
send it, it has to match).
This revokes your API key. A password change signs out every session and deletes your key, so the key making this call stops working the instant it succeeds. Generate a new one afterwards.
{
"status": "password_changed",
"note": "your API key and all sessions were revoked; generate a new key"
}
Delete your account
POST /api/account/delete
Needs password. Returns 204 and removes the account and everything in it.
There is no undo.
Set up an authenticator app
GET /api/account/2fa/setup
{
"secret": "JBSWY3DPEHPK3PXP",
"otpauth_url": "otpauth://totp/EyalSec:alice?secret=…",
"expires_in_seconds": 600,
"note": "call /api/account/2fa/enable with a code from this secret to activate it"
}
The secret is held for you server-side for 10 minutes. Turn otpauth_url into a
QR code, or type secret into your app by hand. Calling this again starts over
with a fresh secret.
Turn on the authenticator app
POST /api/account/2fa/enable
Needs password, and totp: a code generated from the secret you just got.
Note the difference: that totp proves you hold the authenticator, it is not a
second factor on this request. The password is the re-auth.
Returns your backup codes, shown once:
{
"status": "2fa_enabled",
"backup_codes": ["…", "…"],
"note": "backup codes are shown once; store them now. All sessions were revoked."
}
A wrong code returns 422 and enables nothing. Calling this without doing setup first returns 409.
Turn off the authenticator app
POST /api/account/2fa/disable
Needs password. Signs out every session, so your other devices have to log in
again.
New backup codes
POST /api/account/2fa/backup/regenerate
Needs password. Replaces any codes you already had, so the old ones stop
working. Shown once. Requires the authenticator app to be set up (409 if not).
Email second factor
POST /api/account/2fa/email/enable
POST /api/account/2fa/email/disable
Both need password. Enabling requires a recovery email on file, since that is
where the code is sent (409 without one).
Look at your API key
GET /api/account/apikey
{
"exists": true,
"key_prefix": "es2_3f9a…",
"scope": "full",
"created_at": "2026-07-01T09:00:00Z",
"expires_at": "2027-01-01T00:00:00Z",
"last_used_at": "2026-07-16T06:20:00Z",
"last_ip": "203.0.113.4"
}
Useful for checking whether a key is still in use, and from where. It never returns the key itself: only a hash is stored.
Generate a key
POST /api/account/apikey/generate
Needs password, plus totp when 2FA is on. Optional expires (a future
YYYY-MM-DD; omit for a key that never expires) and read_only (any non-empty
value).
This replaces your current key. You get one key per account, so generating a new one kills the one you are calling with. Copy
api_keyfrom the response or you will lock yourself out.
{
"api_key": "es2_NEWKEY…",
"note": "shown once, and it replaces any previous key for this account"
}
A past expires returns 422 and creates nothing.
Revoke your key
POST /api/account/apikey/revoke
Needs password. Returns 204. If you revoke the key you are calling with,
that call is the last thing it does.
Billing portal link
GET /api/account/billing/portal
Returns the URL behind the dashboard's Manage billing button:
{ "portal_url": "https://customer-portal.paddle.com/…" }
404 when there is no active subscription.
What is not here
- Signing out has no API equivalent. Keys do not have sessions, so there is nothing to sign out of. Revoke the key instead.
- Confirming a recovery-email change happens by clicking the link in your inbox. Proving you own an address is not something a key can shortcut.
Rate limits
Every response that hits a limit returns 429 with a Retry-After header, and
a JSON body.
| Limit | Applies to |
|---|---|
| 300 requests/minute per API key | Everything you call with a key. Counted against your account, so making a new key does not reset it. |
| 300 requests/minute per IP address | All traffic from one address, keyed or not. |
| 120 requests/minute | POST /api/get_all_events_with_count/, the heaviest read. |
| 10 requests/minute | Anything under /api/account/ that asks for your password. |
The per-key and per-IP limits are counted separately and both apply, so whichever runs out first is the one you feel.
Getting your password wrong repeatedly locks the account. Five wrong attempts in a row and it locks for a minute, doubling up to 15 minutes. This is the same counter the login page uses, so a locked account is locked everywhere. While it is locked you get 429 even with the right password.
Agent endpoints
These endpoints are used automatically by es-python on your monitored machines; you normally never call them by hand. They're listed here for completeness. They authenticate with the machine's secret, not your API key.
Report an event
POST /api/create_event/ (alias: POST /events)
The runtime sends a Secret: <machine-uuid> header and the event fields
base64-encoded: where, repr, trace, location, and origin. The
server always responds 202 Accepted; it's fire-and-forget. Events are
aggregated and counted server-side, which is why the dashboard shows counts
rather than one row per occurrence (see Events).
Fetch machine configuration
GET /api/machine_config
The runtime sends the Secret header and receives the machine's encrypted
configuration (its taint settings and rules). The response is conditional:
the agent sends an X-Config-Version header and gets 304 Not Modified when
nothing has changed. es-python polls this at startup and roughly every 30
seconds (see Running es-python).
Install bootstrap (public)
POST /install.sh
POST /install/payload
These serve the self-extracting installer when you run the install command from
the Machines page. They're public and always reachable so a new
machine can bootstrap, and they support HTTP Range requests so an interrupted
download can resume with curl -C -. The public
GET /api/supported_os_types/ (documented under Machines) is also
used here to list the available targets.
Errors
The API uses standard HTTP status codes. API-key requests always return a JSON
body of the form {"error": "…"} rather than an HTML page or a redirect.
| Status | Meaning |
|---|---|
| 400 | Bad request: malformed JSON, an invalid regex, or an unparseable cursor or timestamp. |
| 403 | Forbidden: failed CSRF check, missing or invalid auth, a read-only key trying to change something, a capability that is not enabled for your account (Raise rules, source filtering), or a write on an expired trial. |
| 404 | Not found: the object doesn't exist. This is also returned to hide objects that belong to another user. |
| 409 | Conflict: the action clashes with current state (machine already pending/installed, or the machine limit is reached). |
| 422 | Unprocessable: a required field is missing or invalid (e.g. an unknown os_type or python_version, a name longer than 128 characters, or no OS set). |
| 500 | Server error: an unexpected failure on our side, including build or blob-storage problems. |
A 401 Unauthorized is returned specifically when an API key is bad, revoked, or
expired (see Authentication).
Glossary
A plain-English reference for the terms used throughout this guide. Each one is also defined where it first appears, but this is the quick lookup.
Untrusted data (taint)
Data that came from the outside world rather than from your own program, for example bytes read off a network socket, the contents of a file someone else can write, or text typed on standard input. EyalSec calls this untrusted data, and its internal name for it is taint. The two words mean the same thing. EyalSec tracks untrusted data as it flows through your program so it can tell when that data is about to be used in a dangerous way.
Source
Where untrusted data entered your program. EyalSec recognises five sources:
- socket: data received over the network,
- file: data read from a file,
- stdin: data read from standard input,
- env: environment variables and command-line arguments, and
- foreign: code loaded from a file that another user can write (see foreign code below).
You choose which sources EyalSec watches per machine; see Machine configuration.
Sink
A risky action: the dangerous thing untrusted data might end up doing. Common sinks are running a system command, executing code, or opening a file path built from outside input. EyalSec records (or blocks) the moment untrusted data reaches a sink. The pairing is the whole idea: a source brings data in, a sink could do harm with it, and EyalSec watches the path between them.
Report
The default behavior: when untrusted data reaches a sink, EyalSec records an event on your dashboard and lets your program keep running. Nothing about how your app behaves changes. See Report mode vs Report and Raise mode.
Raise
The blocking behavior: instead of just recording, EyalSec stops the risky
action by throwing a Python RuntimeError at that exact spot, so the dangerous
operation never happens. Raise must be enabled on your account; see
Plans & roles.
Modes 1 / 2 / 3 / 4
The four "modes" a dashboard rule can have:
- Mode 1, Hide (UI): a display rule that hides matching events from your views.
- Mode 2, Show (UI): a display rule that keeps matching events visible.
- Mode 3, Raise (machine): tells the runtime to block matching events (Raise behavior). Mode 3 needs Raise enabled on your account.
- Mode 4, Don't send (drop): tells the machine not to send matching events at all; they are never recorded.
Modes 1 and 2 only change what you see on the dashboard; they never affect what's collected or how your program runs. Modes 3 and 4 change what the machine does. Each rule also has a polarity (whitelist or blacklist) that decides whether the mode acts on the matching events or on everything except them. See Machine configuration.
Severity
A per-event rating (critical, high, medium, low, or info) computed on the server from what the event shows, so the rows that deserve attention stand out. Shown as a colored word on the Events page, and filterable there and in the API.
Socket-only build
A slimmer build of the es-python runtime that watches network data only: the file, stdin, foreign, and env instrumentation is compiled out. It is no longer selectable when adding a machine (new machines always get the full build), but some older machines still run it and show a socket-only badge. See Machines.
Origin
The file metadata of the data's source: details about the file the untrusted data came from, such as its type, permissions, size, owner, and timestamps. Origin helps you judge whether a file should have been trusted in the first place. It appears in an event's detail pane.
Foreign code
Python code loaded from a file that another user on the machine can write to.
If a file you import or run could be modified by someone other than you, that's a
risk: they could change what your program does. When foreign-code detection is
on, loading such a file emits a foreign-code:<path> event, and under Raise it
aborts the load. See Running es-python.
public_id vs secret
Every machine has two identifiers:
- public_id: a safe, shareable handle for the machine. It's what the dashboard and API use to refer to a machine. Showing it to someone reveals nothing dangerous.
- secret: the machine's private token, used by
es-pythonto prove which machine it is when it talks to the server. The secret is never shown in the dashboard or returned by the API. Treat it like a password.
Build version
A short label identifying exactly which build of the es-python runtime a machine
is running. The dashboard compares the build installed on a machine against the
latest available build and shows an "up to date" or "update available" badge. To
take a newer build, reinstall the machine; see Machines.
API key
A token (it starts with es2_) that lets a program talk to the dashboard's JSON
API on your behalf, without a browser login. You generate one in
Settings; it's shown only once, so save it somewhere
safe. An API key has the same access your account does.
CVE
Short for Common Vulnerabilities and Exposures: the industry-standard naming
scheme for publicly known security flaws (for example, CVE-2021-44228). When
people talk about a specific known vulnerability, they usually refer to it by its
CVE identifier.
FAQ & troubleshooting
Quick answers to the questions that come up most. If yours isn't here, see Getting help.
Do I have to change my code?
No. You run the same programs the same way, just with es-python instead of
python. EyalSec watches from the runtime; your source code stays exactly as it
is.
Will Report and Raise mode break my app?
Only if untrusted data actually reaches a risky action. For normal traffic
nothing changes; Report and Raise mode only steps in at the exact moment something dangerous
is about to happen, and it does so by throwing a normal Python RuntimeError. If
your code already catches exceptions around that operation, the attack is blocked
and your app can carry on. Many people start in Report mode
and switch to Raise once they're confident in what EyalSec is detecting.
I don't see any events. What's wrong?
Check these things in order:
- You ran your program with
es-python, not plainpython. - The machine shows as installed (green) on the Machines page.
- You don't have a filter or rule hiding the events. Check
Filters and the Events filter toolbar, and remember
that new accounts start with two default "send only matching" drop rules
(patterns
reandwrite); events that match neither are dropped at the machine until you edit or remove those rules (see Filters). - The sources you care about are switched on for that machine. Taint sources are off until you enable them; see Machine configuration. Remember that taint changes take effect on the next process restart.
How do I go back to normal Python?
Just use python as you always have. EyalSec is added alongside it, so your
original Python is always still there. Switching between python and es-python
takes nothing more than which command you type.
How do I update a machine to a newer build?
When the dashboard shows an "update available" badge on a machine, you update
it by reinstalling it. On the Machines page, open the machine's
⋮ menu and choose Reinstall. That mints a fresh install command for you to
run on the host, which pulls down the latest es-python build. Your machine's
internal identity is unchanged, so all your existing events stay attributed to it;
nothing is lost. Reinstalling is the normal, expected way to take a newer build.
Why can't I create a Raise rule?
Raise rules (mode-3 rules that block an action at runtime) must be enabled on your account. If they aren't, you'll see:
Raise rules are not enabled for your account. Ask an administrator to enable them.
Report mode works for every account;
only blocking (Raise) needs the capability turned on. To have it enabled, email
sales@eyalsec.com. See Plans & roles.
My trial ended and the dashboard is read-only. What now?
A new self-signup account gets a 24-hour trial clock that starts when you verify
your email. Once it runs out the dashboard goes read-only: you can still browse
everything, but creating or changing things is blocked, and your events view is
frozen at the moment the trial ended. To unlock full access, email
sales@eyalsec.com. As soon as an admin lifts the trial or adjusts your
account, the clock clears and write access comes back. See
Plans & roles.
How do I use the API?
EyalSec has a JSON API that mirrors what you can do in the dashboard. To use it:
-
Generate an API key in Settings. It starts with
es2_and is shown only once, so copy it somewhere safe. -
Send it on each request in the
X-API-Keyheader, like this:curl -H "X-API-Key: es2_3f9a…" https://eyalsec.com/api/get_all_machines/
API-key requests always return JSON and never redirect to a login page. The key carries the same access your account has, so keep it private. For the full list of endpoints, see the API reference.
I lost my 2FA device. How do I log in?
When you set up two-factor authentication, EyalSec showed you a set of backup
codes (in the form xxxxx-xxxxx). Each one works exactly once in place of the
6-digit app code on the two-factor screen, and they're not case-sensitive. Use one
to get in, then go to Settings to re-enroll your
authenticator on your new device and regenerate a fresh set of backup codes. If
you've also run out of backup codes and can't get in at all, email
support@eyalsec.com. See Getting help.
Getting help
Still stuck, or want to talk to a person? We're here.
- Support: questions, problems, or anything that isn't working the way you expect: email support@eyalsec.com.
- Upgrades: for more machines, a higher event quota, or Report and Raise mode: email sales@eyalsec.com. See Plans & roles for what your account includes.
Before you write in, the FAQ & troubleshooting section answers the most common questions, and the Glossary explains any term you're unsure about. If you do reach out, telling us which machine and roughly when something happened helps us help you faster.