CVE Patch Review

GHSA-CC8F-FCX3-GPJR: SurrealDB adds file allowlist checks for analyzer mapper file reads

GHSA-CC8F-FCX3-GPJR · Updated 2026-06-20 Partial fix

Summary

The patch introduces centralized path validation for filesystem-backed mapper access and wires it into analyzer and tree-store mapper call sites. It mitigates arbitrary file disclosure by allowing operators to constrain readable paths via SURREAL_FILE_ALLOWLIST, but the default behavior remains permissive when the allowlist is unset, so the fix is configuration-dependent rather than a hard security boundary by default.

Analysis

Vulnerability

GHSA-CC8F-FCX3-GPJR describes an arbitrary file disclosure issue in SurrealDB where authenticated users with EDITOR or OWNER privileges can register a DEFINE ANALYZER statement whose mapper() filter references attacker-controlled filesystem paths. The vulnerable flow shown in the patch summary passed the supplied path directly into file iteration logic without any authorization or path-scope validation.

The relevant vulnerable call site in the analyzer mapper was:

Self::iterate_file(&mut terms, path).await?;

Given the advisory scope and the patched files, the core weakness is unrestricted filesystem access from a database feature that accepts a path parameter. This is a host-boundary violation: database-level privileges were sufficient to trigger reads from arbitrary host files rather than only from explicitly sanctioned data directories. Source references: pull request #5600, GitHub Security Advisory, CVE Reports summary.

Patch

The patch adds a centralized file access control helper in crates/core/src/iam/file.rs and introduces a process-level allowlist sourced from the SURREAL_FILE_ALLOWLIST environment variable in crates/core/src/cnf/mod.rs. The new logic canonicalizes the requested path and, when an allowlist is configured, permits access only if the canonical target is under one of the canonicalized allowed directories.

pub static FILE_ALLOWLIST: LazyLock<Vec<PathBuf>> = LazyLock::new(|| {
	std::env::var("SURREAL_FILE_ALLOWLIST")
		.map(|input| extract_allowed_paths(&input))
		.unwrap_or_default()
});

pub(crate) fn is_path_allowed(path: &Path) -> Result<PathBuf, Error> {
	check_is_path_allowed(path, &FILE_ALLOWLIST)
}

fn check_is_path_allowed(path: &Path, allowed_path: &[PathBuf]) -> Result<PathBuf, Error> {
	let canonical_path = fs::canonicalize(path)?;
	if allowed_path.is_empty() {
		return Ok(canonical_path);
	}
	if allowed_path.iter().any(|allowed| canonical_path.starts_with(allowed)) {
		Ok(canonical_path)
	} else {
		Err(Error::FileAccessDenied(path.to_string_lossy().to_string()))
	}
}

The analyzer mapper now validates the path before reading:

let path = is_path_allowed(path)?;
Self::iterate_file(&mut terms, &path).await?;

A second call site in crates/core/src/idx/trees/store/mapper.rs also now invokes is_path_allowed(p)?, which is a positive sign that the maintainers looked beyond the single reported sink. The patch also adds a dedicated FileAccessDenied error variant and unit tests covering empty allowlist behavior and allow/deny decisions for files inside and outside configured directories. Primary source: https://github.com/surrealdb/surrealdb/pull/5600.

Review

Pros

The patch addresses the immediate root cause at the sink level by inserting a mandatory validation step before filesystem reads in the analyzer mapper. Centralizing the logic in iam/file.rs is maintainable and reduces the chance of inconsistent checks across features. Canonicalization before prefix comparison is the correct baseline defense against simple path traversal and symlink-based bypasses, because policy is evaluated against the resolved path rather than the raw user input.

The addition of FILE_ALLOWLIST gives operators a practical containment mechanism without changing query syntax or storage formats. The same helper being reused in another mapper-related module suggests some effort toward broader coverage. The dedicated error type improves observability and makes policy denials distinguishable from generic I/O failures. The included tests validate the intended semantics of the allowlist implementation.

Cons

The most important limitation is explicit in the code: when allowed_path.is_empty(), the helper returns success and imposes no restriction. That means the product remains permissive by default unless operators set SURREAL_FILE_ALLOWLIST. For a vulnerability framed as arbitrary host file disclosure, a security fix that depends on post-upgrade configuration is not a complete default-safe remediation.

The patch is also scoped to specific call sites visible in the diff summary. While crates/core/src/idx/ft/analyzer/mapper.rs and crates/core/src/idx/trees/store/mapper.rs are covered, the review cannot confirm from the provided sources that every filesystem-backed feature now routes through the same guard. If any other path-consuming code path remains outside is_path_allowed, similar disclosure risk could persist elsewhere.

There are also policy-model concerns. The allowlist is process-global, not role-aware, namespace-aware, or database-aware. Any user who can reach a guarded feature can read any file under the configured allowed directories, which may still be broader than least privilege. Finally, canonicalization requires the target to exist and does not itself address time-of-check/time-of-use races if files or symlinks are swapped after validation, though that is a narrower concern than the original unrestricted read issue.

Verdict

Partial fix.

The patch materially improves security by adding centralized path validation and by constraining reads to operator-approved directories when configured. However, it does not fully eliminate the vulnerability class by default because an empty SURREAL_FILE_ALLOWLIST preserves unrestricted access semantics. In practice, this is a strong mitigation and likely sufficient for hardened deployments, but it is not a complete root-cause eradication unless the deployment also sets a restrictive allowlist. Engineers adopting this patch should treat allowlist configuration as mandatory and audit all remaining filesystem read paths for consistent use of is_path_allowed. References: patch PR, advisory, report summary.

Sources