What it is
Out of Danger is one small binary that you symlink under the names of the commands you want to guard. When it is invoked as rm, it reads its policy, decides, and then replaces itself with the configured real executable only if the operation is allowed. Anything doubtful is refused: a malformed policy, an unreadable lock file or an invalid real path stops the command rather than letting it through.
Use it to put a safety rail on the normal command path of a workstation or server, for example to stop a recursive delete of /, of the current directory, or of a tree you have marked as protected.
What it is not. It is not a security boundary. The real executable stays accessible, and running it directly (for example /bin/rm) bypasses the wrapper by design. It prevents mistakes made through the normal command path; it does not contain a hostile user.
Requirements
- A Rust toolchain with Cargo (the project builds with the stable toolchain; CI runs on Ubuntu and macOS).
- No third-party runtime or build dependencies.
- A POSIX system where commands are found through
PATH.
Build and install
Build from a clone of the public repository:
git clone https://github.com/afterdarksys/outofdanger.git
cd outofdanger
cargo build --releaseThe binary is target/release/outofdanger. Check it:
target/release/outofdanger version
target/release/outofdanger validate --config examples/outofdanger.confversion prints outofdanger 0.1.0 for the tagged release, and validate prints valid: 1 commands for the example policy.
Install without replacing system binaries
Do not replace operating-system binaries. Install the wrapper in its own directory and put the command symlinks in a dedicated directory earlier in PATH:
install -d "$HOME/.local/lib/outofdanger" "$HOME/.local/bin/outofdanger-wrappers"
install -m 0755 target/release/outofdanger "$HOME/.local/lib/outofdanger/outofdanger"
"$HOME/.local/lib/outofdanger/outofdanger" link \
--config "$PWD/examples/outofdanger.conf" \
--dir "$HOME/.local/bin/outofdanger-wrappers"Then set OUT_OF_DANGER_CONFIG to the policy you deployed and prepend the wrapper directory to PATH. link creates only missing links and refuses to replace any existing path.
For a system-wide install, use the same layout under /usr/local, keep each real path pointed at the operating-system executable, and deploy the policy as /etc/outofdanger.conf, the default location. Avoid renaming package-managed binaries: an upgrade can replace them and silently bypass or break the wrapper.
Quick start
Try a policy decision without installing anything. check evaluates and prints the decision; it never executes the command.
$ target/release/outofdanger check --config examples/outofdanger.conf --command rm -- -rf /
deny: rm (built-in guard recursive-root (use `permit rm recursive-root` to override))
$ echo $?
10
$ target/release/outofdanger check --config examples/outofdanger.conf --command rm -- /tmp/scratch.txt
allow: rm (command default)Replace check with run to evaluate and then execute the real command when it is allowed. Once the symlinks are on PATH, a plain rm goes through the same decision.
The policy file
The policy is a plain text file. The default path is /etc/outofdanger.conf; OUT_OF_DANGER_CONFIG or --config overrides it. The example in the repository:
# Every command needs an absolute real path and an explicit default.
# Rules are evaluated top-to-bottom; the first match wins.
# `*` matches within one argument and `**` matches zero or more arguments.
command rm /bin/rm
default rm allow
protect rm /etc
protect rm /var/lib
# Optional controls:
# max-operands rm 20
# guard rm recursive-cwd
# lock-file rm /etc/outofdanger.STOPRequired lines
command NAME ABSOLUTE_REAL_PATHnames a guarded command and the real executable it runs.default NAME allow|denyis the decision when no rule matches.
Argument rules
Ordered allow and deny rules take the form ACTION NAME ARG_PATTERN..., and the first matching rule wins. * and ? match characters inside one argument; the token ** matches zero or more complete arguments. Values may be single- or double-quoted, and # starts a comment outside quotes.
Safety controls
These run before the ordered argument rules, so an allow rule cannot bypass them.
| Line | Effect |
|---|---|
| (built in) | Recursive removal of /, //, relative paths that resolve to root and equivalent spellings is denied. Only permit rm recursive-root disables this guard. |
protect rm PATH | Denies removing the directory itself or anything under it, and recursive removal of its ancestors. Relative operands and existing symlink prefixes are resolved, including missing leaf paths. PATH must be absolute. |
max-operands rm N | Denies removing more than N operands, recursive or not. Flags do not count; arguments after -- do. |
guard rm recursive-cwd | Denies recursive removal of the current directory or any ancestor, such as rm -rf .. |
lock-file NAME PATH | Denies every invocation of NAME while PATH exists, including as a dangling symlink. Remove the file to resume. Use a dedicated path in a directory you control. |
The tree, operand-count and working-directory controls support rm only. Other commands use argument rules and the optional lock file. The names outofdanger and ood are reserved for the management interface.
Commands and exit codes
outofdanger check [--config PATH] --command NAME -- ARGS...
outofdanger run [--config PATH] --command NAME -- ARGS...
outofdanger validate [--config PATH]
outofdanger commands [--config PATH]
outofdanger link [--config PATH] --dir PATH
outofdanger help
outofdanger versioncheckprintsallowordenywith the reason, and never executes.runevaluates, then executes the real command if allowed.validatechecks policy syntax and every real command target without executing them. Missing targets, non-executable files, directories, and links back to the wrapper (including hard links) fail.commandslists each configured command, its real path and its default.linkcreates a symlink per configured command in--dir, and refuses to replace anything that already exists.
An allowed check exits 0. A denied operation exits 10. Configuration and usage errors exit 2.
Limits
- The built-in root guard and the path and count guards understand
rm. Other commands get argument rules and emergency locks. - Argument globs are lexical. They do not resolve symlinks, shell expansions, working-directory-relative paths or command-specific meaning. Use
protectfor filesystem trees. - Only invocations that resolve to a wrapper symlink on
PATHare controlled. - Resolution is conservative: a symlink operand can be denied even when
rmwould only unlink the link. Paths can change between evaluation and execution, so this is not atomic filesystem enforcement. validatechecks executable mode bits, not ACLs or mount-level execution restrictions.
How it fits with the other tools
Out of Danger is standalone. It does not read or write the state of the other farm tools. It guards the commands an operator types on one machine. vpscfgfarm is the gate for commands sent to a fleet, and oos manages disk cleanup with its own refusal rules.
Links
- Full README on GitHub
- Example policy
- Tagged versions (v0.1.0; no packaged release downloads)
- MIT license
- Issue tracker
- Security policy: report a bypass privately through GitHub's private vulnerability reporting, not as a public issue.
Built from the public outofdanger repository (README.md, SECURITY.md, examples/outofdanger.conf, src/main.rs help text) at commit 411a1fc.