# Vulnerability Summary: Missing Authorization in chartbrew ## Vulnerability Overview **Vulnerability Name**: Missing Authorization in /api/chart/:chart_id/query via team-level refresh toggle **CVE ID**: CVE-2026-40601 **CVSS Score**: 7.5 / 10 (High) **Severity**: High **Description**: `chartbrew` exposes the `POST /api/chart/:chart_id/query` endpoint without authentication. This endpoint only checks the `team.allowReportRefresh` flag and does not verify whether the target chart belongs to a public report, whether the project is public, or whether the sharing policy permits the operation. **Core Issue**: The route retrieves the chart, project, and team but authorizes the operation solely based on the team-level `allowReportRefresh` flag. It fails to reuse the public report access checks implemented elsewhere in the codebase. ## Impact Scope * **Affected Versions**: 4.9.0 * **Fixed Versions**: v5.0.0 * **Attack Vector**: Network * **Attack Complexity**: Low * **Impact**: * **Confidentiality**: High * **Integrity**: High * **Availability**: None **Specific Impact**: An unauthenticated attacker who knows the chart identifier can trigger data refreshes and retrieve the current data of private charts. Attackers can obtain data from private charts and force on-demand refreshes of these charts. This exposes data that should be restricted to authenticated project members and may reveal real-time backend query results for protected datasets. ## Remediation * **Upgrade Version**: Upgrade `chartbrew` to **v5.0.0** or later. ## Proof of Concept (PoC) **Prerequisites**: * The target chart exists. * The associated team has `allowReportRefresh` enabled. **PoC Code**: ```http POST /api/chart/:id/query?optCache=False HTTP/1.1 Content-Type: application/json {"variables":{}} ``` **Expected Result**: The server returns status `200` along with the target chart object, including `chartData`, `ChartDatasetConfigs`, and project links, even if the underlying project is not public. **Core Vulnerable Code Path**: `server/api/ChartsRoute.js` ```javascript app.post("/chart/:chart_id/query", apiLimiter(10), (req, res) => { return chartController.findById(req.params.chart_id) .then(async (chart) => { const project = await projectController.findById(chart.project_id); const team = await teamController.findById(project.team_id); if (!team.allowReportRefresh) throw new Error(401); return chartController.updateChartData(req.params.chart_id, null, { variables: req.body.variables, }); }); }); ``` `server/controllers/ChartController.js` ```javascript return chartController.updateChartData(...) .then((chart) => res.status(200).send(chart)); ```