Skip to content

Commit 645e41a

Browse files
committed
fix(@angular/ssr): ensure public directory containment in CommonEngine
Ensure that paths resolved in `retrieveSSGPage` strictly remain within the configured `publicPath` by checking `relative()` containment before evaluating the static file. Previously, a string `startsWith()` check was used, which could match sibling directories that share the same name prefix as `publicPath`.
1 parent d3aaa48 commit 645e41a

1 file changed

Lines changed: 8 additions & 6 deletions

File tree

packages/angular/ssr/node/src/common-engine/common-engine.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { ApplicationRef, StaticProvider, Type } from '@angular/core';
1010
import { BootstrapContext } from '@angular/platform-browser';
1111
import { renderApplication, renderModule, ɵSERVER_CONTEXT } from '@angular/platform-server';
1212
import * as fs from 'node:fs';
13-
import { dirname, join, normalize, resolve } from 'node:path';
13+
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
1414
import { URL } from 'node:url';
1515
import { validateUrl } from '../../../src/utils/validation';
1616
import { getAllowedHostsFromEnv } from '../environment-options';
@@ -162,16 +162,18 @@ export class CommonEngine {
162162
// See: https://portswigger.net/web-security/file-path-traversal
163163
const pagePath = join(publicPath, pathname, 'index.html');
164164

165+
const relativePath = relative(publicPath, pagePath);
166+
const isOutside =
167+
relativePath === '..' || relativePath.startsWith('../') || relativePath.startsWith('..\\');
168+
if (isOutside || isAbsolute(relativePath)) {
169+
return undefined;
170+
}
171+
165172
if (this.pageIsSSG.get(pagePath)) {
166173
// Serve pre-rendered page.
167174
return fs.promises.readFile(pagePath, 'utf-8');
168175
}
169176

170-
if (!pagePath.startsWith(normalize(publicPath))) {
171-
// Potential path traversal detected.
172-
return undefined;
173-
}
174-
175177
if (pagePath === resolve(documentFilePath) || !(await exists(pagePath))) {
176178
// View matches with prerender path or file does not exist.
177179
this.pageIsSSG.set(pagePath, false);

0 commit comments

Comments
 (0)