mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-11-06 04:42:35 +00:00
Assert optInt's three edges and the path guard's positive case
Both checks could only be observed failing, so both could have been quietly inverted. `resolveWithinProfile` is `target !== root && !target.startsWith(root + "/")`. Turn that `&&` into `||` and every path except the profile root itself is refused — which the traversal cases could not see, because they expect a refusal either way. A check that refuses everything is not a working check, so the test now writes to a legitimate path inside the profile and confirms the file lands there. `optInt` guards length, period, digits, index, delay and the history target, and each of its three clauses was unasserted: a non-integer (1.5), a negative, and — the one that matters for the boundary — zero, which is NOT negative and must stay accepted. `n < 0` quietly becoming `n <= 0` would reject a clipboard delay of 0 and a derivation index of 0, both real choices. Records one thing found on the way and deliberately not fixed here: a nested export path answers 500 rather than a refusal naming the missing directory. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01Vtx94nXDmNgXEeThUZP4QN
This commit is contained in:
1 parent
bd17ad5eae
commit
2d27b5aaa9
2 files changed
+50
No files matched your search
@@ -201,6 +201,46 @@ describe("what the API refuses", () => {
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.json.detail).toContain("inside the profile directory");
|
||||
}
|
||||
|
||||
// The POSITIVE half. The guard is `target !== root && !target.startsWith(
|
||||
// root + "/")` — turn that `&&` into `||` and every path except the
|
||||
// profile root itself is refused, which the traversal cases above cannot
|
||||
// see because they expect a refusal either way. A check that refuses
|
||||
// everything is not a working check.
|
||||
const inside = await call("POST", "/api/v1/entry/0/document/export", {
|
||||
password: PASSWORD,
|
||||
// Flat, not nested: atomicWrite does not create parent directories,
|
||||
// and a nested path answers 500 rather than a clear refusal — recorded
|
||||
// in TODO.md rather than fixed inside a test for a different property.
|
||||
body: { path: "exported-secret.txt" },
|
||||
});
|
||||
expect(inside.status).toBe(200);
|
||||
expect(inside.json.path).toBe(
|
||||
join(app.profileDir(FINGERPRINT), "exported-secret.txt"),
|
||||
);
|
||||
expect(existsSync(inside.json.path)).toBe(true);
|
||||
});
|
||||
|
||||
it("validates optional integers at every edge, not just the obvious one", async () => {
|
||||
// optInt guards `length`, `period`, `digits`, `index`, `delay` and the
|
||||
// history `target`. Each of its three clauses was unasserted.
|
||||
for (const delay of [1.5, -1, "soon"]) {
|
||||
const res = await call("POST", "/api/v1/secret-mode", {
|
||||
password: PASSWORD,
|
||||
body: { enabled: true, delay },
|
||||
});
|
||||
expect(res.status).toBe(400);
|
||||
expect(res.json.detail).toContain("delay");
|
||||
}
|
||||
|
||||
// Zero is NOT negative, and optInt says non-negative. A delay of 0 is a
|
||||
// real choice, so `n < 0` must not quietly become `n <= 0`.
|
||||
const zero = await call("POST", "/api/v1/secret-mode", {
|
||||
password: PASSWORD,
|
||||
body: { enabled: true, delay: 0 },
|
||||
});
|
||||
expect(zero.status).toBe(200);
|
||||
expect((await call("GET", "/api/v1/config/clipboard_clear_delay")).json.value).toBe(0);
|
||||
});
|
||||
|
||||
it("refuses a non-loopback bind unless explicitly allowed", () => {
|
||||
|
||||
Reference in new issue
Block a user