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
- TLS certificate verification is always on. There is no
with insecureflag, by design. - Headers (including
Authorization) are sent exactly as given and are never logged by the interpreter itself. - A non-2xx response does not raise; it comes back as
response.ok == falsewith the status and body still available, so scripts aren't tempted to swallow errors with a broadtryjust to inspect a 404. - Connection errors, timeouts, and TLS failures are caught and
returned as
response.ok == falsewithresponse.errordescribing the failure, rather than crashing the script — but never silently treated as success.
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
- No package manager. Processing third-party code is a significant trust boundary — signature verification, lock files, registry compromise. Shipping a fake one would be worse than not having one; see Limitations.
- No sandboxing of filesystem/process/network access per script. This matches how Bash, Python, and Ruby scripts already work — it's stated here plainly rather than implied away. Ezy's security work concentrates on how operations are performed (no shell injection, TLS always verified, no credential logging), not on isolating a script from the machine it runs on.