Reflected cross-site scripting vulnerability (XSS) in GData extension (authorized.vt)
Description
OpenRefine is a free, open source tool for working with messy data. Prior to version 3.8.3, the /extension/gdata/authorized endpoint includes the state GET parameter verbatim in a `` tag in the output, so without escaping. An attacker could lead or redirect a user to a crafted URL containing JavaScript code, which would then cause that code to be executed in the victim's browser as if it was part of OpenRefine. Version 3.8.3 fixes this issue.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
OpenRefine before 3.8.3 has a reflected XSS in the GData extension's /authorized endpoint due to unescaped state parameter.
Vulnerability
CVE-2024-47878 is a reflected cross-site scripting (XSS) vulnerability in OpenRefine, a popular tool for cleaning messy data. Prior to version 3.8.3, the /extension/gdata/authorized endpoint in the GData extension directly reflected the state GET parameter into a `` tag without proper HTML escaping or sanitization. This allowed an attacker to inject arbitrary JavaScript code into the response.[1][4]
Exploitation
An attacker could craft a malicious URL containing JavaScript payload in the state parameter and trick a victim into clicking it (e.g., via phishing or redirection). The exploit requires no special configuration; the GData extension must be present, but the attacker does not need valid OAuth credentials. The proof-of-concept URL http://localhost:3333/extension/gdata/authorized?state=%22,alert(1),%22&error= demonstrates that the code executes immediately in the victim's browser context.[4] The fix introduced validation of the state parameter format, ensuring it is a valid base64-encoded JSON with expected winname and cb patterns, rejecting malformed input.[2]
Impact
Successful exploitation allows arbitrary JavaScript execution in the user's browser. The attacker can perform any action the user can, such as deleting projects, accessing database passwords, or executing Jython or Closure expressions if those extensions are enabled. This can lead to complete compromise of the OpenRefine instance and its data.[1][4]
Mitigation
OpenRefine version 3.8.3, released in October 2024, contains the fix. Users should upgrade immediately. No workaround is available. The vulnerability is not listed in CISA's Known Exploited Vulnerabilities catalog at the time of publication.[1][2][3]
- NVD - CVE-2024-47878
- gdata: check cb parameter in authorized command · OpenRefine/OpenRefine@10bf087
- GitHub - OpenRefine/OpenRefine: OpenRefine is a free, open source power tool for working with messy data and improving it
- Reflected cross-site scripting vulnerability (XSS) in GData extension (authorized.vt)
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.openrefine:extensionsMaven | < 3.8.3 | 3.8.3 |
Affected products
3<3.8.3+ 1 more
- (no CPE)range: <3.8.3
- (no CPE)range: < 3.8.3
Patches
110bf0874d67fgdata: check cb parameter in authorized command
3 files changed · +94 −21
extensions/gdata/module/authorized.vt+2 −4 modified@@ -40,10 +40,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <span id="gdata-authorized"></span> <script> - var state = JSON.parse(window.atob("$state")); - - var windowName = state.winname; - var callbackName = state.cb; + var windowName = "$winname"; + var callbackName = "$cb"; var w = window.open("", windowName); var callback = w[callbackName];
extensions/gdata/module/MOD-INF/controller.js+4 −17 modified@@ -101,23 +101,10 @@ function process(path, request, response) { send(request, response, "authorize.vt", context); } else if (path == "authorized") { - var context = {}; - context.state = request.getParameter("state"); - - (function() { - if (Packages.com.google.refine.extension.gdata.TokenCookie.getToken(request) !== null) { - return; - } - var tokenAndExpiresInSeconds = Packages.com.google.refine.extension.gdata.GoogleAPIExtension.getTokenFromCode(module,request); - if (tokenAndExpiresInSeconds) { - var tokenInfo = tokenAndExpiresInSeconds.split(","); - Packages.com.google.refine.extension.gdata.TokenCookie.setToken(request, response, tokenInfo[0], tokenInfo[1]); - return; - } - Packages.com.google.refine.extension.gdata.TokenCookie.deleteToken(request, response); - })(); - - send(request, response, "authorized.vt", context); + // it's a command but we handle it manually here, so as to preserve the URL + var command = new Packages.com.google.refine.extension.gdata.AuthorizedCommand(module); + command.doGet(request, response); + butterfly.responded(); } else if (path == "/" || path == "") { var context = {}; context.version = version;
extensions/gdata/src/com/google/refine/extension/gdata/AuthorizedCommand.java+88 −0 added@@ -0,0 +1,88 @@ + +package com.google.refine.extension.gdata; + +import java.io.IOException; +import java.util.Base64; +import java.util.regex.Pattern; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import edu.mit.simile.butterfly.ButterflyModule; +import org.apache.velocity.VelocityContext; + +import com.google.refine.commands.Command; +import com.google.refine.util.ParsingUtilities; + +public class AuthorizedCommand extends Command { + + Pattern callbackPattern = Pattern.compile("^cb[0-9]+$"); + Pattern winnamePattern = Pattern.compile("^openrefine[0-9]+$"); + + ButterflyModule module; + + public AuthorizedCommand(ButterflyModule module) { + this.module = module; + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + VelocityContext velocityContext = new VelocityContext(); + String state = request.getParameter("state"); + if (state == null) { + respond(response, "error", "No 'state' parameter provided"); + return; + } + try { + byte[] decoded = Base64.getDecoder().decode(state); + JsonNode parsed = ParsingUtilities.mapper.readTree(decoded); + if (parsed instanceof ObjectNode && parsed.has("winname") && parsed.has("cb")) { + ObjectNode object = (ObjectNode) parsed; + String cb = object.get("cb").asText(); + String winname = object.get("winname").asText(); + if (!winnamePattern.matcher(winname).find()) { + respond(response, "error", "Invalid winname provided"); + return; + } + if (!callbackPattern.matcher(cb).find()) { + respond(response, "error", "Invalid callback provided"); + return; + } + velocityContext.internalPut("winname", winname); + velocityContext.internalPut("cb", cb); + + } else { + throw new IllegalArgumentException("expected a JSON object"); + } + } catch (IllegalArgumentException | IOException e) { + respond(response, "error", "Invalid 'state' parameter provided"); + return; + } + + updateToken(request, response); + + try { + module.sendTextFromTemplate(request, response, velocityContext, "authorized.vt", "UTF-8", "text/html", false); + } catch (Exception e) { + respondException(response, e); + } + } + + private void updateToken(HttpServletRequest request, HttpServletResponse response) throws IOException { + if (TokenCookie.getToken(request) != null) { + return; + } + var tokenAndExpiresInSeconds = GoogleAPIExtension.getTokenFromCode(module, request); + if (tokenAndExpiresInSeconds != null) { + var tokenInfo = tokenAndExpiresInSeconds.split(","); + TokenCookie.setToken(request, response, tokenInfo[0], tokenInfo[1]); + return; + } + TokenCookie.deleteToken(request, response); + } + +}
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-pw3x-c5vp-mfc3ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-47878ghsaADVISORY
- github.com/OpenRefine/OpenRefine/commit/10bf0874d67f1018a58b3732332d76b840192feaghsax_refsource_MISCWEB
- github.com/OpenRefine/OpenRefine/security/advisories/GHSA-pw3x-c5vp-mfc3ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.