fix(web): state the expected shape when dropping a CSP source (#2064)
`approveCspSources` warned "dropping unsafe CSP source", which states a security verdict the check never makes: `SAFE_CSP_SOURCE` screens each entry for injection safety only, and deliberately does not bound how broad a grant is (`resourceDomains: ["*"]` passes). The four fields are origin lists, so the real cause of a drop is almost always that the entry isn't an origin — a CSP keyword such as `'unsafe-eval'` has no field in the contract to be requested through. Reading it as a safety verdict is what sent #2012 chasing an XSS regression that wasn't one. The message now names the field the entry came from (so an app declaring several lists can tell which one was bad) and states the shape expected, with no claim about the value's safety. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UL9retfvAXRgvi6EWk4SY4 Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
a92a73003b
commit
268bfa1085
1 changed files with 42 additions and 6 deletions
|
|
@ -67,6 +67,29 @@ describe("approveCspSources", () => {
|
|||
warn.mockRestore();
|
||||
});
|
||||
|
||||
it("names the offending field and states the expected shape, without calling the value unsafe", () => {
|
||||
// #2064: the filter screens for injection safety only, so the message must
|
||||
// not read as a verdict on the value. These four fields are origin lists,
|
||||
// so the real cause is almost always "not an origin" — a CSP keyword has
|
||||
// no field in the contract to be requested through. Naming the key also
|
||||
// tells an app declaring several lists which one the bad entry was in.
|
||||
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
approveCspSources({
|
||||
connectDomains: ["'unsafe-eval'"],
|
||||
frameDomains: ["<script>"],
|
||||
});
|
||||
const messages = warn.mock.calls.map(([message]) => String(message));
|
||||
expect(messages).toHaveLength(2);
|
||||
expect(messages[0]).toContain('"connectDomains"');
|
||||
expect(messages[1]).toContain('"frameDomains"');
|
||||
for (const message of messages) {
|
||||
expect(message).toContain("expected an origin");
|
||||
expect(message).not.toContain("unsafe");
|
||||
}
|
||||
expect(warn.mock.calls[0]?.[1]).toBe("'unsafe-eval'");
|
||||
warn.mockRestore();
|
||||
});
|
||||
|
||||
it("omits a key when every entry is rejected", () => {
|
||||
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
expect(approveCspSources({ connectDomains: ['"; x'] })).toEqual({});
|
||||
|
|
|
|||
Loading…
Reference in a new issue