@arcships/light-ocr-runtime 0.1.6 → 0.1.7

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
@@ -28,12 +28,12 @@
28
28
  "module": "./src/index.mjs",
29
29
  "name": "@arcships/light-ocr-runtime",
30
30
  "optionalDependencies": {
31
- "@arcships/light-ocr-darwin-arm64": "0.5.6",
32
- "@arcships/light-ocr-darwin-x64": "0.5.6",
33
- "@arcships/light-ocr-linux-arm64-gnu": "0.5.6",
34
- "@arcships/light-ocr-linux-x64-gnu": "0.5.6",
35
- "@arcships/light-ocr-win32-arm64": "0.5.6",
36
- "@arcships/light-ocr-win32-x64": "0.5.6"
31
+ "@arcships/light-ocr-darwin-arm64": "0.5.7",
32
+ "@arcships/light-ocr-darwin-x64": "0.5.7",
33
+ "@arcships/light-ocr-linux-arm64-gnu": "0.5.7",
34
+ "@arcships/light-ocr-linux-x64-gnu": "0.5.7",
35
+ "@arcships/light-ocr-win32-arm64": "0.5.7",
36
+ "@arcships/light-ocr-win32-x64": "0.5.7"
37
37
  },
38
38
  "publishConfig": {
39
39
  "access": "public",
@@ -45,5 +45,5 @@
45
45
  },
46
46
  "type": "commonjs",
47
47
  "types": "./src/index.d.ts",
48
- "version": "0.1.6"
48
+ "version": "0.1.7"
49
49
  }
@@ -1,9 +1,101 @@
1
1
  'use strict';
2
2
 
3
+ const { spawnSync } = require('node:child_process');
3
4
  const crypto = require('node:crypto');
4
5
  const fs = require('node:fs');
5
6
  const path = require('node:path');
6
7
 
8
+ // macOS-only integrity relaxation for downstream re-signing.
9
+ //
10
+ // macOS packaging pipelines re-sign the native payload with a Developer ID
11
+ // (or ad-hoc) identity, and osx-sign rewrites the LC_CODE_SIGNATURE blob of
12
+ // every Mach-O under Contents --force. That changes both file size and
13
+ // sha256, so the strict descriptor comparison alone rejects otherwise intact
14
+ // binaries. On macOS only, a Mach-O whose signature verifies AND whose
15
+ // signing identity matches the host process is accepted as an equivalent
16
+ // integrity proof: same TeamIdentifier as process.execPath, or both sides
17
+ // ad-hoc signed. Ad-hoc signatures are reproducible by anyone, so this
18
+ // relaxation stays macOS-only and documented; win32/linux payloads are never
19
+ // re-signed and keep the strict bytes+sha256 gate.
20
+
21
+ const MACHO_MAGIC = new Set([
22
+ 0xfeedface, // 32-bit big-endian
23
+ 0xcefaedfe, // 32-bit little-endian
24
+ 0xfeedfacf, // 64-bit big-endian
25
+ 0xcffaedfe, // 64-bit little-endian
26
+ 0xcafebabe, // universal (fat) big-endian
27
+ 0xbebafeca, // universal (fat) little-endian
28
+ ]);
29
+
30
+ function isMachO(filename) {
31
+ const fd = fs.openSync(filename, 'r');
32
+ try {
33
+ const magic = Buffer.allocUnsafe(4);
34
+ if (fs.readSync(fd, magic, 0, 4, 0) !== 4) return false;
35
+ return MACHO_MAGIC.has(magic.readUInt32BE(0));
36
+ } finally {
37
+ fs.closeSync(fd);
38
+ }
39
+ }
40
+
41
+ function codesignOutput(filename) {
42
+ const result = spawnSync('codesign', ['-dv', '--verbose=4', filename], {
43
+ encoding: 'utf8',
44
+ stdio: ['ignore', 'pipe', 'pipe'],
45
+ });
46
+ if (result.error || result.status !== 0) return null;
47
+ return `${result.stdout}\n${result.stderr}`;
48
+ }
49
+
50
+ // Returns { teamIdentifier, adhoc } for a readable signature, or null when
51
+ // the file carries no readable signature (unsigned, corrupt, not Mach-O).
52
+ function codesignIdentity(filename) {
53
+ const output = codesignOutput(filename);
54
+ if (!output) return null;
55
+ if (/^Signature=adhoc\s*$/m.test(output)) {
56
+ return { teamIdentifier: null, adhoc: true };
57
+ }
58
+ const teamLine = output.match(/^TeamIdentifier=(.*)$/m);
59
+ const teamIdentifier = teamLine ? teamLine[1].trim() : '';
60
+ if (teamIdentifier && teamIdentifier !== 'not set') {
61
+ return { teamIdentifier, adhoc: false };
62
+ }
63
+ return null;
64
+ }
65
+
66
+ function codesignVerifies(filename) {
67
+ const result = spawnSync('codesign', ['--verify', '--strict', filename], {
68
+ encoding: 'utf8',
69
+ stdio: ['ignore', 'pipe', 'pipe'],
70
+ });
71
+ return !result.error && result.status === 0;
72
+ }
73
+
74
+ let hostCodesignIdentityCache;
75
+
76
+ function hostCodesignIdentity() {
77
+ if (hostCodesignIdentityCache === undefined) {
78
+ hostCodesignIdentityCache = codesignIdentity(process.execPath);
79
+ }
80
+ return hostCodesignIdentityCache;
81
+ }
82
+
83
+ // Accept a re-signature only when it was made with the same identity as the
84
+ // host process: identical TeamIdentifier, or both sides ad-hoc signed.
85
+ function signerMatchesHost(artifactIdentity, host = hostCodesignIdentity()) {
86
+ if (!artifactIdentity || !host) return false;
87
+ if (artifactIdentity.adhoc && host.adhoc) return true;
88
+ return !artifactIdentity.adhoc && !host.adhoc &&
89
+ artifactIdentity.teamIdentifier === host.teamIdentifier;
90
+ }
91
+
92
+ function acceptsSignedMacOSMutation(filename) {
93
+ if (process.platform !== 'darwin') return false;
94
+ if (!isMachO(filename)) return false;
95
+ if (!codesignVerifies(filename)) return false;
96
+ return signerMatchesHost(codesignIdentity(filename));
97
+ }
98
+
7
99
  function adapterError(code, message, detail, cause) {
8
100
  const error = new Error(message, cause === undefined ? undefined : { cause });
9
101
  error.name = 'OcrError';
@@ -108,12 +200,23 @@ function verifyArtifact(root, artifact, field) {
108
200
  if (!stats.isFile() || stats.isSymbolicLink()) {
109
201
  throw adapterError('package_load_failed', 'Descriptor artifact is not a regular file', artifact.path);
110
202
  }
111
- if (!Number.isSafeInteger(artifact.bytes) || artifact.bytes < 1 || stats.size !== artifact.bytes) {
203
+ if (!Number.isSafeInteger(artifact.bytes) || artifact.bytes < 1) {
112
204
  throw adapterError('package_load_failed', 'Descriptor artifact byte count mismatch', artifact.path);
113
205
  }
114
- if (!/^[a-f0-9]{64}$/.test(artifact.sha256 || '') || sha256(filename) !== artifact.sha256) {
206
+ if (!/^[a-f0-9]{64}$/.test(artifact.sha256 || '')) {
115
207
  throw adapterError('package_load_failed', 'Descriptor artifact hash mismatch', artifact.path);
116
208
  }
209
+ if (stats.size !== artifact.bytes) {
210
+ if (!acceptsSignedMacOSMutation(filename)) {
211
+ throw adapterError('package_load_failed', 'Descriptor artifact byte count mismatch', artifact.path);
212
+ }
213
+ return filename;
214
+ }
215
+ if (sha256(filename) !== artifact.sha256) {
216
+ if (!acceptsSignedMacOSMutation(filename)) {
217
+ throw adapterError('package_load_failed', 'Descriptor artifact hash mismatch', artifact.path);
218
+ }
219
+ }
117
220
  return filename;
118
221
  }
119
222
 
@@ -496,4 +599,11 @@ function loadNative() {
496
599
  }
497
600
  }
498
601
 
499
- module.exports = { loadNative, validateRuntimeDescriptor };
602
+ const macOSSignature = Object.freeze({
603
+ isMachO,
604
+ codesignIdentity,
605
+ codesignVerifies,
606
+ signerMatchesHost,
607
+ });
608
+
609
+ module.exports = { loadNative, validateRuntimeDescriptor, macOSSignature };
package/src/metadata.cjs CHANGED
@@ -1,3 +1,3 @@
1
1
  'use strict';
2
2
 
3
- module.exports = Object.freeze({ coreVersion: '0.5.6' });
3
+ module.exports = Object.freeze({ coreVersion: '0.5.7' });