Security

What Ezy protects against by construction, and what it doesn't try to.

Process execution

run "some string" is tokenized with Python's shlex.split and executed via subprocess.run(argv, shell=False, ...). No shell is ever invoked, so shell metacharacters (; && | ` backticks, $()) inside the string are treated as literal argument text, not interpreted — there is no command-injection path through run "literal string" alone.

The risk that remains: if a script builds the command string by concatenating untrusted input (run "cat " + user_input), shlex.split will still split on whitespace/quotes inside user_input, which can change argument boundaries even though it can't invoke a shell. run "cmd" with arguments [...] avoids this entirely — the argument list is passed to the process untouched, one list element per argument, with no re-parsing. Prefer it whenever a command is built from anything other than a fixed literal.

Filesystem

File operations act on whatever path string the script provides, resolved the same way Python's open() resolves it. Ezy does not sandbox script filesystem access to a project directory — a script can read or delete anything the OS-level user running ezy can. This matches the trust model of Bash or Python scripts run directly; it is not a sandbox, and should be reviewed with the same care before running with elevated privileges.

Missing parent directories are created automatically by write/append/create file, which is convenient but can create directory structure the caller didn't expect. Symlinks are followed without special handling — there is no symlink-attack detection yet.

HTTP

Modules

Local module imports (use "./utils") only load a file the script author names explicitly, resolved relative to the importing script's own directory. There is no remote/package-registry import path yet, which removes an entire class of supply-chain risk that a real package manager would need to address before shipping.

What was not built, and why that's security-relevant