A validwhile store is untrusted input. One predicate type, command_exit_code, runs a
command you supply. If you could import someone else's store and have it run their commands,
validwhile import would be a remote code execution primitive with a friendly name.
So it is off, twice over:
| Situation | Runs? |
|---|---|
| Command predicate you wrote, no flags | No, reported suspect |
Command predicate you wrote, --allow-commands |
Yes |
Command predicate from validwhile import, no flags |
No |
Command predicate from validwhile import, --allow-commands only |
No |
Command predicate from validwhile import, --allow-commands --allow-imported-commands |
Yes |
The fourth row is the one that matters. Turning commands on for predicates you wrote must not silently turn them on for whatever arrived in somebody's JSONL file. Those are different decisions and they take different flags.
This is enforced in src/validwhile/predicates/command.py and tested in
tests/test_security_commands.py. Those tests assert on a canary file: the command under
test writes a file, and the test asserts the file does not exist. That is a proof that nothing
ran, rather than an inspection of a message that claims nothing ran.
validwhile import from-a-stranger.jsonl # marks every record `imported`, warns loudly
validwhile check --dry-run # lists what WOULD be touched, runs nothing
validwhile list --json | less # read the claims and the contracts--dry-run names every URL that would be fetched and every command that is present, tagged
with its origin, and executes none of them.
- Read outside your project. File, checksum and dependency predicates on an
importedrecord are confined to the project root.../../../.ssh/id_rsais refused and reported assuspectwith a reason. A record you wrote yourself may point anywhere; you authored it. - Set its own working directory outside the root. Same rule, same refusal.
- Read local files over
file://. URL predicates accepthttpandhttpsonly. - Declare itself trustworthy.
originis overwritten withimportedon the way in, whatever the file says.
Say this plainly, because a half-described mitigation is worse than none:
--allow-commandsis not a sandbox. An allowed command runs as you, with your environment, your filesystem and your network. The flag is a decision point, not a jail. If you need isolation, runvalidwhile checkin a container.shell=Falsenarrows the blast radius, it does not remove it. Commands are executed as an argv list with no shell, so; rm -rf ~is inert punctuation rather than a second command. But["curl", "http://…", "-o", "…"]is a perfectly ordinary argv, and it will run if you allowed commands.- Claim and reason strings are attacker-controlled. They are printed to your terminal and
written into your JSON. Terminal escape sequences in a claim are not stripped. Pipe
untrusted output through
less -Ror read it as JSON. - The store is a local SQLite file with no encryption and no access control. It has the permissions of the directory it sits in. Do not put secrets in a claim string; claims are meant to be read by humans and pasted into issues.
- Network predicates leak the fact that you are checking. A
url_etagcheck is an ordinary HTTP request from your machine, with avalidwhile/0.1user agent. Whoever runs that server sees your IP. - No signing. There is no way to verify who wrote a store. Treat any store you did not create as anonymous input.
Open a private security advisory: https://github.com/jimmyjames177414/validwhile/security/advisories/new
Please do not open a public issue for anything that lets an imported store execute code, read
outside the project root, or make an unevaluable check report valid. Those three are the
whole security model.
Expect an acknowledgement within a week. This is a small project maintained in spare time; there is no paid response commitment and no bounty, and pretending otherwise would be its own kind of dishonesty.
Only the latest release. v0.1 is alpha.