herozion 1.1.74 → 1.1.76

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.
@@ -0,0 +1,8 @@
1
+ {
2
+ "_comment": "SHA-256 hashes for each platform binary. Auto-generated in CI from release CHECKSUMS.sha256. DO NOT edit manually.",
3
+ "_version": "1.1.76",
4
+ "herozion-linux-amd64": "388813017d4212e1615132e8ea98cba63eefa8b6885d9b8509f64e9d9c7f46d1",
5
+ "herozion-macos-amd64": "50145d9ac239c0380fe60c2e775ed43bd9588efd32538fe4bcee4c89a6a92cbd",
6
+ "herozion-macos-arm64": "5fe6d4c03b5d7d944e14d59371477b9a5a2e456a8a2ee3f051022587303594cc",
7
+ "herozion-windows-amd64.exe": "1a64cc8460e5cf92b4806e0ecd69b857bc70d2b7fe42981842ff9b8cf24d558f"
8
+ }
package/install.js CHANGED
@@ -42,6 +42,13 @@ function getBinaryName() {
42
42
  const { platform, arch } = process;
43
43
 
44
44
  if (platform === "win32" && arch === "x64") return "herozion-windows-amd64.exe";
45
+ if (platform === "linux" && arch === "arm64") {
46
+ throw new Error(
47
+ "Herozion: Linux ARM64 is not supported by the npm binary installer yet.\n" +
48
+ "Use: pip install herozion OR download from:\n" +
49
+ " https://github.com/Herozion/scanner-releases/releases/latest"
50
+ );
51
+ }
45
52
  if (platform === "linux" && (arch === "x64" || arch === "ia32")) return "herozion-linux-amd64";
46
53
  if (platform === "darwin" && arch === "arm64") return "herozion-macos-arm64";
47
54
  if (platform === "darwin" && (arch === "x64" || arch === "ia32")) return "herozion-macos-amd64";
@@ -59,8 +66,18 @@ const _ALLOWED_REDIRECT_HOSTS = new Set([
59
66
  "objects.githubusercontent.com",
60
67
  "codeload.github.com",
61
68
  "releases.githubusercontent.com",
69
+ "release-assets.githubusercontent.com",
62
70
  ]);
63
71
 
72
+ function _isAllowedRedirectHost(host) {
73
+ if (_ALLOWED_REDIRECT_HOSTS.has(host)) {
74
+ return true;
75
+ }
76
+ // GitHub release binaries are served from various *.githubusercontent.com CDNs.
77
+ // Allow the suffix so new CDN hostnames do not break installs.
78
+ return host.endsWith(".githubusercontent.com");
79
+ }
80
+
64
81
  function _validateRedirectUrl(location) {
65
82
  if (!location || !location.startsWith("https://")) {
66
83
  throw new Error(`Redirect to non-HTTPS URL rejected: ${location}`);
@@ -69,10 +86,10 @@ function _validateRedirectUrl(location) {
69
86
  try { host = new URL(location).hostname; } catch (_) {
70
87
  throw new Error(`Invalid redirect URL: ${location}`);
71
88
  }
72
- if (!_ALLOWED_REDIRECT_HOSTS.has(host)) {
89
+ if (!_isAllowedRedirectHost(host)) {
73
90
  throw new Error(
74
91
  `Redirect to untrusted host rejected: ${host}\n` +
75
- "Only github.com and githubusercontent.com are allowed."
92
+ "Only github.com and *.githubusercontent.com are allowed."
76
93
  );
77
94
  }
78
95
  }
@@ -159,10 +176,22 @@ async function main() {
159
176
  // ── Load bundled checksums (primary trust anchor) ─────────────────────────
160
177
  const bundledChecksumsPath = path.join(__dirname, "bin", "checksums.json");
161
178
  let bundledHash = "";
162
- try {
163
- const bundled = JSON.parse(fs.readFileSync(bundledChecksumsPath, "utf8"));
164
- bundledHash = (bundled[binaryName] || "").toLowerCase().trim();
165
- } catch (_) {}
179
+ if (fs.existsSync(bundledChecksumsPath)) {
180
+ try {
181
+ const bundled = JSON.parse(fs.readFileSync(bundledChecksumsPath, "utf8"));
182
+ bundledHash = (bundled[binaryName] || "").toLowerCase().trim();
183
+ if (!bundledHash) {
184
+ console.warn(`herozion: WARNING — no bundled hash entry for ${binaryName}.`);
185
+ }
186
+ } catch (parseErr) {
187
+ console.warn(`herozion: WARNING — invalid checksums.json: ${parseErr.message}`);
188
+ }
189
+ } else {
190
+ console.warn(
191
+ "herozion: WARNING — bin/checksums.json missing from npm package; " +
192
+ "falling back to remote CHECKSUMS.sha256 verification."
193
+ );
194
+ }
166
195
 
167
196
  console.log(`herozion: downloading ${binaryName} v${VERSION}...`);
168
197
  try {
@@ -170,6 +199,8 @@ async function main() {
170
199
  await download(url, tmpPath);
171
200
 
172
201
  const actualHash = sha256File(tmpPath);
202
+ let bundledVerified = false;
203
+ let remoteVerified = false;
173
204
 
174
205
  // ── Primary check: bundled checksum (trust anchor inside the npm package) ─
175
206
  if (bundledHash) {
@@ -183,6 +214,7 @@ async function main() {
183
214
  "This may indicate a supply-chain attack. Aborting installation."
184
215
  );
185
216
  }
217
+ bundledVerified = true;
186
218
  }
187
219
 
188
220
  // ── Secondary check: CHECKSUMS.sha256 from GitHub Releases ────────────────
@@ -198,56 +230,62 @@ async function main() {
198
230
  }
199
231
  return "";
200
232
  })();
201
- if (remoteHash && actualHash !== remoteHash) {
202
- fs.unlinkSync(tmpPath);
203
- throw new Error(
204
- `Remote checksum mismatch for ${binaryName}.\n` +
205
- ` Expected (remote) : ${remoteHash}\n` +
206
- ` Got : ${actualHash}\n` +
207
- "The downloaded binary does not match the GitHub Releases .sha256 file."
208
- );
233
+ if (remoteHash) {
234
+ if (actualHash !== remoteHash) {
235
+ fs.unlinkSync(tmpPath);
236
+ throw new Error(
237
+ `Remote checksum mismatch for ${binaryName}.\n` +
238
+ ` Expected (remote) : ${remoteHash}\n` +
239
+ ` Got : ${actualHash}\n` +
240
+ "The downloaded binary does not match the GitHub Releases CHECKSUMS.sha256 file."
241
+ );
242
+ }
243
+ remoteVerified = true;
209
244
  }
210
245
  } catch (checksumErr) {
211
- // If we have a valid bundled hash that already passed, remote failure is non-fatal
212
- if (!bundledHash) {
246
+ // If bundled verification already passed, remote failure is non-fatal.
247
+ if (!bundledVerified) {
213
248
  throw checksumErr;
214
249
  }
215
250
  console.warn(`herozion: WARNING — could not verify remote checksum: ${checksumErr.message}`);
216
251
  console.warn("Proceeding because bundled checksum verification passed.");
217
252
  }
218
253
 
219
- // Fail closed: refuse to install if neither hash source could be verified.
220
- if (!bundledHash) {
221
- // We reach here only when bundledHash is empty AND remote check was skipped/failed.
222
- // Secondary catch block re-throws when !bundledHash, so this path is belt-and-suspenders.
254
+ // Fail closed: require at least one independent verification source.
255
+ if (!bundledVerified && !remoteVerified) {
223
256
  fs.unlinkSync(tmpPath);
224
257
  throw new Error(
225
- `Cannot verify ${binaryName}: no bundled hash found and remote checksum unavailable.\n` +
258
+ `Cannot verify ${binaryName}: no bundled hash in package and remote checksum unavailable.\n` +
226
259
  "Installation aborted to prevent supply-chain risk.\n" +
227
260
  `Download manually from: ${BASE_URL}/${binaryName}`
228
261
  );
229
262
  }
230
263
 
231
- // Both checks passed — move binary into place
264
+ // Verification passed — move binary into place
232
265
  fs.renameSync(tmpPath, destPath);
233
266
  if (!isWindows) {
234
267
  fs.chmodSync(destPath, 0o755);
235
268
  }
236
269
  // Write version marker so we don't re-download on repeated installs
237
270
  fs.writeFileSync(markerPath, VERSION, "utf8");
238
- const verifiedBy = bundledHash ? "bundled + remote SHA-256" : "remote SHA-256";
271
+ const verifiedBy = bundledVerified && remoteVerified
272
+ ? "bundled + remote SHA-256"
273
+ : bundledVerified
274
+ ? "bundled SHA-256"
275
+ : "remote SHA-256";
239
276
  console.log(`herozion: binary installed successfully (verified: ${verifiedBy}).`);
240
277
  } catch (err) {
241
278
  // Clean up temp file if it exists
242
279
  try { fs.unlinkSync(tmpPath); } catch (_) {}
243
- // Non-fatal: warn and continue — the bin shim will give a clear error at runtime
244
- console.warn(`herozion: WARNING — could not download binary: ${err.message}`);
245
- console.warn(`You can download it manually from:`);
246
- console.warn(` ${BASE_URL}/${binaryName}`);
280
+ console.error(`herozion: ERROR — could not install binary: ${err.message}`);
281
+ console.error("Try reinstalling with scripts enabled:");
282
+ console.error(" npm install herozion --foreground-scripts");
283
+ console.error(`Or download manually from: ${BASE_URL}/${binaryName}`);
284
+ process.exit(1);
247
285
  }
248
286
  }
249
287
 
250
288
  main().catch((err) => {
251
289
  console.error("herozion install error:", err.message);
252
- // Do not exit(1) — a failed postinstall breaks `npm install` entirely
290
+ process.exit(1);
253
291
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "herozion",
3
- "version": "1.1.74",
3
+ "version": "1.1.76",
4
4
  "description": "Security audit and performance analysis CLI tool for developers",
5
5
  "keywords": [
6
6
  "security",
@@ -23,6 +23,7 @@
23
23
  },
24
24
  "files": [
25
25
  "bin/herozion.js",
26
+ "bin/checksums.json",
26
27
  "install.js",
27
28
  "README.md"
28
29
  ],