juggernaut-bedrock 5.2.4 → 5.2.6

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.
Files changed (2) hide show
  1. package/index.js +59 -1
  2. package/package.json +7 -7
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
- safeForwardArgs: safeForwardArgs
179
+ resolvePkgDir: resolvePkgDir,
180
+ safeForwardArgs: safeForwardArgs,
181
+ versionsMatch: versionsMatch
124
182
  };
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "juggernaut-bedrock",
3
- "version": "5.2.4",
3
+ "version": "5.2.6",
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
- "test": "node --test index.test.js"
9
+ "test": "node --test"
10
10
  },
11
11
  "optionalDependencies": {
12
- "juggernaut-bedrock-linux-x64": "5.2.4",
13
- "juggernaut-bedrock-linux-arm64": "5.2.4",
14
- "juggernaut-bedrock-darwin-x64": "5.2.4",
15
- "juggernaut-bedrock-darwin-arm64": "5.2.4",
16
- "juggernaut-bedrock-win32-x64": "5.2.4"
12
+ "juggernaut-bedrock-linux-x64": "5.2.6",
13
+ "juggernaut-bedrock-linux-arm64": "5.2.6",
14
+ "juggernaut-bedrock-darwin-x64": "5.2.6",
15
+ "juggernaut-bedrock-darwin-arm64": "5.2.6",
16
+ "juggernaut-bedrock-win32-x64": "5.2.6"
17
17
  },
18
18
  "os": [
19
19
  "darwin",