@polyjose/node 0.1.1 → 0.2.0

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/addon-sha256.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
- "darwin-arm64": "eb38505b0e82c9c6b0000422bc4818e82dc71aa3deaf9eb86e28b1ccfecc72b6",
3
- "darwin-x64": "237d8fe44770833f96a8d2ae5b2c41122d939d6ce4f1dfe8e8801ba3f7d064d1",
4
- "linux-arm64-gnu": "115fd95fce72f00ec5e0029abf44205068f9c4097ed52fd9d6a1618eaaa61ef5",
5
- "linux-arm64-musl": "de73ab1a2f5f61d9917e043aac756a2ca54021a788a994145d6e5e0f0211ac86",
6
- "linux-x64-gnu": "a13b6b39e8661230844fa43d2b8baa9396e55c2a7ccfb6917cb56bc033083aef",
7
- "linux-x64-musl": "1a114b20c79f6e5b974c42f01a2bc1f1cd2a9542fa98b05af5720c22f9544732",
8
- "win32-arm64-msvc": "2d1fa65e5b8b45cf2f9bc04e0a33063af9d6eb374fdea0cca5bd74bb072bd2aa",
9
- "win32-x64-msvc": "f69ec67864cbb3e898c36d8f0aae2643cf2c37f533320fb9d095cc7db6c5a3f2"
2
+ "darwin-arm64": "6d6c3958cd6f927b2e12f53c24d9c1902f3f2ffb979b3b4044994cffebcebbba",
3
+ "darwin-x64": "a418f5f08575bd9d2fb9fa4cbcf84a8710c7c6d312132f9bb7819db473f32cc6",
4
+ "linux-arm64-gnu": "cc8e8488d9350fe3b2d957eb45a6a9955b14f88b4fd0ac445e4934858c0c59be",
5
+ "linux-arm64-musl": "dbadc50f85953f80a869f7f223681963faefcac1c27ec27471be802a8819f402",
6
+ "linux-x64-gnu": "05a2a461d1e8f70743c1aedeeb07b57be6a601b3cce56b6e64fc1e93a29da594",
7
+ "linux-x64-musl": "4228de8d078831836078a16ebd1511d3583cb58646c308ac9e4acf7c3639834e",
8
+ "win32-arm64-msvc": "44dba875cf64e02bf85844a1b5a42de26b541d33ed6d2e45b69a39060577f3df",
9
+ "win32-x64-msvc": "a9d80de28164779bba224cab505035a2d711309583e9c763dfd0b102c8fe8ffa"
10
10
  }
package/backend.d.ts CHANGED
@@ -1,90 +1,86 @@
1
1
  /**
2
- * The napi-rs backend of `@polyjose/node` (ADR 0031 §1-§2, `bindings/js/BACKEND.md`).
2
+ * The napi-rs backend of `@polyjose/node` (ADR 0031 §1-§2, `bindings/js/BACKEND.md`; packaging as
3
+ * superseded by the feature 0017 ADR: one package that ships the addon of all eight targets).
3
4
  *
4
- * The generated loader `binding.cjs` (`napi build --platform --js binding.cjs`) already is the
5
- * backend contract: one function per ADR 0030 §4 operation, camelCase, in the ADR's argument
6
- * order, rejecting with an `Error` whose `code` is one of `errorCodes()` (ADR 0030 §5). So this
7
- * module only loads it, checks what it loaded and gives it the facade's `Backend` type; it adds no
8
- * JOSE logic and renames nothing (ADR 0030 §1: loading and integrity checks are entry idiom).
5
+ * The native addon `polyjose.<platform>.node` already is the backend contract: one function per
6
+ * ADR 0030 §4 operation, camelCase, in the ADR's argument order, rejecting with an `Error` whose
7
+ * `code` is one of `errorCodes()` (ADR 0030 §5). Its types are the `binding.d.ts` that
8
+ * `napi build --dts` generates. So this module only picks the addon for this host, authenticates
9
+ * it, loads it and gives it the facade's `Backend` type; it adds no JOSE logic and renames nothing
10
+ * (ADR 0030 §1: loading and integrity checks are entry idiom).
9
11
  *
10
- * The loader is CommonJS (it picks the platform's `.node` file at run time), loaded here through
11
- * `createRequire`, so the package stays ESM-only (ADR 0031 §3). Its types come from the generated
12
- * `binding.d.ts`.
12
+ * **No resolving loader (feature 0017; review-security.md M1, feature 0007 F02).** napi-rs's
13
+ * generated loader picks the first module that loads from an override variable, the package
14
+ * directory, every ancestor `node_modules`, `NODE_PATH` and a WASI chain, so a JavaScript or native
15
+ * file planted in any of them could become the crypto backend and "verify" any token. polyjose
16
+ * builds no such loader (`napi build --no-js`) and resolves no module name at all: this file reads
17
+ * only files of its own package directory, `dirname(import.meta.url)`, by absolute path. It never
18
+ * reads `process.env`, so neither `NODE_PATH` nor any `NAPI_RS_*` variable changes what loads.
13
19
  *
14
- * **Fail closed (feature 0007 F02; review-code.md SF-4, review-security.md M1).** The generated
15
- * loader returns the first module that loads, of any kind:
16
- * - `NAPI_RS_NATIVE_LIBRARY_PATH`, if set;
17
- * - then `./polyjose.<platform>.node` next to it;
18
- * - then `require('@polyjose/node-<platform>')`, which walks every ancestor `node_modules` and
19
- * `NODE_PATH`;
20
- * - then a WASI chain: `./polyjose.wasi.cjs`, then `@polyjose/node-wasm32-wasi`. The napi-rs CLI
21
- * 3.10.12 template always emits it; no `napi build` option omits it, and
22
- * `NAPI_RS_FORCE_WASI` / `NAPI_RS_WASI_FLAVOR` can select it.
23
- * It stamps a JavaScript module found that way `"native"` too. So a JS file planted in an
24
- * ancestor directory or on `NODE_PATH` would become the crypto backend and could "verify" any
25
- * token. polyjose publishes only native addons (ADR 0031 §2: eight targets, no WASI build;
26
- * `@polyjose/wasm` is a separate package). The generated file is never edited; this module wraps
27
- * it and accepts nothing else.
28
- *
29
- * **Before loading,** nothing runs unless every module the loader could pick is a native addon:
30
- * 1. Neither WASI variable is set, whatever its value.
31
- * 2. No WASI candidate resolves.
32
- * 3. Every native candidate that resolves is a `.node` file: the override path, and for every
33
- * platform the loader probes, `./polyjose.<platform>.node` and `@polyjose/node-<platform>`.
34
- * `@polyjose/node-<platform>/package.json`, which the loader also reads, must be a
35
- * `package.json`.
36
- * Resolution is the loader's own: `backend.js` sits next to `binding.cjs`, and `require.resolve`
37
- * uses the same directory, `node_modules` walk, `NODE_PATH` and symlink handling as the loader's
38
- * `require`, and runs no code. Node loads a `.node` file only as a native addon, never as
39
- * JavaScript.
40
- *
41
- * **The hash pin (feature 0007 F07; bindings security audit N-M1, the JVM's 0008 F05 bar).** Steps
42
- * 1-6 make sure the package loads a native addon at this version, but `version()` is what the
43
- * addon itself says, and a native addon is as easy to plant as a JavaScript file. So before the
44
- * loader runs, this module also *authenticates* the addon:
45
- * a. `addon-sha256.json`, next to this file, maps each platform polyjose built this package for
46
- * to its addon's SHA-256 (FIPS 180-4). It is generated, never hand-edited:
47
- * `scripts/addon-hashes.sh`, run by `scripts/build.sh` for a local build and by the release
48
- * assembly of the main package for every published platform. No map, a malformed one, or no
49
- * entry for this host refuses the load: nothing is trusted by default.
50
- * b. It picks the one file the loader would load on this host (the platform name of
51
- * `hostPlatform`, the loader's own): `NAPI_RS_NATIVE_LIBRARY_PATH` when set, else
52
- * `./polyjose.<platform>.node`, else `@polyjose/node-<platform>`, resolved as above. A
53
- * caller's override is not refused; it is held to the same pin.
54
- * c. It reads that file once, hashes those bytes with `node:crypto` and refuses on a mismatch,
55
- * naming the platform, the source and both hashes. There is no fallback to another candidate.
56
- * d. It writes the same bytes, with `O_EXCL`, into a new directory from `fs.mkdtempSync` under
20
+ * **The steps (the F07 hash-pin model, bindings security audit N-M1, the JVM's 0008 F05 bar):**
21
+ * 1. The platform name (`detectPlatform`): `darwin-<arch>`, `win32-<arch>-msvc`, or
22
+ * `linux-<arch>-gnu|musl` for x64 and arm64, with napi-rs's names. Any other host is refused
23
+ * by name, pointing at `@polyjose/wasm`, which runs everywhere.
24
+ * 2. `addon-sha256.json`, in the package directory, maps each shipped platform to its addon's
25
+ * SHA-256 (FIPS 180-4). It is generated, never hand-edited: `scripts/addon-hashes.sh`, run by
26
+ * `scripts/build.sh` for a local build and by `scripts/pack-main.sh` over the eight addons the
27
+ * package ships. No map, a malformed one, or no entry for this host refuses the load: nothing
28
+ * is trusted by default.
29
+ * 3. It reads `polyjose.<platform>.node` from the package directory once, hashes those bytes with
30
+ * `node:crypto` and refuses on a mismatch, naming the platform, the file and both hashes.
31
+ * There is no fallback to another file.
32
+ * 4. It writes the same bytes, with `O_EXCL`, into a new directory from `fs.mkdtempSync` under
57
33
  * `os.tmpdir()` (mode 0700 on POSIX; on Windows the per-user `%TEMP%` ACL), under a random
58
34
  * leaf name (macOS looks a leaf name up in `DYLD_LIBRARY_PATH` before the path), checks the
59
- * copy's SHA-256 again, and points `NAPI_RS_NATIVE_LIBRARY_PATH` at it for the one
60
- * synchronous `require("./binding.cjs")`. The loader's override branch loads exactly that
61
- * path and probes nothing else, so between the check and `dlopen` only this user (who can
62
- * already change anything they run) could swap the file; whoever can write the installed
63
- * file or an ancestor `node_modules` can't. The caller's environment is restored right
64
- * after, and the copy is deleted (a loaded library stays mapped; Windows can't delete a
65
- * loaded DLL, so there the copy stays in the temporary directory).
66
- *
67
- * **After loading,** what the loader returned must be:
68
- * 4. the exports of exactly one `.node` module in the CommonJS cache, the private copy of step
69
- * d, shared only with a loader (`binding.cjs`), so no JavaScript module wraps or re-exports
70
- * it, and a `binding.cjs` that other code required first (with its own addon) is refused;
71
- * 5. stamped `__napiBindingTarget === "native"`;
72
- * 6. at this package's version: `version()` must equal `package.json`'s version. That is the
73
- * workspace version, which every release keeps in step (feature 0007 T04). So an addon from
74
- * another release is refused, whatever `NAPI_RS_ENFORCE_VERSION_CHECK` says (SF-4 item 3).
35
+ * copy's SHA-256 again, and `require`s that absolute path, which Node's `.node` handler hands
36
+ * to `dlopen`. So between the check and `dlopen` only this user (who can already change
37
+ * anything they run) could swap the file; whoever can write the installed package can't. The
38
+ * copy is deleted right after (a loaded library stays mapped; Windows can't delete a loaded
39
+ * DLL, so there the copy stays in the temporary directory).
40
+ * 5. What loaded must be the exports of exactly that one module in the CommonJS cache, the
41
+ * private copy, so no JavaScript module wraps or re-exports it.
42
+ * 6. It must be this package's version: `version()` equals `package.json`'s version, the
43
+ * workspace version every release keeps in step (feature 0007 T04), so an addon of another
44
+ * release is refused.
75
45
  * Every refusal is an `Error` with `code` `ERR_POLYJOSE_ADDON_REFUSED`. This doesn't defend against
76
46
  * code already running in the process, which can patch anything. It makes sure the package never
77
47
  * selects a substitute.
78
48
  *
79
- * `test/loader.test.ts` pins the loader's load targets and checks `LOADER_PLATFORMS` against them.
80
- * So a CLI bump that adds a candidate fails there before this module can miss it.
49
+ * `test/loader.test.ts` pins what this module loads (its built-ins and its one `require`), the
50
+ * platform detection, and each refusal.
81
51
  */
82
52
  import type { Backend } from "./facade/backend.js";
53
+ /** The one `fs` call platform detection makes (so tests can fake it). */
54
+ interface TextReader {
55
+ readFileSync(path: string, encoding: "utf8"): string;
56
+ }
57
+ interface ProcessReport {
58
+ excludeNetwork: boolean;
59
+ getReport(): {
60
+ readonly header?: {
61
+ readonly glibcVersionRuntime?: unknown;
62
+ };
63
+ readonly sharedObjects?: unknown;
64
+ } | null;
65
+ }
66
+ /** What `detectPlatform` reads of `process`. */
67
+ interface PlatformHost {
68
+ readonly platform: string;
69
+ readonly arch: string;
70
+ readonly report?: ProcessReport;
71
+ }
72
+ /**
73
+ * The eight platforms whose addon this package ships (ADR 0031 §2's targets): the only keys a map
74
+ * may have. `test/loader.test.ts` checks them against package.json's `napi.targets` and `files`.
75
+ */
76
+ export declare const PLATFORMS: readonly ["darwin-x64", "darwin-arm64", "linux-x64-gnu", "linux-arm64-gnu", "linux-x64-musl", "linux-arm64-musl", "win32-x64-msvc", "win32-arm64-msvc"];
83
77
  /**
84
- * Every platform name the generated loader probes: the eight polyjose publishes (ADR 0031 §2) and
85
- * the napi-rs template's others, which are never published. `test/loader.test.ts` checks that this
86
- * is exactly the loader's set.
78
+ * Step 1: this host's platform name, napi-rs's (`napi build --platform` names the addon after it):
79
+ * `darwin-x64|arm64`, `win32-x64|arm64-msvc`, `linux-x64|arm64-gnu|musl`. Any other platform or
80
+ * architecture is `<platform>-<arch>`, which `PLATFORMS` does not list. On Linux it reads
81
+ * /usr/bin/ldd and may take a process report (`isMusl`); it refuses when it cannot tell the libc.
87
82
  */
88
- export declare const LOADER_PLATFORMS: readonly ["darwin-x64", "darwin-arm64", "linux-x64-gnu", "linux-arm64-gnu", "linux-x64-musl", "linux-arm64-musl", "win32-x64-msvc", "win32-arm64-msvc", "android-arm64", "android-arm-eabi", "win32-x64-gnu", "win32-ia32-msvc", "darwin-universal", "freebsd-x64", "freebsd-arm64", "freebsd-ppc64", "linux-arm-musleabihf", "linux-arm-gnueabihf", "linux-loong64-musl", "linux-loong64-gnu", "linux-riscv64-musl", "linux-riscv64-gnu", "linux-ppc64-gnu", "linux-s390x-gnu", "openharmony-arm64", "openharmony-x64", "openharmony-arm"];
89
- /** The napi loader, typed as the facade's backend. */
83
+ export declare function detectPlatform(host: PlatformHost, fs: TextReader): string;
84
+ /** The native addon, typed as the facade's backend. */
90
85
  export declare const backend: Backend;
86
+ export {};