@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 +7 -7
- package/src/load-native.cjs +113 -3
- package/src/metadata.cjs +1 -1
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.
|
|
32
|
-
"@arcships/light-ocr-darwin-x64": "0.5.
|
|
33
|
-
"@arcships/light-ocr-linux-arm64-gnu": "0.5.
|
|
34
|
-
"@arcships/light-ocr-linux-x64-gnu": "0.5.
|
|
35
|
-
"@arcships/light-ocr-win32-arm64": "0.5.
|
|
36
|
-
"@arcships/light-ocr-win32-x64": "0.5.
|
|
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.
|
|
48
|
+
"version": "0.1.7"
|
|
49
49
|
}
|
package/src/load-native.cjs
CHANGED
|
@@ -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
|
|
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 || '')
|
|
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
|
-
|
|
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