mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-11 21:57:14 +03:00
Fix: reject root-relative .. segments and warn per scan
Dot-segment hrefs like /../outside.css could leave the project, and a process-wide warning set hid missing-sheet notices on later detectHtml calls. AI assistance: implemented with Cursor Grok 4.6. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -964,13 +964,14 @@ function buildStaticWindow(staticDoc) {
|
||||
};
|
||||
}
|
||||
|
||||
const warnedMissingStylesheets = new Set();
|
||||
|
||||
function resolveLinkedCssPath(fileDir, href) {
|
||||
const stripped = href.split(/[?#]/)[0];
|
||||
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
|
||||
if (!rootRelative) return path.resolve(fileDir, stripped);
|
||||
const rel = stripped.replace(/^\/+/, '');
|
||||
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
|
||||
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
|
||||
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
|
||||
const rel = segments.join(path.sep);
|
||||
let dir = fileDir;
|
||||
for (;;) {
|
||||
const parent = path.dirname(dir);
|
||||
@@ -990,6 +991,7 @@ function resolveLinkedCssPath(fileDir, href) {
|
||||
|
||||
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
|
||||
const styleTexts = [];
|
||||
const warnedMissingStylesheets = new Set();
|
||||
for (const styleEl of modules.selectAll('style', root.children || [])) {
|
||||
styleTexts.push(modules.domutils.textContent(styleEl));
|
||||
}
|
||||
|
||||
@@ -1358,6 +1358,19 @@ describe('detectHtml — static HTML/CSS engine', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('does not follow root-relative .. segments out of the page directory', async () => {
|
||||
await withStaticFixture({
|
||||
'project/package.json': '{}',
|
||||
'project/index.html': `<!DOCTYPE html><html><head>
|
||||
<link rel="stylesheet" href="/../outside.css">
|
||||
</head><body><div class="card">Card</div></body></html>`,
|
||||
'outside.css': '.card { border-left: 5px solid #3b82f6; border-radius: 4px; }',
|
||||
}, async ({ dir }) => {
|
||||
const f = await detectHtml(path.join(dir, 'project', 'index.html'));
|
||||
expect(findingIds(f)).not.toContain('side-tab');
|
||||
});
|
||||
});
|
||||
|
||||
test('warns when a linked stylesheet cannot be read', async () => {
|
||||
const writes = [];
|
||||
const origWrite = process.stderr.write.bind(process.stderr);
|
||||
@@ -1371,9 +1384,11 @@ describe('detectHtml — static HTML/CSS engine', () => {
|
||||
<link rel="stylesheet" href="/missing/app.css">
|
||||
</head><body><div>Page</div></body></html>`,
|
||||
}, async ({ file, dir }) => {
|
||||
await detectHtml(file);
|
||||
await detectHtml(file);
|
||||
const msg = writes.join('');
|
||||
expect(msg).toContain('could not read linked stylesheet /missing/app.css');
|
||||
const hits = msg.split('could not read linked stylesheet /missing/app.css').length - 1;
|
||||
expect(hits).toBe(2);
|
||||
expect(msg).toContain(`resolved to ${path.join(dir, 'missing', 'app.css')}`);
|
||||
});
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user