Skip to content
Draft
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
1 change: 1 addition & 0 deletions SHELL_FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Blocked features are rejected before execution with exit code 2.
- ✅ `ping [-c N] [-W DURATION] [-i DURATION] [-q] [-4|-6] [-h] HOST` — send ICMP echo requests to a network host and report round-trip statistics; `-f` (flood), `-b` (broadcast), `-s` (packet size), `-I` (interface), `-p` (pattern), and `-R` (record route) are blocked; count/wait/interval are clamped to safe ranges with a warning; multicast, unspecified (`0.0.0.0`/`::`), and broadcast addresses (IPv4 last-octet `.255`) are rejected — note: directed broadcasts on non-standard subnets (e.g. `.127` on a `/25`) are not blocked without subnet-mask knowledge
- ✅ `ps [-e|-A] [-f] [-p PIDLIST]` — report process status; default shows current-session processes; `-e`/`-A` shows all; `-f` adds UID/PPID/STIME columns; `-p` selects by PID list
- ✅ `printf FORMAT [ARGUMENT]...` — format and print data to stdout; supports `%s`, `%b`, `%c`, `%d`, `%i`, `%o`, `%u`, `%x`, `%X`, `%e`, `%E`, `%f`, `%F`, `%g`, `%G`, `%%`; format reuse for excess arguments; `%n` rejected (security risk); `-v` rejected
- ✅ `read [-r] [-p PROMPT] [-d DELIM] [-n N] [-N N] [-t SECS] [NAME...]` — read one delimited chunk from stdin and assign each IFS-split field to a shell variable (defaulting to `REPLY` when no NAME is given); `-n`/`-N` are capped at 1 MiB; non-raw mode interprets `\<delim>` as line continuation and `\<X>` as `X`; `-p` is suppressed unless stdin is a terminal (matches bash); `-a` (array), `-s` (silent), `-u` (read from FD), `-e` (readline), and `-i` (initial text) are not implemented
- ✅ `sed [-n] [-e SCRIPT] [-E|-r] [SCRIPT] [FILE]...` — stream editor for filtering and transforming text; uses RE2 regex engine; `-i`/`-f` rejected; `e`/`w`/`W`/`r`/`R` commands blocked
- ✅ `strings [-a] [-n MIN] [-t o|d|x] [-o] [-f] [-s SEP] [FILE]...` — print printable character sequences in files (default min length 4); offsets via `-t`/`-o`; filename prefix via `-f`; custom separator via `-s`
- ✅ `tail [-n N|-c N] [-q|-v] [-z] [FILE]...` — output the last part of files (default: last 10 lines); supports `+N` offset mode; `-f`/`--follow` is rejected
Expand Down
63 changes: 49 additions & 14 deletions analysis/symbols_builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,32 @@ var builtinPerCommandSymbols = map[string][]string{
"strings.ReplaceAll", // 🟢 replaces all occurrences of a substring; pure function, no I/O.
"strings.ToLower", // 🟢 converts string to lowercase; pure function, no I/O.
},
"read": {
"context.CancelFunc", // 🟢 cancellation function returned by context.WithTimeout; pure type.
"context.Context", // 🟢 deadline/cancellation plumbing; pure interface, no side effects.
"context.DeadlineExceeded", // 🟢 sentinel error value for context deadline expiry; pure constant.
"context.WithTimeout", // 🟢 derives a timeout-bound context for read -t; no I/O itself.
"errors.Is", // 🟢 error comparison; pure function, no I/O.
"fmt.Errorf", // 🟢 error formatting; pure function, no I/O.
"fmt.Fprint", // 🟠 writes the -p prompt to callCtx.Stderr; no filesystem access, delegates to Write.
"fmt.Sprintf", // 🟢 string formatting; pure function, no I/O.
"io.EOF", // 🟢 sentinel error value; pure constant.
"io.Reader", // 🟢 interface type; no side effects.
"os.ErrDeadlineExceeded", // 🟢 sentinel error returned by *os.File when SetReadDeadline fires; pure constant.
"os.File", // 🟠 *os.File type used for type-asserting callCtx.Stdin to access SetReadDeadline/Stat; no constructors invoked.
"os.ModeCharDevice", // 🟢 file mode bit constant for character devices (TTY detection for read -p); pure constant.
"strconv.ParseFloat", // 🟢 string-to-float conversion (parses -t TIMEOUT seconds); pure function, no I/O.
"strings.ContainsRune", // 🟢 checks if a rune is in IFS; pure function, no I/O.
"time.Duration", // 🟢 duration type; pure integer alias, no I/O.
"time.Second", // 🟢 constant representing one second; no side effects.
"time.Time", // 🟢 time value type; pure data, no side effects.
"unicode/utf8.DecodeLastRuneInString", // 🟢 decodes last UTF-8 rune in a string (trailing IFS-whitespace strip); pure function, no I/O.
"unicode/utf8.DecodeRune", // 🟢 decodes first UTF-8 rune from a byte slice; pure function, no I/O.
"unicode/utf8.DecodeRuneInString", // 🟢 decodes first UTF-8 rune from a string; pure function, no I/O.
"unicode/utf8.FullRune", // 🟢 reports whether a byte slice begins with a complete UTF-8 rune; pure function, no I/O.
"unicode/utf8.RuneSelf", // 🟢 first byte value above which UTF-8 multi-byte encoding begins; pure constant.
"unicode/utf8.UTFMax", // 🟢 maximum number of bytes in a UTF-8 encoding; pure constant.
},
"sort": {
"bufio.NewScanner", // 🟢 line-by-line input reading (e.g. head, cat); no write or exec capability.
"context.Context", // 🟢 deadline/cancellation plumbing; pure interface, no side effects.
Expand Down Expand Up @@ -401,20 +427,23 @@ var builtinPerCommandSymbols = map[string][]string{
}

var builtinAllowedSymbols = []string{
"bufio.NewScanner", // 🟢 line-by-line input reading (e.g. head, cat); no write or exec capability.
"bufio.Scanner", // 🟢 scanner type for buffered input reading; no write or exec capability.
"bufio.SplitFunc", // 🟢 type for custom scanner split functions; pure type, no I/O.
"bytes.Buffer", // 🟢 in-memory buffer to capture command output; no I/O side effects.
"bytes.Equal", // 🟢 compares two byte slices for equality; pure function, no I/O.
"bytes.IndexByte", // 🟢 finds a byte in a byte slice; pure function, no I/O.
"bytes.NewReader", // 🟢 wraps a byte slice as an io.Reader; pure in-memory, no I/O.
"context.Context", // 🟢 deadline/cancellation plumbing; pure interface, no side effects.
"context.WithTimeout", // 🟢 creates a child context with a deadline; no filesystem or network I/O itself.
"errors.As", // 🟢 error type assertion; pure function, no I/O.
"errors.Is", // 🟢 error comparison; pure function, no I/O.
"errors.New", // 🟢 creates a simple error value; pure function, no I/O.
"fmt.Errorf", // 🟢 error formatting; pure function, no I/O.
"fmt.Sprintf", // 🟢 string formatting; pure function, no I/O.
"bufio.NewScanner", // 🟢 line-by-line input reading (e.g. head, cat); no write or exec capability.
"bufio.Scanner", // 🟢 scanner type for buffered input reading; no write or exec capability.
"bufio.SplitFunc", // 🟢 type for custom scanner split functions; pure type, no I/O.
"bytes.Buffer", // 🟢 in-memory buffer to capture command output; no I/O side effects.
"bytes.Equal", // 🟢 compares two byte slices for equality; pure function, no I/O.
"bytes.IndexByte", // 🟢 finds a byte in a byte slice; pure function, no I/O.
"bytes.NewReader", // 🟢 wraps a byte slice as an io.Reader; pure in-memory, no I/O.
"context.CancelFunc", // 🟢 cancellation function returned by context.WithTimeout/WithCancel; pure type, no side effects beyond context tree.
"context.Context", // 🟢 deadline/cancellation plumbing; pure interface, no side effects.
"context.DeadlineExceeded", // 🟢 sentinel error value for context deadline expiry; pure constant.
"context.WithTimeout", // 🟢 creates a child context with a deadline; no filesystem or network I/O itself.
"errors.As", // 🟢 error type assertion; pure function, no I/O.
"errors.Is", // 🟢 error comparison; pure function, no I/O.
"errors.New", // 🟢 creates a simple error value; pure function, no I/O.
"fmt.Errorf", // 🟢 error formatting; pure function, no I/O.
"fmt.Fprint", // 🟠 writes to a writer (e.g. callCtx.Stderr for read -p prompts); no filesystem access, delegates to Write.
"fmt.Sprintf", // 🟢 string formatting; pure function, no I/O.
"github.com/DataDog/rshell/internal/version.Version", // 🟢 build version string; read-only package-level variable, no I/O.
"github.com/prometheus-community/pro-bing.NewPinger", // 🔴 creates an ICMP pinger by resolving host; network I/O is the explicit purpose of the ping builtin.
"github.com/prometheus-community/pro-bing.NoopLogger", // 🟢 no-op logger that discards pro-bing internal messages; no side effects.
Expand Down Expand Up @@ -468,8 +497,11 @@ var builtinAllowedSymbols = []string{
"net.ParseIP", // 🟢 parses an IP address string into a net.IP; pure function, no I/O.
"net.Interface", // 🟢 OS network interface descriptor; read-only struct, no network connections.
"net.Interfaces", // 🟠 read-only OS interface enumeration function; no network connections or writes.
"os.ErrDeadlineExceeded", // 🟢 sentinel error value for *os.File read/write deadline expiry; pure constant.
"os.File", // 🟠 *os.File type, used for type-asserting callCtx.Stdin to access SetReadDeadline/Stat (e.g. read -t timeout, TTY detection); no constructors invoked.
"os.FileInfo", // 🟢 file metadata interface returned by Stat; no I/O side effects.
"os.IsNotExist", // 🟢 checks if error is "not exist"; pure function, no I/O.
"os.ModeCharDevice", // 🟢 file mode bit constant for character devices (TTY detection); pure constant.
"os.O_RDONLY", // 🟢 read-only file flag constant; cannot open files by itself.
"os.PathError", // 🟢 error type for filesystem path errors; pure type, no I/O.
"path/filepath.Dir", // 🟢 returns the directory component of a path; pure function, no I/O.
Expand Down Expand Up @@ -532,9 +564,12 @@ var builtinAllowedSymbols = []string{
"unicode.Range32", // 🟢 struct type for 32-bit Unicode ranges; pure data.
"unicode.RangeTable", // 🟢 struct type for Unicode range tables; pure data.
"unicode.Zs", // 🟢 Unicode space separator category range table; pure data, no I/O.
"unicode/utf8.DecodeLastRuneInString", // 🟢 decodes the last UTF-8 rune from a string (used for trailing-IFS-whitespace stripping); pure function, no I/O.
"unicode/utf8.DecodeRune", // 🟢 decodes first UTF-8 rune from a byte slice; pure function, no I/O.
"unicode/utf8.DecodeRuneInString", // 🟢 decodes first UTF-8 rune from a string; pure function, no I/O.
"unicode/utf8.FullRune", // 🟢 reports whether a byte slice begins with a complete UTF-8 rune; pure function, no I/O.
"unicode/utf8.RuneError", // 🟢 replacement character returned for invalid UTF-8; constant, no I/O.
"unicode/utf8.RuneSelf", // 🟢 first byte value above which UTF-8 multi-byte encoding begins; constant, no I/O.
"unicode/utf8.UTFMax", // 🟢 maximum number of bytes in a UTF-8 encoding; constant, no I/O.
"unicode/utf8.Valid", // 🟢 checks if a byte slice is valid UTF-8; pure function, no I/O.

Expand Down
13 changes: 13 additions & 0 deletions builtins/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,19 @@ type CallContext struct {
// dir overrides the working directory for path resolution.
// Returns the command's exit code.
RunCommand func(ctx context.Context, dir string, name string, args []string) (uint8, error)

// SetVar assigns a value to a shell variable in the calling shell's
// scope. Returns an error if the value exceeds the per-variable size
// limit or if the total variable-storage cap would be exceeded.
// Used by builtins that mutate parent-shell state, such as read.
SetVar func(name, value string) error

// GetVar returns the value of a shell variable. The bool reports
// whether the variable was set; an unset variable returns ("", false).
// Used by builtins that need to consult shell state, such as read
// reading IFS for field-splitting.
GetVar func(name string) (value string, ok bool)

// Proc provides access to the proc filesystem for the ps builtin.
// The path is fixed at construction time and cannot be overridden by callers.
Proc *ProcProvider
Expand Down
Loading
Loading