fix(security): unauth share_token leak (HIGH) + restore path-traversal, logo file-read, branding path keys (#946)
* fix(security): stop unauth share_token leak + block restore path-traversal, logo-path file read, branding path keys * test: update resolveLogoFile for the c7x5 containment (reject outside-storage absolute paths, keep inside) * fix(security): codex round-1 — escape LIKE wildcards in share-link resolve, keep in-storage absolute logos, guard restore verification - shareLinkService: escape %/_ in the link_partial LIKE fallback so an anonymous /resolve/____… wildcard can't match an arbitrary share_link and leak its bearer token (reopened GHSA-rh8r). Explicit ESCAPE for SQLite. - resolveLogoFile: re-add the raw absolute candidate but keep it subject to the storage-root containment filter (GHSA-c7x5) so legit in-storage absolute logos resolve while /etc/passwd stays rejected. - restoreService: apply the same pathEscapes guard in post-restore verification so a skipped traversal entry isn't fs.access'd/hashed. --------- Co-authored-by: Paul Nothaft <[email protected]>
This commit is contained in:
co-authored by
Paul Nothaft
parent
8cbb37310b
commit
9050affd8d
@@ -99,10 +99,22 @@ describe('resolveLogoFile', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('treats absolute paths as-is when they exist', async () => {
|
||||
it('rejects an absolute path OUTSIDE the storage roots (GHSA-c7x5)', async () => {
|
||||
// The raw-absolute candidate was an arbitrary-file-read primitive
|
||||
// (logo_path: '/etc/passwd' → rasterised into a PDF). Absolute paths
|
||||
// outside the storage roots are now dropped even if they exist.
|
||||
existsSpy.mockImplementation((p) => p === '/abs/path/logo.png');
|
||||
getAppSetting.mockResolvedValue(null);
|
||||
const out = await resolveLogoFile({ logo_path: '/abs/path/logo.png' });
|
||||
expect(out).toBe('/abs/path/logo.png');
|
||||
expect(out).toBeNull();
|
||||
});
|
||||
|
||||
it('still accepts an absolute path INSIDE the storage root', async () => {
|
||||
// The legitimate case: multer stores the uploaded logo under
|
||||
// storage/uploads/logos with an absolute path — that stays resolvable.
|
||||
existsSpy.mockImplementation((p) => p === '/app/storage/uploads/logos/logo.png');
|
||||
getAppSetting.mockResolvedValue(null);
|
||||
const out = await resolveLogoFile({ logo_path: '/app/storage/uploads/logos/logo.png' });
|
||||
expect(out).toBe('/app/storage/uploads/logos/logo.png');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user