diff --git a/packages/paperclip-runner/scripts/build-verified-provider-entrypoints.mjs b/packages/paperclip-runner/scripts/build-verified-provider-entrypoints.mjs index 44c9ea60b6..52daaa3a62 100644 --- a/packages/paperclip-runner/scripts/build-verified-provider-entrypoints.mjs +++ b/packages/paperclip-runner/scripts/build-verified-provider-entrypoints.mjs @@ -1,3 +1,4 @@ +import { chmod } from "node:fs/promises"; import { builtinModules } from "node:module"; import { dirname, resolve } from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; @@ -63,6 +64,9 @@ export async function bundleVerifiedProviderEntrypoints({ write = true } = {}) { logLevel: "silent", }); assertSelfContainedBundle(entrypoint, result); + if (write && process.platform !== "win32") { + await chmod(entrypoint.output, 0o755); + } results.push({ entrypoint, result }); } return results; diff --git a/packages/paperclip-runner/scripts/build-verified-provider-entrypoints.test.mjs b/packages/paperclip-runner/scripts/build-verified-provider-entrypoints.test.mjs index 26c9950b0e..46a93c646e 100644 --- a/packages/paperclip-runner/scripts/build-verified-provider-entrypoints.test.mjs +++ b/packages/paperclip-runner/scripts/build-verified-provider-entrypoints.test.mjs @@ -1,4 +1,5 @@ import assert from "node:assert/strict"; +import { stat } from "node:fs/promises"; import test from "node:test"; import { @@ -15,3 +16,16 @@ test("verified JS provider entrypoints bundle into one descriptor-safe file", as assert.match(source, /^#!\/usr\/bin\/env node\n/); } }); + +test("written provider entrypoints satisfy qualified launch permissions", async (t) => { + if (process.platform === "win32") { + t.skip("POSIX launch permissions do not apply on Windows"); + return; + } + await bundleVerifiedProviderEntrypoints(); + for (const entrypoint of verifiedProviderEntrypoints) { + const mode = (await stat(entrypoint.output)).mode; + assert.equal(mode & 0o022, 0, entrypoint.name); + assert.notEqual(mode & 0o100, 0, entrypoint.name); + } +});