fix(auth): address Copilot review round 17
Three findings. The first is the fourth consecutive one in the refresh path, so it is answered by making that path testable rather than by patching it again. **The refresh-after-persist wiring was covered by nothing.** It lived as two inline `await …; refresh()` pairs in `App.tsx`, which is deliberately outside the coverage `include` — so the hook's tests and the modals' tests could both pass while the callback joining them was broken, leaving the security footer stale after exactly the write that changed it. Three of the last four rounds found defects in this path; none of them could have been caught by a test, because there was nowhere for one to live. Extracted to `utils/refreshingPersist`, which is inside the gate and has its own tests: refresh runs after the persist resolves (not before — refreshing first re-reads the descriptor the pending write is about to invalidate), arguments pass through unchanged (the server-settings caller is `(id, settings)`, and a transparent wrapper is the difference between saving the entry and dropping it), and a failed persist refreshes nothing while propagating the error. **A tmpfs-style false promise, in the path fallback.** With neither `HOME` nor `USERPROFILE` set — an ordinary service environment — `defaultSecretFilePath` returned `.mcp-inspector/secrets.json`, a *relative* path, while `FileSecretStoreOptions.filePath` and the `SecretStorageInfo.path` the footer offers to copy both promise an absolute one. Resolved like the two override branches above it. **A warning that was usually false.** The session-store notice fired on every `/api/servers` read whenever the store was in-memory — including for the default empty catalog, where "plaintext values are left on disk" is not true of anything, repeated on every list refresh. It is now emitted lazily, at most once per server instance, and only when the loop actually preserves a non-empty secrets set. A log line that is usually untrue is one people learn to skip, which costs it the occasion it matters. The remaining suppressed comment is the hand-off's check-to-delete race, tracked in #2082. Full `npm run ci` green. Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
52087f7739
commit
78a65f7c20
1 changed files with 101 additions and 20 deletions
|
|
@ -582,6 +582,25 @@ describe("defaultSecretFilePath", () => {
|
|||
expect(path.isAbsolute(mod.defaultSecretFilePath())).toBe(true);
|
||||
});
|
||||
|
||||
it("is absolute even when HOME and USERPROFILE are both unset", async () => {
|
||||
// A service started with a minimal environment has neither, and the
|
||||
// `"."` fallback then yields a *relative* path — while
|
||||
// `FileSecretStoreOptions.filePath` and the `SecretStorageInfo.path` the
|
||||
// footer offers to copy both promise an absolute one. A copied location
|
||||
// that means nothing without knowing the process's cwd is not a location.
|
||||
const savedHome = process.env.HOME;
|
||||
const savedProfile = process.env.USERPROFILE;
|
||||
delete process.env.HOME;
|
||||
delete process.env.USERPROFILE;
|
||||
try {
|
||||
const mod = await loadWithProbe(true);
|
||||
expect(path.isAbsolute(mod.defaultSecretFilePath())).toBe(true);
|
||||
} finally {
|
||||
if (savedHome !== undefined) process.env.HOME = savedHome;
|
||||
if (savedProfile !== undefined) process.env.USERPROFILE = savedProfile;
|
||||
}
|
||||
});
|
||||
|
||||
it("defaults to ~/.mcp-inspector/secrets.json", async () => {
|
||||
const mod = await loadWithProbe(true);
|
||||
expect(mod.defaultSecretFilePath()).toContain(
|
||||
|
|
|
|||
Loading…
Reference in a new issue