Skip to content

Vite Example (Vue) Escapes the Bazel Sandbox #614

@tenzinhl

Description

@tenzinhl

Intro

The Vue build example in frontend/vue/ which builds using vite currently escapes the bazel sandbox, causing non-hermetic (and unexpected) behavior when building with sandboxes.

How to reproduce

cd frontend
bazel run //vue:build_test
# This succeeds, and should

rm src/components/HelloWorld.vue
bazel run //vue:build_test
# !!! This succeeds, and should NOT

# We can validate that this should not in fact succeed by doing a bazel clean then rebuilding
bazel clean
bazel run //vue:build_test
# Now it correctly fails!

Why does it fail?

This fails because the vite build process escapes the sandbox. Specifically by the time vite.config.ts is evaluated, the @ alias is resolved to an absolute path that is under the workspace execroot rather than the sandbox execroot.

To check this, here's the diff I applied to vite.config.ts:

diff --git a/frontend/vue/vite.config.ts b/frontend/vue/vite.config.ts
index b86c7aa..678c9eb 100644
--- a/frontend/vue/vite.config.ts
+++ b/frontend/vue/vite.config.ts
@@ -1,4 +1,4 @@
-import { fileURLToPath, URL } from 'url';
+// import { fileURLToPath, URL } from 'url'; // Commented out to avoid sandbox escape
 
 import { defineConfig } from 'vite';
 import vue from '@vitejs/plugin-vue';
@@ -9,7 +9,19 @@ export default defineConfig({
   plugins: [vue(), vueJsx()],
   resolve: {
     alias: {
-      '@': fileURLToPath(new URL('./src', import.meta.url)),
+      '@': (() => {
+        // @ts-ignore - We know process exists at runtime
+        console.error(`DEBUG: Vite config - process.cwd(): ${process.cwd()}`);
+        console.error(`DEBUG: Vite config - import.meta.url: ${import.meta.url}`);
+
+        // WORKAROUND: Use process.cwd() instead of import.meta.url to avoid sandbox escape
+        // const resolved = fileURLToPath(new URL('./src', import.meta.url));
+        // @ts-ignore - We know process exists at runtime
+        const resolved = process.cwd() + '/src';
+        console.error(`DEBUG: Vite config - resolved @/ alias to: ${resolved}`);
+
+        return resolved;
+      })(),
     },
     dedupe: ['vue'],

Now build such that there's a build failure (can do this by just building with HelloWorld.vue deleted), and we see:

DEBUG: Vite config - process.cwd(): /private/var/tmp/_bazel_tenzinlow/c154fecce8d0e75559a8f01413d9ae9e/sandbox/darwin-sandbox/289/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/vue
DEBUG: Vite config - import.meta.url: file:///private/var/tmp/_bazel_tenzinlow/c154fecce8d0e75559a8f01413d9ae9e/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/vue/vite.config.ts
DEBUG: Vite config - resolved @/ alias to: /private/var/tmp/_bazel_tenzinlow/c154fecce8d0e75559a8f01413d9ae9e/sandbox/darwin-sandbox/289/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin/vue/src

Specifically, note that import.meta.url resolves to the workspace execroot (/private/var/tmp/_bazel_tenzinlow/c154fecce8d0e75559a8f01413d9ae9e/execroot/_main/ )! Not the proper sandbox execroot for the action (/private/var/tmp/_bazel_tenzinlow/c154fecce8d0e75559a8f01413d9ae9e/sandbox/darwin-sandbox/289/execroot/_main/, which we can see process.cwd() is correctly in, note the sandbox in the path).

Potential Solution?

As noted in that diff, at least this specific failure is fixed by using process.cwd() to get a path that's in the sandbox directory. After applying that diff, doing the above steps to try and repro (build first with file, then with it deleted) will properly fail the build on the second go. I think there are potentially still other paths that vite may access (e.g.: when importing node modules) that escape the sandbox directory, but this at least solves it for the alias.

This was a big deal for our company, as this sandbox escape was causing some real havoc in our CI as the workspace bazel-out/ as we were changing a build process to use JS instead of TS files, but our CI runners would then have stale *.ts files next to the new *.js files in bazel-out/, and Vite didn't handle this well (since it would look at the old TS files, which could have different content). This was causing our remote builds to fail, with the working solution being to bazel clean first. After we updated our vite.config.ts to use process.cwd() as suggested in this patch it fixed our issue, but it does seem like a bit of a code smell.

Related Docs/Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions