getredash/redash · error · ValueError

Scripts can only be run from the configured scripts director

Error message

Scripts can only be run from the configured scripts directory

What it means

Raised in ScriptQueryRunner.__init__ (redash/query_runner/script.py:65) as ValueError when the configured scripts path contains '../', i.e. it is not confined to a single directory. This is a deliberate path-traversal guard: the runner only permits executing scripts from one whitelisted directory, and any configuration that could escape it is rejected at construction time.

Source

Thrown at redash/query_runner/script.py:65

            },
            "required": ["path"],
        }

    @classmethod
    def type(cls):
        return "insecure_script"

    def __init__(self, configuration):
        super(Script, self).__init__(configuration)

        path = self.configuration.get("path", "")
        # If path is * allow any execution path
        if path == "*":
            return

        # Poor man's protection against running scripts from outside the scripts directory
        if path.find("../") > -1:
            raise ValueError("Scripts can only be run from the configured scripts directory")

    def test_connection(self):
        pass

    def run_query(self, query, user):
        try:
            script = query_to_script_path(self.configuration["path"], query)
            return run_script(script, self.configuration["shell"])
        except IOError as e:
            return None, str(e)
        except subprocess.CalledProcessError as e:
            return None, str(e)


register(Script)

View on GitHub (pinned to ca79fe988d)

Solutions

  1. Set the path to the exact directory containing the scripts with no '../' segments (canonicalize it first: realpath)
  2. If you genuinely need multiple directories, use '*' with the understanding that it disables the confinement entirely
  3. Alternatively symlink the needed scripts into a single directory and configure that directory

Example fix

# before
path: /opt/redash/../shared/scripts
# after
path: /opt/shared/scripts
Defensive patterns

Strategy: validation

Validate before calling

import os.path
path = os.path.realpath(path)
if path != '*' and '..' + os.sep in path:
    raise ValueError('scripts path must not traverse parent directories')

Try / catch

try:
    runner = ScriptQueryRunner(configuration)
except ValueError as e:
    return error_response(400, 'Invalid scripts path: {}'.format(e))

Prevention

When it happens

Trigger: Saving a Script data source whose path setting includes a parent-directory segment, e.g. '/opt/redash/../scripts' or 'scripts/../../shared/scripts'. The only path that bypasses the check is the literal '*' (allow anything).

Common situations: Admins trying to point two data sources at a shared parent directory; symlinks not usable so '../' is attempted; mis-typed absolute path that accidentally contains a parent segment.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of getredash/redash@ca79fe988d (2026-08-28). Data as JSON: /api/errors/77d8dd1f26466e44. Report an issue: GitHub.