@boardwalk-labs/runner 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/daemon/identity.js
CHANGED
|
@@ -2,10 +2,20 @@
|
|
|
2
2
|
// Persisted runner identity — the standing credential + coordinates a machine keeps between
|
|
3
3
|
// restarts (`runner start` after a reboot skips registration). One JSON file per
|
|
4
4
|
// (control plane, pool), mode 0600: the runner token is a credential.
|
|
5
|
-
import { mkdir, readFile, writeFile, unlink } from "node:fs/promises";
|
|
5
|
+
import { mkdir, readFile, writeFile, unlink, chmod } from "node:fs/promises";
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import { z } from "zod";
|
|
9
|
+
/** Re-assert restrictive perms on a path (POSIX only). `mkdir`/`writeFile` set mode only when they
|
|
10
|
+
* CREATE, so a pre-existing dir/file could be group/world-readable; this makes the credential
|
|
11
|
+
* fail-safe regardless. No-op on Windows (no POSIX mode bits). Best-effort — a chmod failure must
|
|
12
|
+
* not break enrollment. NOTE: this hardens the file at rest; it does NOT stop a same-UID process
|
|
13
|
+
* (an in-process run program) from reading it — that requires per-run UID/container isolation. */
|
|
14
|
+
async function hardenPerms(target, mode) {
|
|
15
|
+
if (process.platform === "win32")
|
|
16
|
+
return;
|
|
17
|
+
await chmod(target, mode).catch(() => undefined);
|
|
18
|
+
}
|
|
9
19
|
export const runnerIdentitySchema = z.strictObject({
|
|
10
20
|
runner_id: z.string().min(1),
|
|
11
21
|
runner_token: z.string().min(1),
|
|
@@ -25,15 +35,21 @@ function identityFile(dir, controlPlaneUrl, pool) {
|
|
|
25
35
|
}
|
|
26
36
|
export async function saveIdentity(dir, identity) {
|
|
27
37
|
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
38
|
+
await hardenPerms(dir, 0o700); // re-assert: mkdir's mode only applies when it CREATES the dir
|
|
28
39
|
const file = identityFile(dir, identity.control_plane_url, identity.pool);
|
|
29
40
|
await writeFile(file, `${JSON.stringify(identity, null, 2)}\n`, { mode: 0o600 });
|
|
41
|
+
await hardenPerms(file, 0o600); // re-assert: writeFile's mode only applies to a NEW file
|
|
30
42
|
return file;
|
|
31
43
|
}
|
|
32
44
|
export async function loadIdentity(dir, controlPlaneUrl, pool) {
|
|
33
45
|
try {
|
|
34
|
-
const
|
|
46
|
+
const file = identityFile(dir, controlPlaneUrl, pool);
|
|
47
|
+
const raw = await readFile(file, "utf8");
|
|
35
48
|
const parsed = runnerIdentitySchema.safeParse(JSON.parse(raw));
|
|
36
|
-
|
|
49
|
+
if (!parsed.success)
|
|
50
|
+
return null;
|
|
51
|
+
await hardenPerms(file, 0o600); // repair perms if something loosened them since save
|
|
52
|
+
return parsed.data;
|
|
37
53
|
}
|
|
38
54
|
catch {
|
|
39
55
|
return null;
|
|
@@ -15,6 +15,7 @@ import { promisify } from "node:util";
|
|
|
15
15
|
import { stat, mkdir, readFile, writeFile, rm } from "node:fs/promises";
|
|
16
16
|
import { tmpdir } from "node:os";
|
|
17
17
|
import { dirname, join } from "node:path";
|
|
18
|
+
import { extract as tarExtract } from "tar";
|
|
18
19
|
import { createLogger } from "./support/index.js";
|
|
19
20
|
const log = createLogger("WorkspaceStore");
|
|
20
21
|
const exec = promisify(execFile);
|
|
@@ -107,7 +108,13 @@ export class TarWorkspaceArchiver {
|
|
|
107
108
|
}
|
|
108
109
|
async extract(srcPath, dir) {
|
|
109
110
|
await mkdir(dir, { recursive: true });
|
|
110
|
-
|
|
111
|
+
// Extract via node-tar, NOT `tar xzf`: extraction reads UNTRUSTED input (a program artifact
|
|
112
|
+
// from an arbitrary control plane on a self-hosted runner, or a prior run's workspace snapshot),
|
|
113
|
+
// and node-tar is hardened + portable — with default `preservePaths: false` it strips absolute
|
|
114
|
+
// paths and `..` members, and refuses to write THROUGH a symlink (unlinking it first), closing
|
|
115
|
+
// the traversal/symlink-escape gaps that differ between GNU tar and bsdtar. Same behavior on
|
|
116
|
+
// macOS and Linux, no dependency on which `tar` the OS ships.
|
|
117
|
+
await tarExtract({ file: srcPath, cwd: dir });
|
|
111
118
|
}
|
|
112
119
|
}
|
|
113
120
|
/** Production fs — node's fs/promises. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boardwalk-labs/runner",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Boardwalk self-hosted runner: execute cloud-scheduled runs on your own machines. Currently: the canonical runner contract types.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|