test(auth): pin the loose-mode test to the mechanism that survived

No behavior change.

Round 7 removed the loose-mode `console.warn` from selection — it was
encryption-blind and duplicated the caveat printed moments later. This test
kept asserting `warn` was called with "0644" and kept passing, because the
caveat also contains the mode. So it was green while describing a code path
that no longer exists: named "warns once at selection", commented as if the
selection warning were the thing under test, and pinned to a substring loose
enough that either line would satisfy it.

Asserts the caveat's full sentence now, so it is fastened to the surviving
mechanism rather than to whichever line happens to mention the mode.

Also records what it took to reach this state at all, since it is not
obvious and I had it wrong: as the file's owner you *cannot* stage a
loose-mode descriptor, because `tightenSecretFilePermissions` repairs a 0644
file to 0600 during selection and the report is then correctly "owner-only".
It is only real for a file owned by another user or on a read-only mount,
which is why the chmod is mocked — standing in for a privilege a test cannot
drop.

Found by @mcp-inspector-v2-f1, who tried to screenshot the state for the PR
body and got "Owner-only permissions." instead.

Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-22 17:12:29 -04:00
commit 5f4a6ea09b

View file

@ -882,10 +882,19 @@ describe("the descriptor reports a mode it could not verify", () => {
});
describe("the descriptor reports a mode it could not tighten", () => {
it("carries looseMode into the descriptor and warns once at selection", async () => {
// The warning alone was not enough: the caveat under the secret field
// states the mode as fact, and on this box that statement was untrue
// while the browser had no way to know.
it("carries looseMode into the descriptor, and the caveat states the mode", async () => {
// Reaching this state at all requires the chmod to *fail*: as the file's
// owner you cannot stage it, because `tightenSecretFilePermissions`
// repairs a 0644 file to 0600 during selection and the descriptor then
// correctly reports owner-only. It is only real for a file owned by
// another user or on a read-only mount — hence the mocked chmod, which
// is standing in for a privilege we cannot drop inside a test.
//
// The warning is asserted through the *caveat* path deliberately.
// Selection used to warn here too, and that line was removed in round 7
// for being encryption-blind and duplicating this one — so asserting the
// caveat text is what keeps this test pinned to the surviving mechanism
// rather than passing on whichever line happens to mention the mode.
const filePath = path.join(tmpDir, "secrets.json");
process.env.MCP_INSPECTOR_SECRET_FILE = filePath;
process.env.MCP_INSPECTOR_SECRET_STORE = "file";
@ -925,7 +934,13 @@ describe("the descriptor reports a mode it could not tighten", () => {
await import("@inspector/core/auth/node/secret-store-selection.js");
const info = await mod.getSecretStorageInfo();
expect(info.looseMode).toBe(0o644);
expect(warn).toHaveBeenCalledWith(expect.stringContaining("0644"));
// The plaintext wording, in full: this file is unencrypted, so a
// reader really does get the secrets.
expect(warn).toHaveBeenCalledWith(
expect.stringContaining(
"mode 0644, not 0600, and could not be tightened — anyone who can read it can read the secrets in it",
),
);
} finally {
vi.doUnmock("node:fs/promises");
vi.doUnmock("@inspector/core/auth/node/secret-store.js");