picosnitch

picosnitch helps protect your security and privacy by "snitching" on anything that connects to the internet, letting you know when, how much data was transferred, and to where. It uses BPF to monitor network traffic per application, and per parent to cover those that just call others. It also hashes every executable, and will complain if some mischievous program is giving it trouble.

Open Source

picosnitch Source Code

Author

elesiuta

Description

Monitor network traffic per executable

#bandwidth-monitor#ebpf#intrusion-detection#linux#monitoring#network-analysis#network-monitor#network-monitoring#network-traffic#network-visualization#networking#privacy#security

Homepage

https://elesiuta.github.io/picosnitch/

License

GPL-3.0

Created

15 Jul 20

Last Updated

11 Jul 26

Latest version

v2.2.1

Primary Language

C

Size

5,397 KB

Stars

962

Forks

38

Watchers

962

Language Usage

Language Usage

Star History

Star History

Recent Commits

  • Eric Lesiuta (11 Jul 26)

    release: 2.2.1

  • Eric Lesiuta (11 Jul 26)

    fix: relax two over-strict constraints introduced in 2.2.0 - build: unpin the hatchling build backend. `python -m build --no-isolation` validates the exact pin against the system hatchling, so the AUR and nixpkgs source builds (hatchling 1.30.1) failed with "Missing dependencies: hatchling==1.31.0". - config: allow data.mode up to 0o7777 instead of 0o777, so a valid setgid/ sticky mode is no longer rejected and silently reset to 0644.

  • Eric Lesiuta (11 Jul 26)

    release: 2.2.0

  • Eric Lesiuta (11 Jul 26)

    tests, docs, and release tooling - cover the virustotal upload bounds/hash-verify, live-feed subscriber cap, config validation, privilege-drop gid handling, and control-char neutralization - document the remote-SQL schema/grants and configuration knobs - reference CI actions by tag, pin release tooling, fix debian build deps, gitignore uv.lock

  • Eric Lesiuta (11 Jul 26)

    ui: harden dashboard rendering and the geoip download - curses and web output map Unicode control/format/surrogate chars to a visible placeholder so a tampered name can't spoof or overwrite cells - webui adds a DNS-rebinding Host allowlist from the bound socket, closes SSE on feed end, clamps huge time windows, and validates the port - geoip download streams with a 64 MiB cap

  • Eric Lesiuta (11 Jul 26)

    config: validate all knobs against a hostile config.toml Validate and clamp BPF/perf sizes, owner/group/user/mode, and ignore_* lists so a malformed config can't crash-loop the daemon. retention_days / write_limit_seconds / request_limit_seconds reject only negatives (a negative retention wipes the db); no upper bound.

  • Eric Lesiuta (11 Jul 26)

    fix: capture-path correctness and robustness (bpf, monitor, secondary) - read real_parent for ancestry; copy the DNS hostname with probe_read_user_str; DNS tid map is an LRU hash - verify the /proc/pid/exe inode against the event before trusting the readlink; thread mod_cnt through the sha256 reads for correct cache invalidation - LRU-bound the domain/inode caches, errors="replace" on /proc reads so a non-UTF-8 comm can't abort a drain batch, gate optional hooks on kallsyms symbol presence - cap live-feed subscribers and drop slow ones without stalling the publisher

  • Eric Lesiuta (11 Jul 26)

    security: harden privilege drops, daemon boundaries, and root file handling - the four dropped subprocesses always drop root: close the inherited fanotify fd first, resolve the desktop user's primary gid from passwd, and fall back to nobody when it is unset/invalid or would be uid/gid 0 - virustotal bounds direct uploads to 32 MiB, verifies the file hash before sending, caps JSON responses, and uploads only on a genuine 404 - secure config/run/data dirs (root-owned, config.toml 0600) and reject symlink/hardlink/FIFO swaps before opening any data path - locale-robust start/stop/restart; start-no-daemon clears a stale pidfile instead of refusing to run

  • Eric Lesiuta (08 Jul 26)

    fix(tests): ty-ignore the fake State/Queue args in test_virustotal

  • Eric Lesiuta (08 Jul 26)

    security: harden privileged program execution - relaunch_argv: only re-exec an absolute argv0, else fall back to `sys.executable -m picosnitch`; pass cwd="/" to the re-exec spawns so the -m fallback doesn't put an inherited cwd on sys.path[0] - resolve clang/bpftool/llvm-strip and systemctl against standard system dirs before $PATH, to a fixed absolute path - load libc via ctypes.CDLL(None) and libbpf via absolute-path/soname instead of find_library(), which can fall back to running gcc/objdump/ld from $PATH - notifications: always drop privileges (desktop user, else nobody) so notify-send never runs with root privileges

  • Eric Lesiuta (08 Jul 26)

    fix(bpf): read real_parent for ancestry; copy the DNS hostname with probe_read_user_str - parent is the SIGCHLD recipient (the tracer when ptraced); real_parent is the creator, so a debugger can no longer masquerade as a process's ancestry - a fixed-size probe_read_user faults (dropping the DNS mapping) when a short hostname sits within 80 bytes of an unmapped page; _str stops at the NUL - add an ExecEvent struct byte-sync regression test

  • Eric Lesiuta (08 Jul 26)

    fix(secondary): strip carriage returns from conn.log fields A lone \r in an attacker-influenced field (name/cmdline/domain) could forge or overwrite a CSV log line; strip it alongside comma/newline/NUL.

  • Eric Lesiuta (08 Jul 26)

    fix(remote-sql): dedup executables on a key_hash, not the raw TEXT columns UNIQUE(exe, name, cmdline, sha256) lost batches containing a real cmdline: MySQL prefix indexes deduped on 191-char prefixes so the full-value id SELECT found no row (crash), and Postgres rejected a long incompressible cmdline whose index tuple exceeded its 2704-byte btree limit. (SQLite has neither limit, so the local schema is unaffected.) Add a key_hash column (sha256 of the 4-tuple) and resolve ids on it. Remote schema changed: drop the old remote tables to recreate.

  • Eric Lesiuta (08 Jul 26)

    fix(config): reject a scalar where a list is expected A scalar ignore_ports/ignore_domains/ignore_ips (e.g. ignore_ports = 443) slipped past the generic-type skip and raised TypeError in the secondary's filters every write cycle, halting logging and growing new_processes unbounded.

  • Eric Lesiuta (08 Jul 26)

    fix(ui): neutralize control chars in curses output; fix a latched exclude filter - _safe_addnstr maps every C0/DEL to '?': curses acts on \r \n \t \b as cursor moves, so a control char in a hostile process name could overwrite or spoof rows - clear add_filter_exclude in the follow-through so pressing 'e' on an empty result list no longer latches and turns the next plain filter into an exclude - drop the dead last-seen timestamp in top's totals rows

  • Eric Lesiuta (08 Jul 26)

    fix(webui): validate Host, fail loudly, consistent errors, real connection counts - reject non-loopback Host headers on a loopback bind (DNS-rebinding guard) - bind failure -> clear error + exit 1; invalid PICOSNITCH_PORT warns - catch OverflowError in the window parser; unknown dim -> 400 JSON, not 500 - swallow client-disconnect writes in the do_GET catch-all - aggregate returns a connections count; the overview column shows it (was em-dash) - drop write-only tr.dataset.event

  • Eric Lesiuta (08 Jul 26)

    fix(cli): harden start/stop/restart, systemd interplay, and shutdown - stop returns failure (exit 1) when the daemon doesn't exit in time; restart aborts - start-no-daemon clears a stale pidfile instead of refusing to start - treat a systemd unit that is "activating" (Restart backoff) as active too - systemd command off systemd fails cleanly instead of a raw traceback - suggest a runnable sudo command (relaunch_argv), not a bare __main__.py path - suspend/resume: let the monitor drain before SIGTERM; wake the loop on SIGTERM/SIGINT

  • Eric Lesiuta (08 Jul 26)

    fix(virustotal): only upload on a 404, not on auth/rate-limit/network errors The bare except treated every lookup failure as "file absent from VT", so a 401/429/timeout triggered a file upload -- worsening rate limits and shipping the binary to VirusTotal on a transient error.

  • Eric Lesiuta (08 Jul 26)

    fix(monitor): verify /proc/pid/exe inode before trusting an exec-race readlink A process that re-exec'd between the event and get_fd() was mislabeled and poisoned the fd/comm caches. Blank proc_exe when its (dev, ino) no longer matches the event; keep it when the event carried no inode (st_ino == 0).

  • Eric Lesiuta (08 Jul 26)

    fix(bpf): gate optional hooks on kallsyms symbol presence, not address kptr_restrict=2 zeroes kallsyms addresses even for root, so the addr != 0 check silently disabled every optional hook (inet6 send/recv, tcp/mptcp splice_read) on hardened boxes -- dropping all IPv6 and spliced-recv bytes. A listed name is proof enough of existence; only tcp_read_sock's actor filter needs the real io_zcrx_recv_skb address, so it alone stays disabled (with a clearer message) when the address is unavailable.

  • Eric Lesiuta (08 Jul 26)

    fix: harden monitor drain, db id-cache rollback, dns dict growth - monitor: decode kernel-supplied comm/pcomm/gpcomm with errors="replace" and isolate per-entry drain failures. drain_map lookup_and_delete's every entry from the kernel before returning, so a process with a non-UTF-8 comm doing network I/O raised UnicodeDecodeError mid-drain -- dropping the rest of the (now unrecoverable) batch every interval and crashing the monitor at the unguarded teardown drain. Same treatment for the /proc text reads (comm/status/stat/cmdline): a zombie with a non-UTF-8 comm raised out of _read_tgid_comm dropping its drain entry, a non-UTF-8 argv blanked the whole cmdline, and a non-UTF-8 Name: line misreported uid as 0. - secondary: make the exe/addr/domain id caches transaction-aware. The rowid of an INSERT was cached inside `with con:`; on a transient write failure the transaction rolls back and frees that rowid, but the cache kept it, so a retry (or any later exe reusing the rowid) silently misattributed connections to the wrong executable. Drop a failed transaction's cache entries so the retry re-inserts. - monitor: read domain_dict via .get() so peer-IP lookups no longer auto-vivify permanent "" entries (unbounded growth on a long-running daemon); now a plain dict, defaultdict was only load-bearing via that bug.

  • Eric Lesiuta (08 Jul 26)

    fix(bpf): locate libc for getaddrinfo uprobe on all distros find_library/ldconfig/hardcoded-FHS missed non-FHS layouts (nix/guix), so the DNS-name uprobe silently fell back to reverse-DNS there. Map the runtime address of getaddrinfo to its file in /proc/self/maps instead: the real loaded path, correct on glibc/musl/nix alike with no guessing.

  • Eric Lesiuta (06 Jul 26)

    fix(docs): capture the TUI executable view instead of the splash The TUI parses the DB-IP GeoIP CSV (~700k rows) on launch, before the first real draw, which can block for up to 10s. The tapes' fixed `Sleep` raced that, so the first screenshots/frames captured the "loading database" splash. - Bump VHS 0.7.2 -> 0.11.0 for the `Wait` command and replace the fixed splash sleeps in tui.tape and demo.tape with `Wait+Screen /Executable/`, which blocks until the real view renders regardless of load time or runner speed. - README: link each hero image to its full-size PNG again. They're now bundled with the docs site rather than release assets that forced a download, so a direct link just opens the image. - screenshots.md: add the missing "Group by executable" TUI screenshot.

  • Eric Lesiuta (05 Jul 26)

    release: 2.1.2

  • Eric Lesiuta (05 Jul 26)

    add favicon for the docs site and webui

  • Eric Lesiuta (04 Jul 26)

    fix: FUSE hash correlation + non-UTF-8 config; trim dead code - fuse: include _mod_cnt in the hash reply-correlation key so a post-modification re-hash isn't served a stale pre-mod reply - config: fall back to defaults on a non-UTF-8 config.toml - drop dead code (SendRecv{,6}Event, 5 unused libbpf sigs, urllib.error import, no-op except socket.timeout, dummy tui thread); unlink(missing_ok=True) - test: pin conn_key/conn_val byte layout; cover both fixes

  • Eric Lesiuta (02 Jul 26)

    build: drop stale gentoo ebuild

  • Eric Lesiuta (02 Jul 26)

    fix(db): purge orphaned executables in retention, like domains/addresses - executables interns (exe, name, cmdline, sha256); a distinct cmdline is a distinct row, so high argv cardinality (per-invocation tokens) grew it unbounded -- domains/addresses were purged of orphans but executables were not - measured ~377 B/row incl. index: negligible for typical systems (tens of MB), but ~GB/yr under pathological per-command argv; this bounds it to the same retention window as connections - add one DELETE mirroring the existing orphan purges (startup-only, id=0 sentinel kept); test asserts orphan dropped, referenced + sentinel survive

  • Eric Lesiuta (02 Jul 26)

    fix(bpf): set _layout_="ms" on packed ctypes structs - Python 3.19 makes the implicit _pack_ layout an error; set it explicitly to "ms" (the current default for _pack_ structs), silencing the 3.14 DeprecationWarning now - "ms" == the existing layout: sizes and field offsets are byte-identical, so the kernel struct mirrors are unchanged - attribute is inert on 3.12/3.13 (verified: tight-packing unchanged)

  • Eric Lesiuta (02 Jul 26)

    fix(notify): point notify-send at the desktop user's D-Bus session bus - after dropping root to desktop.user the notifier kept root's env, so notify-send had no XDG_RUNTIME_DIR / DBUS_SESSION_BUS_ADDRESS and every notification silently failed to reach the user's session bus - set both to the systemd-logind standard /run/user/<uid>[/bus] for that uid, plus HOME from the passwd entry - this is also the only bus path notify-send's AppArmor profile (abstractions/dbus-session-strict) permits - verified end-to-end: real notify-send delivers to a service on the session bus with the env set, and fails without it

picosnitch Website

Website

picosnitch

Per-executable network bandwidth monitoring for Linux

Redirects

Redirects to https://elesiuta.github.io/picosnitch/

Security Checks

1 security checks failed (64 passed)

  • Risky Category Detected

Server Details

  • IP Address 185.199.108.153
  • Hostname cdn-185-199-108-153.github.com
  • Location California, Pennsylvania, United States of America, NA
  • ISP GitHub Inc.
  • ASN AS54113

Categories

Some proxies may block this service, as it falls into the following categories

  • Free Hosting

Associated Countries

  • US US

Safety Score

Website marked as moderately safe

90%

Blacklist Check

elesiuta.github.io was found on 0 blacklists

  • AntiSocial Blacklist
  • Artists Against 419
  • Badbitcoin
  • Bambenek Consulting
  • CERT Polska
  • CoinBlockerLists
  • CRDF
  • CryptoScamDB
  • EtherAddressLookup
  • EtherScamDB
  • Fake Website Buster
  • MetaMask EthPhishing
  • NABP Not Recommended Sites
  • OpenPhish
  • PetScams
  • PhishFeed
  • PhishFort
  • Phishing.Database
  • PhishStats
  • PhishTank
  • Phishunt
  • RPiList Not Serious
  • Scam.Directory
  • SecureReload Phishing List
  • Spam404
  • StopGunScams
  • Suspicious Hosting IP
  • ThreatFox
  • ThreatLog
  • TweetFeed
  • URLhaus
  • ViriBack C2 Tracker

Website Preview

Website preview

picosnitch Reviews

More Intrusion Detection

About the Data: picosnitch

API

You can access picosnitch's data programmatically via our API. Simply make a GET request to:

https://api.awesome-privacy.xyz/v1/services/picosnitch

The REST API is free, no-auth and CORS-enabled. To learn more, view the API Docs or read the API Usage Guide.

Share picosnitch

Help your friends compare Intrusion Detection, and pick privacy-respecting software and services.
Share picosnitch and Awesome Privacy with your network!

View Intrusion Detection (5)