mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-14 23:26:39 +03:00
Add regression tests for token-authorized CORS on non-localhost dev hosts
Covers the fix for the ddev breakage reported in #304: the live server now reflects Access-Control-Allow-Origin for any request bearing the valid session token, so dev servers on loopback aliases (https://*.ddev.site, Valet's *.test, hosts-file entries) work again while tokenless remote origins stay blocked. The server/browser source changes shipped in b1c5707f; this adds the test coverage that was written alongside them: - tokenless remote origins get no ACAO on any route, token'd or not - a non-loopback origin with the valid token is reflected, with Vary: Origin, on both the real request and its OPTIONS preflight - the /manual-edit-stash source assertion tracks the token-bearing URL Prepared with AI assistance. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
f2f73edb33
commit
0f80c1f5aa
@@ -42,7 +42,7 @@ describe('live-browser source contracts', () => {
|
||||
);
|
||||
assert.match(
|
||||
SOURCE,
|
||||
/fetch\('http:\/\/localhost:' \+ PORT \+ '\/manual-edit-stash'[\s\S]{0,260}?pageUrl: location\.pathname[\s\S]{0,80}?element: extractContext\(contextElement\)[\s\S]{0,40}?ops,/,
|
||||
/fetch\('http:\/\/localhost:' \+ PORT \+ '\/manual-edit-stash\?token='[\s\S]{0,300}?pageUrl: location\.pathname[\s\S]{0,80}?element: extractContext\(contextElement\)[\s\S]{0,40}?ops,/,
|
||||
'Save should stage edits through /manual-edit-stash with element context and ops',
|
||||
);
|
||||
assert.match(
|
||||
|
||||
@@ -393,17 +393,17 @@ describe('live-server integration', () => {
|
||||
assert.ok(body.includes('__IMPECCABLE_LIVE_INIT__'), 'authorized /live.js returns the assembled bundle');
|
||||
});
|
||||
|
||||
it('CORS: a remote origin gets no Access-Control-Allow-Origin on any route', async () => {
|
||||
it('CORS: a tokenless remote origin gets no Access-Control-Allow-Origin on any route', async () => {
|
||||
const evil = 'https://evil.example';
|
||||
for (const path of ['/health', `/live.js?token=${server.token}`, `/status?token=${server.token}`]) {
|
||||
for (const path of ['/health', '/live.js', '/status', '/status?token=not-the-token']) {
|
||||
const res = await fetch(`http://localhost:${server.port}${path}`, { headers: { Origin: evil } });
|
||||
assert.equal(
|
||||
res.headers.get('access-control-allow-origin'),
|
||||
null,
|
||||
`remote origin must not be reflected on ${path}`,
|
||||
`tokenless remote origin must not be reflected on ${path}`,
|
||||
);
|
||||
}
|
||||
// Preflight from a remote origin is likewise unauthorized to read.
|
||||
// Preflight from a tokenless remote origin is likewise unauthorized to read.
|
||||
const preflight = await fetch(`http://localhost:${server.port}/poll`, {
|
||||
method: 'OPTIONS',
|
||||
headers: { Origin: evil, 'Access-Control-Request-Method': 'POST' },
|
||||
@@ -411,6 +411,28 @@ describe('live-server integration', () => {
|
||||
assert.equal(preflight.headers.get('access-control-allow-origin'), null);
|
||||
});
|
||||
|
||||
it('CORS: a non-loopback origin with the valid token is reflected (ddev-style dev hosts)', async () => {
|
||||
// A dev server on a loopback alias (https://my-site.ddev.site, *.test)
|
||||
// sends a non-loopback Origin, but its overlay requests carry the session
|
||||
// token — that token, not the origin, is the trust signal.
|
||||
const origin = 'https://my-site.ddev.site';
|
||||
const res = await fetch(`http://localhost:${server.port}/status?token=${server.token}`, {
|
||||
headers: { Origin: origin },
|
||||
});
|
||||
assert.equal(res.status, 200);
|
||||
assert.equal(res.headers.get('access-control-allow-origin'), origin);
|
||||
assert.ok(/\bOrigin\b/i.test(res.headers.get('vary') || ''), 'Vary: Origin accompanies the reflected origin');
|
||||
|
||||
// Preflight to the same token-bearing URL is authorized too (OPTIONS hits
|
||||
// the same URL, query string included).
|
||||
const preflight = await fetch(`http://localhost:${server.port}/poll?token=${server.token}`, {
|
||||
method: 'OPTIONS',
|
||||
headers: { Origin: origin, 'Access-Control-Request-Method': 'POST' },
|
||||
});
|
||||
assert.equal(preflight.status, 204);
|
||||
assert.equal(preflight.headers.get('access-control-allow-origin'), origin);
|
||||
});
|
||||
|
||||
it('CORS: a loopback origin is reflected with Vary: Origin', async () => {
|
||||
for (const origin of [
|
||||
`http://localhost:${server.port}`,
|
||||
|
||||
Reference in New Issue
Block a user