BOA fork of lshell, version 0.11.9 — the first tagged 0.11 release on this repository (the previous public tag is 0.10.10.1). Base: upstream ghantoos/lshell 0.11.1, merged 2026-09-02; every BOA change since is below, newest first. The tarball attached is what BOA installs (lshell-0.11.9.tar.gz, md5 eb89bd53b0ad72c499edf4849fb7cce2).
v0.11.9 (BOA fork, 2026-09-10)
- Security: the Landlock exemption is decided for the whole line.
is_exemptread the first word of the joined pipeline, soping -c 1 host | composer install(bothpasswdandpingare exempt setuid tools in BOA's config,|is not forbidden) ran composer and every process it spawned with no ruleset at all, the same line-level hole 0.11.8 closed for the noexec layer. A line is exempt only when every one of its segments is; in practice a single exempt command. - A kernel refusal of the ruleset at exec time (
landlock.restrictraisesOSErrorinsidepreexec_fn, re-raised in the parent with the child's own type) is handled like every other containment failure (denied, exit 126) instead of ending the session with a traceback. - The prompt honours
$LPS1again as the whole prompt (the 0.11.1 merge dropped the override the man page and sample config document),prompt_short : 2renders the path it is given, and the BOAprompt_short : 1shapes ([dir],[sites@client]) are what the suite asserts. landlock_ro,landlock_rwandlandlock_exempttake the+ [...]/- [...]merge like every other list key; a per-group override in that syntax used to refuse the login.- The typed line, not the
LD_PRELOAD-prefixed one, is stored as a job's command (jobs) and logged on a containment failure, asexec_cmd's own contract says. - README: the log carries the Landlock rule counts and refused symlink targets, not the rules themselves.
v0.11.8 (BOA fork, 2026-09-07)
- Security: the noexec layer is applied per pipeline segment, in the line itself. 0.11.7 handed the library over in
LSHELL_NOEXECwith a decision taken per LINE (anysegment inallowed_shell_escape=> nothing handed over), sofind ... -exec ... | trueran find without the preload: one shell-escape segment anywhere in a pipeline disarmed the layer for the whole line (measured on a BOA box through a real tenant session). Nowcmd_parse_executeprefixes every segment whose executable is outsideallowed_shell_escapewithLD_PRELOAD=<library>, a POSIX assignment prefix the shell applies to that one command only, and leaves shell-escape segments as typed:drush status | grep xpreloads grep and not drush,find ... | truepreloads find. A segment led by a POSIX special builtin is never prefixed (the assignment would persist), nor one led by alandlock_exemptsetuid tool,sudo/suor an SFTP protocol binary (the loader ignoresLD_PRELOADfor setuid binaries, and those names are read as the command by the exempt test and the privileged branch);exec_cmdnow takes the typed line asdisplayand uses it for every message, log line and decision, so only the shell ever sees the prefix;landlock.is_exemptskips leadingVAR=valuewords (LANG=C passwdstays exempt); a user's ownLD_PRELOAD=is still refused; with a usablepath_noexecthe upstream whole-line environment form applies instead.LSHELL_NOEXECis gone; no dispatcher support is needed, the shell that finally runs the line does the work.
v0.11.7 (BOA fork, 2026-09-07)
- Security: the noexec layer is back for commands outside
allowed_shell_escape. It could not be preloaded on the shell commands run through (0.11 runs every command as[exec_shell, "-c", cmd], and a workingsudo_noexec.soon that shell stops it executing anything), so the library found byset_noexecis now kept asnoexec_libraryand handed to the command inLSHELL_NOEXEC; a dispatcher that knows the variable (BOA's websh) appliesLD_PRELOADto the command it finally launches, sofind -exec, an editor's shell escape or GNU sed'secan no longer exec anything, while a plain shell ignores the variable and behaves as before. Shell-escape commands never carry it, and a value already in the session environment is dropped at every launch, so it cannot ride into a shell-escape command's children.
v0.11.6 (BOA fork, 2026-09-07)
- Fix: say what the noexec check actually decided. A box that ships
sudo_noexec.sologgeddisabling incompatible noexec libraryfollowed bynoexec library not foundat every session -- both misleading: the library is present and working, and preloading it is refused only because 0.11 runs every command as[exec_shell, "-c", cmd], soLD_PRELOADlands on that shell and would stop it executing anything (measured on a BOA box: wget, openssl, grep, sed and find all exit 126). One accurate line is written instead,not foundis kept for a library that really is absent, and the probe carries the reason it must keep executing a binary, with unit tests that fail if it is turned into a builtin probe. - Fix:
path_noexec : '', the documented way to switchLD_PRELOADoff, silently made everyallowed_shell_escapecommand forbidden (the early return skipped the merge that folds that list intoallowed) and left an emptypath_noexecin the runtime config, whichutils.exec_cmdreads as "preload configured" and turns intoLD_PRELOAD=on every command. The list is merged and the key dropped. - Note: under 0.10 the preload was applied to the command process itself, so the noexec layer was real there. It is unavailable in the 0.11 execution model; confinement rests on Landlock, the allowed/forbidden lists and the dispatcher named by
exec_shell.
v0.11.5 (BOA fork, 2026-09-07)
- Security: only a root-owned symlink inside a user's
pathroot extends the Landlock read-write set. A link the user can create or move is ignored (owner and target are read from the one open link inode), the sharedlandlock_rwroots (/tmp,/var/tmp,/dev) are never scanned, configured roots are compared by their real paths, and a derived target that is/, a parent of a configured root, or alandlock_roroot or inside one is refused and logged. Before, any link in those places widened the confinement of every later shell session, up to a/rule that dissolved it, and a link into a read-only root silently turned it read-write.
v0.11.4 (BOA fork, 2026-09-06)
- Security: the per-user log file is created and kept
0600(owner only), as 0.10 did. 0.11 set0660at every session start; with the log directory shared by every restricted user and the file's group being the user's primary group, a neighbour in the same group could read the whole command history for the window between a login and the next0600re-assertion.
v0.11.3 (BOA fork, 2026-09-02)
- Fix: symlink targets are resolved three levels inside each read-write root (was two), so a per-user drush extension farm at
~/.drush/usr/<tool>pointing into the account tree keeps loading under Landlock (found on the first real-shell upgrade drill: drush8 failed to open a command file).
v0.11.2 (BOA fork, 2026-09-02)
- Feature: Landlock child-process confinement (
landlock,landlock_ro,landlock_rw,landlock_exempt,landlock_strict): every command and every process it spawns is confined by the kernel to the configured roots plus the user'spathentries and home, applied before exec and inherited, never liftable. Fails open with a log line on kernels without Landlock unless strict. - Feature:
exec_shell(default/bin/sh) names the shell commands run through, restoring the/bin/sh -cexecution of 0.10 so a dispatcher installed as/bin/shis honoured (upstream hardcodesbash -c). - BOA prompt shapes kept (
user:[dir]$,user:[sites@client]$).