feat(secrets): 0.6.2 — the content-root rejection message teaches the container case

Suggested by the OtOpcUa session, the only real containerized deployment in this family
and the one that hit the false positive. A store on a volume mounted inside the content
root is durable across container replacement but is rejected anyway, because the rule
cannot tell a mount from an ordinary directory. The message now says so, gives the fix,
and forecloses the wrong one by stating that no override exists.

A false positive's entire cost is the confusion in the moment, so an error that names its
own fix costs nothing on the true-positive path and turns the false positive into a
two-minute edit. Message only, no behaviour change.

Their case for keeping the rule strict is recorded in the CHANGELOG because it is better
than the trade-off I had written: the workaround is an improvement rather than a
concession, an override flag has the lifecycle of a knob set once and never revisited,
and mount detection would make the verdict depend on runtime platform state so the same
path would pass on one host and fail on another.
This commit is contained in:
Joseph Doherty
2026-08-11 09:04:02 -04:00
parent 74066bbe27
commit c5e894d9b3
4 changed files with 56 additions and 2 deletions
@@ -159,6 +159,29 @@ public sealed class SecretsStorePathRulesTests
Assert.Contains(result.Failures!, f => f.Contains("content root", StringComparison.Ordinal));
}
[Fact]
public void The_rejection_message_teaches_the_container_case()
{
// A false positive's entire cost is the confusion in the moment, and the known one is a
// store on a volume mounted inside the content root — durable in a container, rejected
// anyway, because the rule cannot tell a mount from an ordinary directory. A boot error
// that names its own fix costs nothing on the true-positive path and turns that false
// positive into a two-minute edit. Asserted rather than left to the doc comment, because
// the message is the only part of this a stuck operator will actually read.
var env = new StubHostEnvironment { ContentRootPath = Root("app") };
var validator = new SecretsOptionsValidator(env);
ValidateOptionsResult result =
validator.Validate(name: null, OptionsWith(Root("app", "data", "secrets.db")));
Assert.True(result.Failed);
string message = Assert.Single(result.Failures!);
Assert.Contains("mounted volume", message, StringComparison.OrdinalIgnoreCase);
Assert.Contains("OUTSIDE the content root", message, StringComparison.Ordinal);
// And that it forecloses the wrong fix, rather than leaving the reader to hunt for a flag.
Assert.Contains("not look for an override", message, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void Validator_accepts_an_absolute_path_outside_the_content_root()
{