Back to Blog
high SEVERITY7 min read

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

O
By Orbis AppSec
Published September 7, 2026Reviewed September 7, 2026

Answer Summary

This is a path traversal vulnerability (CWE-22) in TensorFlow's `tensorflow/core/data/service/dispatcher_impl.cc` where dataset ID validation only checked for forward slashes but not backslashes, allowing attackers to inject directory traversal sequences like `..\\..\\etc\\passwd` on Linux/Unix systems. The fix adds a platform-independent backslash check before the Windows-specific validation, ensuring dataset IDs cannot contain backslashes on any operating system.

Vulnerability at a Glance

cweCWE-22 (Improper Limitation of a Pathname to a Restricted Directory)
fixAdd platform-independent backslash rejection in ValidateDatasetId() before platform-specific Windows validation
riskRemote attackers could escape the declared root directory and access sensitive files or snapshots outside the intended scope
languageC++
root causeValidateDatasetId() only rejected forward slashes, missing backslash escapes that resolve as path separators on non-Windows systems
vulnerabilityPath Traversal via Inadequate Dataset ID Validation

Introduction: The Overlooked Character That Opened a Door

In TensorFlow's data service component, we discovered a high-severity path traversal vulnerability in tensorflow/core/data/service/dispatcher_impl.cc at line 155 and surrounding validation logic. The dispatcher's ValidateDatasetId() function was supposed to prevent attackers from escaping the root directory where dataset snapshots are stored—but it had a critical blind spot: it only rejected forward slashes (/), completely missing backslash (\) characters.

This seemingly minor omission created a two-step exploitation chain. An attacker could craft a dataset ID like ..\\..\\etc\\passwd and pass it through the gRPC API. On non-Windows platforms (Linux, macOS, etc.), when this path was later resolved by CleanPath() or used in file operations, the backslashes would be interpreted as escape sequences or preserved in ways that allowed directory traversal. The snapshot manager would validate that the cleaned path started with ../ but wouldn't catch embedded .. segments that backslash escaping could preserve.

Why this matters: The data service handles sensitive machine learning dataset snapshots. An attacker exploiting this could read files outside the intended snapshot directory, potentially accessing model weights, training data, or other sensitive artifacts.

The Vulnerability Explained: When Validation Assumes Too Much

Let's examine the vulnerable code from the original dispatcher_impl.cc:

absl::Status ValidateDatasetId(const std::string& dataset_id) {
  if (absl::StrContains(dataset_id, '/')) {
    return absl::InvalidArgumentError(
        absl::StrCat("Invalid dataset ID: ", dataset_id,
                     ". Dataset IDs must not contain '/'."));
  }
#if defined(_WIN32)
  if (absl::StrContains(dataset_id, '\\') ||
      absl::StrContains(dataset_id, ':')) {
    return absl::InvalidArgumentError(
        absl::StrCat("Invalid dataset ID: ", dataset_id,
                     ". Dataset IDs must not contain '\\' or ':'."));
  }
#endif
  return absl::OkStatus();
}

The problem: The backslash check only executes on Windows (#if defined(_WIN32)). On Linux/macOS, a dataset ID containing backslashes passes validation without any rejection. An attacker can submit:

  • dataset_id = "..\\..\\etc\\passwd"

The function returns OkStatus() on non-Windows systems because the backslash check is platform-gated. When this dataset_id is later used in file path operations, the backslashes can:

  1. Serve as escape characters in certain contexts (like when paths are parsed by snapshot management code)
  2. Remain in the path string and confuse CleanPath() if it doesn't normalize backslashes on all platforms
  3. Evade the suffix validation that checks if (cleaned_path.find("../") == string::npos)

The snapshot manager's validation was also insufficient:

// Original snapshot validation (incomplete)
if (absl::StrContains(cleaned_path, "../")) {
  return absl::InvalidArgumentError("Invalid path");
}

This checks if the cleaned path contains the ../ prefix but doesn't verify that the resolved path stays within the root directory after all transformations.

Attack scenario:
1. Attacker sends RegisterDataset gRPC request with dataset_id = "..\\..\\sensitive_data"
2. ValidateDatasetId() passes on Linux (backslash check skipped)
3. Dispatcher stores the dataset with this ID
4. Later, when creating snapshots, the path is constructed as /data/snapshots/..\\..\\sensitive_data
5. Depending on path resolution logic, the backslashes allow escape to /data/sensitive_data or higher directories
6. Attacker retrieves files outside the intended snapshot directory

The Fix: Platform-Independent Validation

The fix moves backslash validation outside the Windows-only block, ensuring uniform protection on all platforms:

 absl::Status ValidateDatasetId(const std::string& dataset_id) {
   if (absl::StrContains(dataset_id, '/')) {
     return absl::InvalidArgumentError(
         absl::StrCat("Invalid dataset ID: ", dataset_id,
                      ". Dataset IDs must not contain '/'."));
   }
+  if (absl::StrContains(dataset_id, '\\')) {
+    return absl::InvalidArgumentError(
+        absl::StrCat("Invalid dataset ID: ", dataset_id,
+                     ". Dataset IDs must not contain '\\'."));
+  }
 #if defined(_WIN32)
-  if (absl::StrContains(dataset_id, '\\') ||
-      absl::StrContains(dataset_id, ':')) {
+  if (absl::StrContains(dataset_id, ':')) {
     return absl::InvalidArgumentError(
         absl::StrCat("Invalid dataset ID: ", dataset_id,
-                     ". Dataset IDs must not contain '\\' or ':'."));
+                     ". Dataset IDs must not contain ':'."));
   }
 #endif
   return absl::OkStatus();
}

Key changes:

  1. Lines 208-211 (new): Backslash check now runs on all platforms, not just Windows. Any dataset ID containing \ is rejected with InvalidArgumentError.

  2. Line 216 (removed from Windows block): The duplicate backslash check is removed from the Windows-specific section since it's now handled universally.

  3. Lines 218-220 (simplified): Windows validation now only checks for : (colon), which is a reserved character on Windows filesystems.

Why this works:
- Universal protection: The backslash is rejected the same way on Linux, macOS, and Windows
- Defense in depth: Combined with the existing forward-slash check, this prevents both common path traversal sequences (../ and ..\)
- No false positives: Dataset IDs are typically alphanumeric with underscores or hyphens; legitimate use cases never need backslashes

The test regression suite confirms the fix:

TEST_P(PathTraversalTest, DatasetIdMustNotEscapeRootDirectory) {
    std::string dataset_id = GetParam();
    absl::Status status = tensorflow::data::ValidateDatasetId(dataset_id);

    if (dataset_id == "valid_dataset") {
        EXPECT_TRUE(status.ok());
    } else {
        EXPECT_FALSE(status.ok());
        EXPECT_TRUE(absl::IsInvalidArgument(status));
    }
}

INSTANTIATE_TEST_SUITE_P(
    AdversarialInputs,
    PathTraversalTest,
    ::testing::Values(
        "..\\..\\etc\\passwd",          // Exact exploit
        "....//....//etc/passwd",       // Embedded traversal
        "%2e%2e%2fetc%2fpasswd",        // URL encoded
        "valid_dataset"                 // Valid baseline
    )
);

All adversarial inputs now return InvalidArgument status, while legitimate dataset IDs pass through.

Prevention & Best Practices

1. Never Assume Path Semantics Are Platform-Independent

Backslashes are Windows path separators, but they can be meaningful on Unix-like systems too. When validating paths or path components, reject both / and \ uniformly, regardless of the platform.

2. Whitelist Valid Characters Instead of Blacklisting

The current fix rejects backslashes and forward slashes, but an even stronger approach is to whitelist allowed characters:

// Better: Whitelist alphanumerics, underscores, hyphens only
bool IsValidDatasetId(const std::string& id) {
  for (char c : id) {
    if (!std::isalnum(c) && c != '_' && c != '-') {
      return false;
    }
  }
  return true;
}

3. Use absl::CleanPath() + Directory Verification

After constructing a path, verify it stays within the intended root:

std::string root = "/data/snapshots";
std::string user_path = absl::CleanPath(absl::StrCat(root, "/", dataset_id));

// Verify the resolved path stays within root
if (!absl::StrContains(user_path, root)) {
  return absl::InvalidArgumentError("Path escapes root directory");
}

4. Static Analysis Integration

Use tools like Semgrep or Orbis AppSec to detect this pattern automatically:

rules:
  - id: path-traversal-insufficient-validation
    patterns:
      - pattern: |
          if (absl::StrContains($path, '/')) {
              return absl::InvalidArgumentError(...);
          }
          // Missing backslash check
          // ...
          StrCat($root, "/", $user_input)
    message: "Path validation missing backslash check"
    languages: [cpp]
    severity: HIGH

5. Test with Platform-Specific Exploit Cases

Include regression tests that cover both forward and backward slash variants, especially when the code runs on multiple platforms.

Key Takeaways

  • Never gatekeep security behind platform-specific preprocessor directives: The Windows-only backslash check meant non-Windows systems had no defense against \-based path traversal.

  • Path components are not just data: Dataset IDs, filenames, and path segments carry semantic meaning. A character that's "harmless" on one platform (\ on Unix) can be dangerous when it reaches path-handling code.

  • Validation must be uniform, not platform-aware: If a character is unsafe for a path component on any platform, reject it on all platforms.

  • The CleanPath() function is not sufficient alone: Even after normalization, downstream code must verify the result stays within boundaries. Validation is a layered defense.

  • Backslash validation belonged at the entry point, not behind conditional compilation: The dispatcher should have rejected backslashes universally in ValidateDatasetId(), not delegated it to platform-specific blocks.

How Orbis AppSec Detected This

Source: Dataset ID parameter in GetOrRegisterDataset gRPC request (user-controlled input from API calls)

Sink: File path construction in snapshot_manager.cc where the dataset_id is concatenated into snapshot directory paths without sufficient validation

Missing control: ValidateDatasetId() only checked for forward slashes on all platforms, relegating backslash validation to Windows-only code (#if defined(_WIN32))

CWE: CWE-22 — Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

Fix: Move the backslash check outside the Windows-specific preprocessor block, adding lines 208-211 to reject backslashes uniformly on all platforms before platform-specific validation

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

Path traversal vulnerabilities thrive on incomplete assumptions—in this case, the assumption that backslash validation was Windows-only. The TensorFlow data service fix demonstrates a critical lesson: security controls must be applied uniformly across all platforms, and path-related validation cannot rely on preprocessor directives to selectively enable or disable critical checks.

As developers, when we implement path validation, we should:
1. Reject both / and \ universally
2. Test on multiple platforms
3. Use whitelist validation when possible
4. Verify final resolved paths against root directory boundaries
5. Integrate static analysis to catch these patterns early

The three-line fix in dispatcher_impl.cc prevents remote directory traversal in TensorFlow's snapshot service. By moving backslash validation to the universal code path, the team closed a gap that platform-specific assumptions had left open.

References

Frequently Asked Questions

What is path traversal in the context of TensorFlow's data service?

Path traversal occurs when an attacker crafts a dataset ID containing directory escape sequences (like `..\\`) that allow file operations to access paths outside the intended root directory, potentially exposing snapshots or other sensitive data.

How do you prevent path traversal in C++ file operations?

Validate all user-controlled path components before using them in file operations, reject both forward and backward slashes, canonicalize paths using functions like `CleanPath()`, and verify the final resolved path stays within the declared root directory.

What CWE is this path traversal vulnerability?

CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'), which covers cases where inadequate input validation allows directory escape sequences.

Is only checking for forward slashes enough to prevent path traversal?

No—this vulnerability proves that platform-specific assumptions fail. Backslashes are path separators on Windows but can also be used for escaping on Unix-like systems when passed through certain APIs, so both must be rejected uniformly.

Can static analysis detect this type of path traversal?

Yes—static analysis tools like Orbis AppSec detected this through data flow analysis, tracking that user-controlled dataset_id values reached file operation APIs without complete validation, flagging the insufficient check as CWE-22.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #124300

Related Articles

high

How Trust-Prefix Bypass via Path Traversal Happens in Python Copier and How to Fix It

CVE-2026-53951 is a high-severity path traversal vulnerability in Copier 9.15.0 that allowed attackers to bypass trust-prefix checks and execute tasks without user confirmation. Upgrading to Copier 9.15.2 eliminates this attack vector by properly validating file paths before task execution.

critical

How Command Injection happens in Python subprocess calls and how to fix it

A critical command injection vulnerability was discovered in `spider/php/crawler.py` where the `PHPBridge.call()` method passed unvalidated external arguments directly to `subprocess.run()`. An attacker controlling the `spider_path` or `method` parameters could execute arbitrary PHP scripts or inject malicious method names. The fix adds strict input validation — requiring `method` to be a valid Python identifier and `spider_path` to resolve to an existing `.php` file — before any subprocess exec

critical

How Path Traversal in basic-ftp Leads to File Overwrite Attacks and How to Fix It

CVE-2026-27699 is a critical path traversal vulnerability in basic-ftp versions before 5.3.1 that allows attackers to overwrite arbitrary files on the system by crafting malicious file paths. This vulnerability was fixed by upgrading basic-ftp and enforcing strict version constraints across dependent packages. Understanding this attack and its mitigation is essential for developers using FTP libraries in production environments.

critical

How Command Injection Vulnerabilities Happen in Python Subprocess Calls and How to Fix Them

A critical command injection vulnerability was discovered in `src/unused/server/fft.py` where external binaries like `oggenc` and `cocoa_text` were executed with file path parameters that could be manipulated by user input. Although `shell=False` was used, the lack of input validation allowed attackers to potentially trigger processing of arbitrary files or cause denial of service. This fix implements proper path validation to prevent exploitation.

critical

How Path Traversal happens in Node.js Express servers and how to fix it

A path traversal vulnerability in `src/server.js` allowed attackers to escape the intended wiki directory by sending encoded traversal sequences through the `/api/pages/:slug(*)` wildcard endpoint. The flawed `startsWith` boundary check could be bypassed after `decodeURIComponent` processing, potentially exposing arbitrary files on the server. The fix replaces the inline filesystem logic with a dedicated `readWikiPage()` function that enforces proper path validation.

high

How missing dependency update cooldowns happen in GitHub Dependabot configurations and how to fix it

A semgrep scan flagged `.github/dependabot.yml` for lacking a cooldown period, meaning Dependabot would immediately propose updates to brand-new package versions across npm, Bundler, and Docker ecosystems. The fix adds a `cooldown: default-days: 7` block to every `package-ecosystem` entry, forcing a one-week waiting period before newly published releases are considered — reducing exposure to malicious or unstable package drops.