Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/controllers/llmo/llmo.js
Original file line number Diff line number Diff line change
Expand Up @@ -1421,6 +1421,20 @@ function LlmoController(ctx) {
}
// CDN routing — only when cdnType is provided
if (cdnTypeNormalized) {
// Guard: the AEMCS Fastly routing API operates at domain level only.
// Enabling it for a subpath site would intercept all traffic on the host,
// not just the intended subpath. Reject until path-scoped routing lands (LLMO-4579).
try {
const siteUrlForGuard = baseURL.startsWith('http') ? baseURL : `https://${baseURL}`;
const siteUrlObj = new URL(siteUrlForGuard);
if (siteUrlObj.pathname && siteUrlObj.pathname !== '/') {
log.error(`[edge-optimize-routing-failed] ${baseURL} Subpath site cannot use host-level auto-routing`);
return badRequest('Automated CDN routing is not yet supported for subpath sites. Path-scoped routing requires a Fastly API update (LLMO-4579).');
}
} catch {
// Malformed URL — let the subsequent probe step surface the error
}

// Exchange promise token from cookie for an IMS user token
let imsUserToken;
try {
Expand Down
18 changes: 18 additions & 0 deletions test/controllers/llmo/llmo.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5811,6 +5811,24 @@ describe('LlmoController', () => {
expect((await result.json()).message).to.include('not available in stage');
});

it('returns 400 when site baseURL has a non-root path (subpath site)', async () => {
mockSite.getBaseURL = sinon.stub().returns('https://example.com/docs');
const result = await controller.createOrUpdateEdgeConfig(makeRoutingCtx());
expect(result.status).to.equal(400);
expect((await result.json()).message).to.include('subpath sites');
expect(mockLog.error).to.have.been.calledWith(
sinon.match(/Subpath site cannot use host-level auto-routing/),
);
expect(callCdnRoutingApiStub).to.not.have.been.called;
});

it('does not reject when site baseURL has a trailing slash only', async () => {
mockSite.getBaseURL = sinon.stub().returns('https://example.com/');
const result = await controller.createOrUpdateEdgeConfig(makeRoutingCtx());
expect(result.status).to.not.equal(400);
expect(callCdnRoutingApiStub).to.have.been.called;
});

it('returns 500 when EDGE_OPTIMIZE_ROUTING_CONFIG is invalid JSON', async () => {
const result = await controller.createOrUpdateEdgeConfig(
makeRoutingCtx({ env: { ENV: 'prod', EDGE_OPTIMIZE_ROUTING_CONFIG: 'not-json' } }),
Expand Down
Loading