A retention duration with unparsed characters is misread: --keep-newer-than 1.0y deletes every snapshot #215

Closed
opened 2026-10-06 01:49:40 +02:00 by clawbot · 1 comment
Collaborator

parseDuration (internal/vaultik/helpers.go:144-185) first tries time.ParseDuration. When that fails, it falls back to an unanchored regex, (\d+)\s*([a-zA-Z]+), which picks out whatever number-and-letter pieces it finds and ignores everything else. Measured on next at 0700901:

  • 1.0y, 30.0d and 10.0mo all become 0s.
  • 2.1w becomes 1 week, 1.5w becomes 5 weeks and 1,5y becomes 5 years.
  • 30 days ago and x7d are both accepted.

Trigger: vaultik snapshot create --prune --keep-newer-than 1.0y. The zero duration puts the cutoff at "now", so every snapshot of the backed-up names is selected, including the one just made (internal/vaultik/snapshot.go:571-581). The post-backup prune runs forced and quiet (snapshot.go:146,152), removes their metadata, and PruneBlobs deletes their blobs. The run ends "Finished successfully". snapshot purge --older-than 2.1w --force deletes two weeks more than asked for.

Acceptable: the README documents these flags as taking a number and a unit. An input that is not entirely a sequence of valid number-and-unit parts is an error, so nothing is deleted.

Definition of done

  1. parseDuration rejects any input containing characters that are not part of a valid number-and-unit sequence: decimals in the custom units, separators, and leading or trailing text.
  2. The table test in internal/vaultik covers 1.0y, 2.1w, 1,5y, 30 days ago and x7d as errors, and keeps every case it already has.
  3. make check passes.

Model: fable-5-1 (audit); opus-5-5 (issue)

`parseDuration` (`internal/vaultik/helpers.go:144-185`) first tries `time.ParseDuration`. When that fails, it falls back to an unanchored regex, `(\d+)\s*([a-zA-Z]+)`, which picks out whatever number-and-letter pieces it finds and ignores everything else. Measured on `next` at `0700901`: - `1.0y`, `30.0d` and `10.0mo` all become 0s. - `2.1w` becomes 1 week, `1.5w` becomes 5 weeks and `1,5y` becomes 5 years. - `30 days ago` and `x7d` are both accepted. Trigger: `vaultik snapshot create --prune --keep-newer-than 1.0y`. The zero duration puts the cutoff at "now", so every snapshot of the backed-up names is selected, including the one just made (`internal/vaultik/snapshot.go:571-581`). The post-backup prune runs forced and quiet (`snapshot.go:146,152`), removes their metadata, and `PruneBlobs` deletes their blobs. The run ends "Finished successfully". `snapshot purge --older-than 2.1w --force` deletes two weeks more than asked for. Acceptable: the README documents these flags as taking a number and a unit. An input that is not entirely a sequence of valid number-and-unit parts is an error, so nothing is deleted. ## Definition of done 1. `parseDuration` rejects any input containing characters that are not part of a valid number-and-unit sequence: decimals in the custom units, separators, and leading or trailing text. 2. The table test in `internal/vaultik` covers `1.0y`, `2.1w`, `1,5y`, `30 days ago` and `x7d` as errors, and keeps every case it already has. 3. `make check` passes. Model: fable-5-1 (audit); opus-5-5 (issue)
clawbot self-assigned this 2026-10-06 01:49:40 +02:00
Author
Collaborator

Fixed in #237: when Go cannot parse the duration, the input must now be whole numbers each followed directly by a unit, or it is an error. 1.0y, 2.1w, 1,5y, 30 days ago and x7d are all rejected.

Judgement call: 30 days (a space between number and unit) is now rejected too, matching Go units, where 30 s was already an error.

Model: opus-5-5

Fixed in https://git.eeqj.de/sneak/vaultik/pulls/237: when Go cannot parse the duration, the input must now be whole numbers each followed directly by a unit, or it is an error. `1.0y`, `2.1w`, `1,5y`, `30 days ago` and `x7d` are all rejected. Judgement call: `30 days` (a space between number and unit) is now rejected too, matching Go units, where `30 s` was already an error. Model: opus-5-5
Sign in to join this conversation.