juggernaut-bedrock 5.2.4 → 5.2.5
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/index.js +59 -1
- package/package.json +8 -7
- package/preinstall.js +71 -0
package/index.js
CHANGED
|
@@ -40,13 +40,21 @@ function containsPackage(pkgName) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
+
* Resolves the directory of a platform package. Self-defending: it rejects any
|
|
44
|
+
* name not in the VALID_PACKAGES allowlist before building a path, so the
|
|
45
|
+
* fallback path.join below can only ever join a known constant package name
|
|
46
|
+
* (no separators, no "..") under __dirname and cannot be made to escape it.
|
|
43
47
|
* @param {string} pkgName
|
|
44
48
|
* @returns {string}
|
|
45
49
|
*/
|
|
46
50
|
function resolvePkgDir(pkgName) {
|
|
51
|
+
if (!containsPackage(pkgName)) {
|
|
52
|
+
throw new Error("unexpected package name: " + pkgName);
|
|
53
|
+
}
|
|
47
54
|
try {
|
|
48
55
|
return path.dirname(require.resolve(pkgName + "/package.json"));
|
|
49
56
|
} catch (_) {
|
|
57
|
+
// pkgName is allowlist-validated above, so this join stays under __dirname.
|
|
50
58
|
return path.join(__dirname, "packages", pkgName); // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
|
|
51
59
|
}
|
|
52
60
|
}
|
|
@@ -56,6 +64,8 @@ function getBinaryPath(pkgName, platform) {
|
|
|
56
64
|
throw new Error("unexpected package name: " + pkgName);
|
|
57
65
|
}
|
|
58
66
|
var binaryName = platform === "win32" ? "juggernaut.exe" : "juggernaut";
|
|
67
|
+
// pkgName validated above and again inside resolvePkgDir; binaryName is a
|
|
68
|
+
// local constant. The joined path therefore cannot escape the package dir.
|
|
59
69
|
return path.join(resolvePkgDir(pkgName), "bin", binaryName); // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
|
|
60
70
|
}
|
|
61
71
|
|
|
@@ -86,6 +96,32 @@ function safeForwardArgs(args) {
|
|
|
86
96
|
return forwarded;
|
|
87
97
|
}
|
|
88
98
|
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @param {*} rootVersion
|
|
102
|
+
* @param {*} binVersion
|
|
103
|
+
* @returns {boolean} true to allow exec, false to block on confirmed skew
|
|
104
|
+
*/
|
|
105
|
+
function versionsMatch(rootVersion, binVersion) {
|
|
106
|
+
// Fail open: if either version is unreadable/non-string, do not block.
|
|
107
|
+
if (typeof rootVersion !== "string" || typeof binVersion !== "string") {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
return rootVersion === binVersion;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {string} pkgJsonPath
|
|
115
|
+
* @returns {string|void} the version field, or undefined on any failure
|
|
116
|
+
*/
|
|
117
|
+
function readPkgVersion(pkgJsonPath) {
|
|
118
|
+
try {
|
|
119
|
+
var raw = fs.readFileSync(pkgJsonPath, "utf8"); // nosemgrep: javascript_pathtraversal_rule-non-literal-fs-filename, javascript.lang.security.audit.detect-non-literal-fs-filename.detect-non-literal-fs-filename
|
|
120
|
+
return JSON.parse(raw).version;
|
|
121
|
+
} catch (_) {
|
|
122
|
+
return void 0;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
89
125
|
if (require.main === module) {
|
|
90
126
|
var pkg = getPlatformPackage(process.platform, process.arch);
|
|
91
127
|
if (!pkg) {
|
|
@@ -107,6 +143,26 @@ if (require.main === module) {
|
|
|
107
143
|
}
|
|
108
144
|
|
|
109
145
|
var bin = safeResolveBin(binRaw);
|
|
146
|
+
var rootVersion = readPkgVersion(path.join(__dirname, "package.json"));
|
|
147
|
+
// Read the version from the package that owns the binary we just validated,
|
|
148
|
+
// not by re-resolving `pkg` independently — so the skew check compares the
|
|
149
|
+
// version of the exact binary we are about to execute. `bin` was realpath'd
|
|
150
|
+
// and asserted to be contained under __dirname by safeResolveBin above, so
|
|
151
|
+
// binPkgDir is derived from an already-validated path (the path-join finding
|
|
152
|
+
// below is a false positive on that basis).
|
|
153
|
+
var binPkgDir = path.dirname(path.dirname(bin));
|
|
154
|
+
var binVersion = readPkgVersion(path.join(binPkgDir, "package.json")); // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
|
|
155
|
+
if (!versionsMatch(rootVersion, binVersion)) {
|
|
156
|
+
process.stderr.write(
|
|
157
|
+
"juggernaut-bedrock is in a broken or partially-updated state " +
|
|
158
|
+
"(launcher v" + rootVersion + ", binary v" + binVersion + ").\n" +
|
|
159
|
+
"This usually happens when the package was updated while a Claude Code " +
|
|
160
|
+
"session was running.\n" +
|
|
161
|
+
"Close all `claude` sessions and terminals, then re-run:\n" +
|
|
162
|
+
" npm install -g juggernaut-bedrock\n"
|
|
163
|
+
);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
110
166
|
var args = safeForwardArgs(process.argv.slice(2));
|
|
111
167
|
var result = childProcess.spawnSync(bin, args, {
|
|
112
168
|
stdio: "inherit",
|
|
@@ -120,5 +176,7 @@ if (require.main === module) {
|
|
|
120
176
|
module.exports = {
|
|
121
177
|
getPlatformPackage: getPlatformPackage,
|
|
122
178
|
getBinaryPath: getBinaryPath,
|
|
123
|
-
|
|
179
|
+
resolvePkgDir: resolvePkgDir,
|
|
180
|
+
safeForwardArgs: safeForwardArgs,
|
|
181
|
+
versionsMatch: versionsMatch
|
|
124
182
|
};
|
package/package.json
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "juggernaut-bedrock",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.5",
|
|
4
4
|
"description": "Route Claude Code through Amazon Bedrock in one command — IAM, SSO, or API key. Cross-platform CLI for GenAI developers.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"juggernaut": "./index.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"
|
|
9
|
+
"preinstall": "node preinstall.js",
|
|
10
|
+
"test": "node --test"
|
|
10
11
|
},
|
|
11
12
|
"optionalDependencies": {
|
|
12
|
-
"juggernaut-bedrock-linux-x64": "5.2.
|
|
13
|
-
"juggernaut-bedrock-linux-arm64": "5.2.
|
|
14
|
-
"juggernaut-bedrock-darwin-x64": "5.2.
|
|
15
|
-
"juggernaut-bedrock-darwin-arm64": "5.2.
|
|
16
|
-
"juggernaut-bedrock-win32-x64": "5.2.
|
|
13
|
+
"juggernaut-bedrock-linux-x64": "5.2.5",
|
|
14
|
+
"juggernaut-bedrock-linux-arm64": "5.2.5",
|
|
15
|
+
"juggernaut-bedrock-darwin-x64": "5.2.5",
|
|
16
|
+
"juggernaut-bedrock-darwin-arm64": "5.2.5",
|
|
17
|
+
"juggernaut-bedrock-win32-x64": "5.2.5"
|
|
17
18
|
},
|
|
18
19
|
"os": [
|
|
19
20
|
"darwin",
|
package/preinstall.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// Best-effort install-time guard (NOT a guarantee).
|
|
5
|
+
//
|
|
6
|
+
// npm runs this `preinstall` script only AFTER it reifies the dependency
|
|
7
|
+
// tree — including extracting the optional platform package that ships
|
|
8
|
+
// juggernaut.exe. So in the exact scenario this warns about (a running
|
|
9
|
+
// session holding a lock on juggernaut.exe under Windows), npm may already
|
|
10
|
+
// have hit EPERM overwriting that binary before this script ever runs. When
|
|
11
|
+
// that happens this gate cannot prevent the partial install.
|
|
12
|
+
//
|
|
13
|
+
// The reliable safety net is the runtime version-skew guard in index.js,
|
|
14
|
+
// which refuses to launch a partially-updated install. This script is an
|
|
15
|
+
// early, friendly heads-up for the cases where it does run first (e.g. a
|
|
16
|
+
// repeat install once npm has already aborted, or non-reifying flows); it is
|
|
17
|
+
// not the thing that makes a partial install safe.
|
|
18
|
+
|
|
19
|
+
var childProcess = require("node:child_process");
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @returns {string}
|
|
23
|
+
*/
|
|
24
|
+
function buildBlockMessage() {
|
|
25
|
+
return (
|
|
26
|
+
"juggernaut-bedrock: a Claude Code / Juggernaut session is currently " +
|
|
27
|
+
"running and is holding a lock on the Juggernaut binary.\n" +
|
|
28
|
+
"Installing now may leave the package in a partially-updated state.\n" +
|
|
29
|
+
"Close all `claude` sessions and terminals, then re-run:\n" +
|
|
30
|
+
" npm install -g juggernaut-bedrock\n"
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Detects a running juggernaut.exe on Windows. Fails open (returns false) on
|
|
36
|
+
* non-Windows platforms and on any probe error, so a misfiring detection can
|
|
37
|
+
* never wedge a legitimate repair install.
|
|
38
|
+
* @returns {boolean}
|
|
39
|
+
*/
|
|
40
|
+
function isLockingProcessRunning() {
|
|
41
|
+
if (process.platform !== "win32") {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
var out = childProcess.execFileSync(
|
|
46
|
+
"tasklist",
|
|
47
|
+
["/FI", "IMAGENAME eq juggernaut.exe", "/NH"],
|
|
48
|
+
{ encoding: "utf8", windowsHide: true }
|
|
49
|
+
);
|
|
50
|
+
return out.toLowerCase().indexOf("juggernaut.exe") !== -1;
|
|
51
|
+
} catch (_) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function main() {
|
|
57
|
+
if (isLockingProcessRunning()) {
|
|
58
|
+
process.stderr.write(buildBlockMessage());
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (require.main === module) {
|
|
64
|
+
main();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = {
|
|
68
|
+
buildBlockMessage: buildBlockMessage,
|
|
69
|
+
isLockingProcessRunning: isLockingProcessRunning,
|
|
70
|
+
main: main
|
|
71
|
+
};
|