Skip to content

Commit 1858c2c

Browse files
authored
fix: assert no path traversal in renders (#801)
1 parent a5e55bf commit 1858c2c

File tree

4 files changed

+39
-2
lines changed

4 files changed

+39
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 4.0.17 (2025-10-09)
2+
3+
- fix: assert no path traversal in renders (CVE-2025-11539), [#801](https://github.com/grafana/grafana-image-renderer/pull/801), [Proximyst](https://github.com/Proximyst), [KristianGrafana](https://github.com/KristianGrafana)
4+
15
## 4.0.16 (2025-10-06)
26

37
- Docker: Update Chromium to 141.0.7390.54 (CVE-2025-10890, CVE-2025-10891, CVE-2025-10892), [#799](https://github.com/grafana/grafana-image-renderer/pull/799), [Proximyst](https://github.com/Proximyst)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
services:
2+
grafana:
3+
image: grafana/grafana-enterprise:nightly
4+
ports:
5+
- 3000:3000
6+
environment:
7+
GF_RENDERING_SERVER_URL: http://renderer:8081/render
8+
GF_RENDERING_CALLBACK_URL: http://grafana:3000/
9+
GF_LOG_FILTERS: rendering:debug
10+
11+
renderer:
12+
build:
13+
context: ../../../
14+
dockerfile: Dockerfile
15+
ports:
16+
- 8081:8081
17+
environment:
18+
ENABLE_METRICS: 'true'

plugin.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
"url": "https://github.com/grafana/grafana-image-renderer/blob/master/LICENSE"
2525
}
2626
],
27-
"version": "4.0.16",
28-
"updated": "2025-10-06"
27+
"version": "4.0.17",
28+
"updated": "2025-10-09"
2929
},
3030
"dependencies": {
3131
"grafanaDependency": ">=11.3.8"

src/browser/browser.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os from 'os';
22
import uniqueFilename from 'unique-filename';
3+
import * as boom from '@hapi/boom';
34
import * as puppeteer from 'puppeteer';
45
import chokidar from 'chokidar';
56
import path from 'path';
@@ -306,6 +307,8 @@ export class Browser {
306307
};
307308

308309
async takeScreenshot(page: puppeteer.Page, options: ImageRenderOptions, signal: AbortSignal): Promise<RenderResponse> {
310+
assertNoPathTraversal(options.filePath);
311+
309312
await this.performStep('prepare', options.url, signal, async () => {
310313
if (this.config.verboseLogging) {
311314
this.log.debug(
@@ -451,6 +454,8 @@ export class Browser {
451454
}
452455

453456
async exportCSV(page: any, options: RenderOptions, signal: AbortSignal): Promise<RenderCSVResponse> {
457+
assertNoPathTraversal(options.filePath);
458+
454459
let downloadFilePath = '';
455460
let downloadPath = '';
456461
await this.performStep('prepare', options.url, signal, async () => {
@@ -753,3 +758,13 @@ async function waitForQueriesAndVisualizations(page: puppeteer.Page, options: Im
753758
// Give some more time for rendering to complete
754759
await new Promise((resolve) => setTimeout(resolve, 50));
755760
}
761+
762+
function assertNoPathTraversal(path?: string | null) {
763+
if (!path) {
764+
return;
765+
}
766+
767+
if (path.indexOf('/') !== -1 || path.indexOf('\\') !== -1 || path === '..') {
768+
throw boom.badRequest('File path should not include directories');
769+
}
770+
}

0 commit comments

Comments
 (0)