@orbit-tools/cli 0.27.0 → 0.28.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbit-tools/cli",
3
- "version": "0.27.0",
3
+ "version": "0.28.1",
4
4
  "mcpName": "io.github.constellation-works/orbit",
5
5
  "description": "npm proxy for the Orbit CLI. Downloads the matching native binary from GitHub Releases on install and forwards all invocations.",
6
6
  "bin": {
@@ -190,13 +190,14 @@ function normalizeTrustedReleaseKeys(trustedKeys) {
190
190
  }
191
191
  const notAfter = key.notAfter || null;
192
192
  const revokedAt = key.revokedAt || null;
193
- // Mirror the shell-side awk regex (release_date_number) so a malformed
193
+ // Mirror the shell-side date format check (release_date_number) so a malformed
194
194
  // override like notAfter: "next month" fails closed instead of silently
195
195
  // becoming "never expires" under lexicographic comparison.
196
- if (notAfter !== null && !/^\d{4}-\d{2}-\d{2}$/.test(notAfter)) {
196
+ // The length check also rejects trailing newlines, which JS's $ permits.
197
+ if (notAfter !== null && (notAfter.length !== 10 || !/^\d{4}-\d{2}-\d{2}$/.test(notAfter))) {
197
198
  throw new Error(`trusted release signing key ${key.id} has invalid notAfter: ${notAfter} (expected YYYY-MM-DD)`);
198
199
  }
199
- if (revokedAt !== null && !/^\d{4}-\d{2}-\d{2}$/.test(revokedAt)) {
200
+ if (revokedAt !== null && (revokedAt.length !== 10 || !/^\d{4}-\d{2}-\d{2}$/.test(revokedAt))) {
200
201
  throw new Error(`trusted release signing key ${key.id} has invalid revokedAt: ${revokedAt} (expected YYYY-MM-DD)`);
201
202
  }
202
203
  return {
@@ -320,6 +321,24 @@ function extractTarGz(archivePath, destDir) {
320
321
  validateExtractedBinary(path.join(destDir, 'orbit'));
321
322
  }
322
323
 
324
+ // Stage the binary beside `destPath` and rename it into place so `destPath`
325
+ // only ever holds a complete, executable file. The bin shim treats an existing
326
+ // file as installed, so a partial copy (ENOSPC, kill) must never land there.
327
+ function installBinary(sourcePath, destPath, fsImpl = fs) {
328
+ const tempPath = path.join(
329
+ path.dirname(destPath),
330
+ `.${path.basename(destPath)}.${process.pid}.${crypto.randomBytes(4).toString('hex')}.tmp`
331
+ );
332
+ try {
333
+ fsImpl.copyFileSync(sourcePath, tempPath, fs.constants.COPYFILE_EXCL);
334
+ fsImpl.chmodSync(tempPath, 0o755);
335
+ fsImpl.renameSync(tempPath, destPath);
336
+ } catch (err) {
337
+ fsImpl.rmSync(tempPath, { force: true });
338
+ throw err;
339
+ }
340
+ }
341
+
323
342
  async function main() {
324
343
  if (process.env.ORBIT_SKIP_DOWNLOAD === '1') {
325
344
  log('ORBIT_SKIP_DOWNLOAD=1 set; skipping binary download.');
@@ -374,8 +393,7 @@ async function main() {
374
393
  if (!fs.existsSync(extractedBinary)) {
375
394
  fail(`extracted archive did not contain 'orbit' binary at ${extractedBinary}`);
376
395
  }
377
- fs.copyFileSync(extractedBinary, BIN_PATH);
378
- fs.chmodSync(BIN_PATH, 0o755);
396
+ installBinary(extractedBinary, BIN_PATH);
379
397
  } finally {
380
398
  fs.rmSync(tmpDir, { recursive: true, force: true });
381
399
  }
@@ -394,6 +412,7 @@ module.exports = {
394
412
  acknowledgeTrustedPublicKeyOverride,
395
413
  acknowledgeTrustedKeysOverride,
396
414
  extractTarGz,
415
+ installBinary,
397
416
  normalizeTrustedReleaseKeys,
398
417
  readTrustedReleaseKeys,
399
418
  validateArchiveMembers,