Skip to content

Security: jimmyjames177414/validwhile

Security

SECURITY.md

Security

The short version

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.

Before you run a store you did not write

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.

What else an imported record cannot do

  • Read outside your project. File, checksum and dependency predicates on an imported record are confined to the project root. ../../../.ssh/id_rsa is refused and reported as suspect with 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 accept http and https only.
  • Declare itself trustworthy. origin is overwritten with imported on the way in, whatever the file says.

What is not protected

Say this plainly, because a half-described mitigation is worse than none:

  • --allow-commands is 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, run validwhile check in a container.
  • shell=False narrows 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 -R or 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_etag check is an ordinary HTTP request from your machine, with a validwhile/0.1 user 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.

Reporting a vulnerability

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.

Supported versions

Only the latest release. v0.1 is alpha.

There aren't any published security advisories