@scriptc/runtime 0.0.11 → 0.0.13

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/src/scr_stream.c CHANGED
@@ -786,7 +786,7 @@ static void scr_stream_resume_kick(ScrStream *s) {
786
786
  * parameters implicitly-any: the compiler-emitted invoke thunks box the
787
787
  * chunk/encoding/error into dyn, and the COMPLETION callback arrives as
788
788
  * a callable dyn function whose glue lands here — arguments arrive as
789
- * borrowed DOM values: the error is null/undefined (none), an
789
+ * borrowed dyn values: the error is null/undefined (none), an
790
790
  * error-shaped object ({message}), or a string; transform/flush data is
791
791
  * bytes, a string, or absent. clo->caps[0] boxes the retained stream. */
792
792
 
@@ -870,7 +870,7 @@ ScrDyn *scr_stream_done_dyn_l(ScrClosure *clo, ScrDyn *const *args, size_t argc)
870
870
  }
871
871
 
872
872
  /* Checked-dynamic chunks (the JS lane's push/write of any-typed values):
873
- * dispatch by DOM tag — bytes copy out, strings convert utf8, null takes
873
+ * dispatch by dyn tag — bytes copy out, strings convert utf8, null takes
874
874
  * the null path (EOF for push; ERR_STREAM_NULL_VALUES for write), and
875
875
  * anything else throws the write TypeError (Node would throw
876
876
  * ERR_INVALID_ARG_TYPE — approximated by the same shape). Borrow d. */
@@ -1531,7 +1531,7 @@ void scr_stream_set_flush(ScrStream *s, ScrClosure *cb /*moves*/, ScrStreamPlain
1531
1531
  * keys are ignored exactly like Node), and callback slots holding
1532
1532
  * callable values (Node's `typeof === 'function'` guard — anything else
1533
1533
  * leaves the slot to the prototype/fallback) bind as closures whose one
1534
- * cap boxes the dyn callable; the invs below build the DOM arguments the
1534
+ * cap boxes the dyn callable; the invs below build the dyn arguments the
1535
1535
  * way the compiler-emitted dyn thunks do (chunk as Buffer-flavored bytes,
1536
1536
  * encoding 'buffer', the error via the boundary encoding, the completion
1537
1537
  * callback as a callable dyn over the *_done glue). Options that Node's
@@ -3038,7 +3038,7 @@ static void scr_stream_run_tick(ScrStreamTick *t) {
3038
3038
  /* Fully closed: nothing calls the option callbacks again (Node's
3039
3039
  * contract), so drop them here — this breaks the reference cycle
3040
3040
  * a callback capturing its own stream forms, including the dyn-
3041
- * options closures whose stream edge rides through a DOM function
3041
+ * options closures whose stream edge rides through a dyn function
3042
3042
  * value the cycle collector cannot traverse. */
3043
3043
  if (st->r.read_cb) { scr_closure_release(st->r.read_cb); st->r.read_cb = NULL; }
3044
3044
  if (st->w.write_cb) { scr_closure_release(st->w.write_cb); st->w.write_cb = NULL; }
package/src/scr_tls.c CHANGED
@@ -350,7 +350,30 @@ typedef struct ScrTlsCli {
350
350
  static mbedtls_x509_crt scr_tls_system_roots;
351
351
  static bool scr_tls_system_roots_loaded = false;
352
352
 
353
+ /* setDefaultCACertificates' replacement anchors (scr_tls_ca.c — always
354
+ * compiled alongside this unit): parsed lazily per generation, so each
355
+ * set re-parses once and every later dial reuses the chain. An EMPTY
356
+ * replacement keeps an initialized, certificate-free chain — every
357
+ * verification fails, Node's own consequence of trusting nothing. */
358
+ static mbedtls_x509_crt scr_tls_override_roots;
359
+ static uint64_t scr_tls_override_parsed_gen = 0;
360
+
353
361
  static mbedtls_x509_crt *scr_tls_system_ca(void) {
362
+ const char *pem = NULL;
363
+ size_t pem_len = 0;
364
+ uint64_t gen = 0;
365
+ if (scr_tls_ca_default_override(&pem, &pem_len, &gen)) {
366
+ if (gen != scr_tls_override_parsed_gen) {
367
+ if (scr_tls_override_parsed_gen != 0) mbedtls_x509_crt_free(&scr_tls_override_roots);
368
+ mbedtls_x509_crt_init(&scr_tls_override_roots);
369
+ /* +1 for the NUL — mbedTLS's PEM mode wants it counted. A parse
370
+ * failure inside one block leaves the chain with the blocks that
371
+ * did parse (the scr_tls_ca.c divergence note). */
372
+ if (pem_len > 0) (void)mbedtls_x509_crt_parse(&scr_tls_override_roots, (const unsigned char *)pem, pem_len + 1);
373
+ scr_tls_override_parsed_gen = gen;
374
+ }
375
+ return &scr_tls_override_roots;
376
+ }
354
377
  if (!scr_tls_system_roots_loaded) {
355
378
  scr_tls_system_roots_loaded = true;
356
379
  mbedtls_x509_crt_init(&scr_tls_system_roots);
@@ -983,8 +1006,20 @@ ScrHttpClientReq *scr_https_request(ScrStr *host /*borrowed*/, double port,
983
1006
  fn, 443, &scr_tls_cli_wrap, cli);
984
1007
  }
985
1008
 
1009
+ ScrHttpClientReq *scr_https_request_agent(ScrStr *host /*borrowed*/, double port,
1010
+ ScrStr *path /*borrowed*/, ScrStr *method /*borrowed*/,
1011
+ double timeout_ms, ScrArr *header_pairs /*borrowed*/,
1012
+ bool auto_end, bool reject_unauthorized,
1013
+ const char *ca /*borrowed, len 0 = none*/, size_t ca_len,
1014
+ const ScrDyn *agent /*borrowed*/,
1015
+ ScrClosure *cb /*moves, nullable*/, ScrHttpRespFn fn) {
1016
+ ScrTlsCli *cli = scr_tls_cli_new(host, reject_unauthorized, ca, ca_len);
1017
+ return scr_http_request_agent_ex(host, port, path, method, timeout_ms, header_pairs, auto_end,
1018
+ agent, cb, fn, 443, &scr_tls_cli_wrap, cli);
1019
+ }
1020
+
986
1021
  /* ── RUNTIME options records (the divergence-66 stance) ────────────────
987
- * A non-literal options value — the checked-dynamic JS lane's DOM record
1022
+ * A non-literal options value — the checked-dynamic JS lane's dyn record
988
1023
  * — reads its members at RUNTIME. Members the literal path lowers take
989
1024
  * the same lowering; members whose literal forms are compile fences
990
1025
  * become runtime gates that THROW the catchable fence instead of
@@ -1002,7 +1037,7 @@ static void scr_tls_opt_fence(const char *api, const char *key, const char *hint
1002
1037
  scr_throw_error(SCR_ERR_ERROR, scr_jb_finish(&b));
1003
1038
  }
1004
1039
 
1005
- /* JS truthiness over the DOM kinds — Node reads requestCert /
1040
+ /* JS truthiness over the dyn kinds — Node reads requestCert /
1006
1041
  * rejectUnauthorized through `if (options.x)`. */
1007
1042
  static bool scr_tls_dyn_truthy(const ScrDyn *v) {
1008
1043
  switch (v->kind) {
@@ -1219,9 +1254,11 @@ static bool scr_tls_opts_validate(const ScrDyn *opts) {
1219
1254
  }
1220
1255
 
1221
1256
  /* The server-options walk. Fills the cert/key out-params (+1 each) from a
1222
- * DOM options record; false = exception pending (partial results released). */
1223
- static bool scr_tls_srv_opts_walk(const ScrDyn *opts, const char *api, ScrBytes **cert_out,
1224
- ScrBytes **key_out) {
1257
+ * dyn options record; false = exception pending (partial results released).
1258
+ * Exported runtime-internally: scr_http2.c's createSecureServerDyn rides
1259
+ * the same walk (scr_http2.c always links alongside this unit). */
1260
+ bool scr_tls_srv_opts_walk(const ScrDyn *opts, const char *api, ScrBytes **cert_out,
1261
+ ScrBytes **key_out) {
1225
1262
  *cert_out = NULL;
1226
1263
  *key_out = NULL;
1227
1264
  if (opts == NULL || opts->kind != SCR_DYN_OBJ) {
@@ -0,0 +1,288 @@
1
+ /* node:tls — the CA-store introspection slice: getCACertificates,
2
+ * rootCertificates, and setDefaultCACertificates (scr_runtime.h has the
3
+ * contracts). Its own link gate ("tlsca." libCalls), deliberately free
4
+ * of mbedTLS: everything here is PEM-BLOCK bookkeeping over the host
5
+ * bundle, so a program that only inspects the CA store keeps the lean
6
+ * link line. scr_tls.c (when it links — cc.ts compiles this unit
7
+ * alongside it) consults scr_tls_ca_default_override for its client
8
+ * trust anchors, which is what makes setDefaultCACertificates LIVE
9
+ * semantics: the next https/tls dial verifies against the replaced set.
10
+ *
11
+ * Divergences, documented: the host bundle stands in for Node's
12
+ * compiled-in Mozilla roots ('bundled' and rootCertificates) AND for the
13
+ * platform store ('system') — the same /etc/ssl/cert.pem stance
14
+ * scr_tls.c's anchors established; and set-time validation is PEM-BLOCK
15
+ * shaped (a well-formed block with corrupt DER inside is kept here where
16
+ * Node's X509 parse would drop it — such a cert then fails verification
17
+ * instead, the same observable outcome one step later). Deduplication is
18
+ * byte-exact over the extracted block where Node compares parsed X509
19
+ * identities; re-encoded duplicates of one certificate stay distinct
20
+ * entries, which no equality the tests observe can distinguish without a
21
+ * parser. */
22
+ #include "scr_runtime.h"
23
+
24
+ #include <stdio.h>
25
+ #include <stdlib.h>
26
+ #include <string.h>
27
+
28
+ /* The per-type caches: +1 held here for the process lifetime (an atexit
29
+ * teardown releases them so the RC audit stays clean). 'default' also
30
+ * keeps the concatenated PEM buffer scr_tls.c parses into anchors. */
31
+ static ScrArr *scr_ca_bundled = NULL;
32
+ static ScrArr *scr_ca_system = NULL;
33
+ static ScrArr *scr_ca_extra = NULL;
34
+ static ScrArr *scr_ca_default = NULL;
35
+ static char *scr_ca_override_buf = NULL;
36
+ static size_t scr_ca_override_len = 0;
37
+ static uint64_t scr_ca_override_gen = 0; /* 0 = never set */
38
+
39
+ static void scr_ca_oom(void) {
40
+ fputs("scriptc: out of memory\n", stderr);
41
+ abort();
42
+ }
43
+
44
+ static void scr_ca_teardown(void) {
45
+ if (scr_ca_bundled != NULL) scr_arr_release(scr_ca_bundled);
46
+ if (scr_ca_system != NULL) scr_arr_release(scr_ca_system);
47
+ if (scr_ca_extra != NULL) scr_arr_release(scr_ca_extra);
48
+ if (scr_ca_default != NULL) scr_arr_release(scr_ca_default);
49
+ scr_ca_bundled = scr_ca_system = scr_ca_extra = scr_ca_default = NULL;
50
+ free(scr_ca_override_buf);
51
+ scr_ca_override_buf = NULL;
52
+ }
53
+
54
+ static bool scr_ca_teardown_armed = false;
55
+
56
+ static void scr_ca_arm_teardown(void) {
57
+ if (!scr_ca_teardown_armed) {
58
+ scr_ca_teardown_armed = true;
59
+ atexit(scr_ca_teardown);
60
+ }
61
+ }
62
+
63
+ /* Portable bounded substring search (memmem is GNU/BSD-only; win32 has
64
+ * neither). Needles here are short constant markers — the naive scan is
65
+ * plenty. */
66
+ static const char *scr_ca_find(const char *hay, size_t hay_len, const char *needle, size_t needle_len) {
67
+ if (needle_len == 0 || hay_len < needle_len) return NULL;
68
+ for (size_t i = 0; i + needle_len <= hay_len; i++) {
69
+ if (hay[i] == needle[0] && memcmp(hay + i, needle, needle_len) == 0) return hay + i;
70
+ }
71
+ return NULL;
72
+ }
73
+
74
+ /* Every "-----BEGIN CERTIFICATE-----" ... "-----END CERTIFICATE-----"
75
+ * block of `text` (other PEM kinds — keys, CRLs — skip, like Node's CA
76
+ * loaders), pushed onto `arr` as one string each, END line included.
77
+ * Returns the number of blocks found. */
78
+ static size_t scr_ca_push_pem_blocks(ScrArr *arr, const char *text, size_t len) {
79
+ static const char BEGIN[] = "-----BEGIN CERTIFICATE-----";
80
+ static const char END[] = "-----END CERTIFICATE-----";
81
+ size_t found = 0;
82
+ const char *p = text;
83
+ const char *limit = text + len;
84
+ while (p < limit) {
85
+ const char *b = scr_ca_find(p, (size_t)(limit - p), BEGIN, sizeof BEGIN - 1);
86
+ if (b == NULL) break;
87
+ const char *e = scr_ca_find(b, (size_t)(limit - b), END, sizeof END - 1);
88
+ if (e == NULL) break;
89
+ const char *stop = e + (sizeof END - 1);
90
+ /* Node's entries carry the block's trailing newline when the source
91
+ * had one — keep it so round-trips through writeFileSync + parse
92
+ * stay concatenation-safe. */
93
+ if (stop < limit && *stop == '\r') stop++;
94
+ if (stop < limit && *stop == '\n') stop++;
95
+ scr_arr_push_ref(arr, scr_str_new(b, (size_t)(stop - b)));
96
+ found++;
97
+ p = stop;
98
+ }
99
+ return found;
100
+ }
101
+
102
+ /* One whole file, malloc'd (NULL on any failure — absent bundles answer
103
+ * empty stores, the same silence Node has on hosts without the file). */
104
+ static char *scr_ca_read_file(const char *path, size_t *out_len) {
105
+ FILE *f = fopen(path, "rb");
106
+ if (f == NULL) return NULL;
107
+ if (fseek(f, 0, SEEK_END) != 0) {
108
+ fclose(f);
109
+ return NULL;
110
+ }
111
+ long sz = ftell(f);
112
+ if (sz < 0) {
113
+ fclose(f);
114
+ return NULL;
115
+ }
116
+ rewind(f);
117
+ char *buf = malloc((size_t)sz + 1);
118
+ if (buf == NULL) scr_ca_oom();
119
+ size_t got = fread(buf, 1, (size_t)sz, f);
120
+ fclose(f);
121
+ buf[got] = '\0';
122
+ *out_len = got;
123
+ return buf;
124
+ }
125
+
126
+ /* The host bundle, probed in scr_tls.c's documented order. */
127
+ static ScrArr *scr_ca_load_host_bundle(void) {
128
+ ScrArr *arr = scr_arr_new(SCR_ELEM_STR, 128);
129
+ static const char *const bundles[] = {
130
+ "/etc/ssl/cert.pem", /* macOS, Alpine */
131
+ "/etc/ssl/certs/ca-certificates.crt", /* Debian/Ubuntu */
132
+ "/etc/pki/tls/certs/ca-bundle.crt", /* Fedora/RHEL */
133
+ "/etc/ssl/ca-bundle.pem", /* openSUSE */
134
+ };
135
+ for (size_t i = 0; i < sizeof bundles / sizeof bundles[0]; i++) {
136
+ size_t len = 0;
137
+ char *text = scr_ca_read_file(bundles[i], &len);
138
+ if (text == NULL) continue;
139
+ scr_ca_push_pem_blocks(arr, text, len);
140
+ free(text);
141
+ break; /* first bundle wins, like the anchor probe */
142
+ }
143
+ return arr;
144
+ }
145
+
146
+ static ScrArr *scr_ca_bundled_arr(void) {
147
+ if (scr_ca_bundled == NULL) {
148
+ scr_ca_arm_teardown();
149
+ scr_ca_bundled = scr_ca_load_host_bundle();
150
+ }
151
+ return scr_ca_bundled;
152
+ }
153
+
154
+ static ScrArr *scr_ca_system_arr(void) {
155
+ if (scr_ca_system == NULL) {
156
+ scr_ca_arm_teardown();
157
+ scr_ca_system = scr_ca_load_host_bundle();
158
+ }
159
+ return scr_ca_system;
160
+ }
161
+
162
+ static ScrArr *scr_ca_extra_arr(void) {
163
+ if (scr_ca_extra == NULL) {
164
+ scr_ca_arm_teardown();
165
+ scr_ca_extra = scr_arr_new(SCR_ELEM_STR, 4);
166
+ const char *path = getenv("NODE_EXTRA_CA_CERTS");
167
+ if (path != NULL && path[0] != '\0') {
168
+ size_t len = 0;
169
+ char *text = scr_ca_read_file(path, &len);
170
+ if (text != NULL) {
171
+ scr_ca_push_pem_blocks(scr_ca_extra, text, len);
172
+ free(text);
173
+ }
174
+ }
175
+ }
176
+ return scr_ca_extra;
177
+ }
178
+
179
+ /* 'default' with no override: bundled ∪ extra (Node's own composition
180
+ * when no --use-*-ca flag redirects it). */
181
+ static ScrArr *scr_ca_default_arr(void) {
182
+ if (scr_ca_default == NULL) {
183
+ scr_ca_arm_teardown();
184
+ ScrArr *bundled = scr_ca_bundled_arr();
185
+ ScrArr *extra = scr_ca_extra_arr();
186
+ size_t nb = (size_t)scr_arr_len(bundled);
187
+ size_t ne = (size_t)scr_arr_len(extra);
188
+ scr_ca_default = scr_arr_new(SCR_ELEM_STR, nb + ne > 0 ? nb + ne : 1);
189
+ for (size_t i = 0; i < nb; i++) scr_arr_push_ref(scr_ca_default, scr_arr_get_ref(bundled, (double)i));
190
+ for (size_t i = 0; i < ne; i++) scr_arr_push_ref(scr_ca_default, scr_arr_get_ref(extra, (double)i));
191
+ }
192
+ return scr_ca_default;
193
+ }
194
+
195
+ ScrArr *scr_tls_ca_get(ScrStr *type) {
196
+ ScrArr *arr = NULL;
197
+ if (strcmp(type->data, "default") == 0) arr = scr_ca_default_arr();
198
+ else if (strcmp(type->data, "bundled") == 0) arr = scr_ca_bundled_arr();
199
+ else if (strcmp(type->data, "system") == 0) arr = scr_ca_system_arr();
200
+ else if (strcmp(type->data, "extra") == 0) arr = scr_ca_extra_arr();
201
+ if (arr == NULL) {
202
+ /* Node's ERR_INVALID_ARG_VALUE TypeError, message-exact. */
203
+ char msg[256];
204
+ int n = snprintf(msg, sizeof msg, "The argument 'type' is invalid. Received '%s'", type->data);
205
+ scr_throw_error_msg_code(SCR_ERR_TYPE, msg, n < 0 ? 0 : (size_t)n, "ERR_INVALID_ARG_VALUE");
206
+ return NULL;
207
+ }
208
+ return scr_arr_retain(arr);
209
+ }
210
+
211
+ ScrArr *scr_tls_ca_root(void) { return scr_arr_retain(scr_ca_bundled_arr()); }
212
+
213
+ void scr_tls_ca_set_default(ScrArr *certs) {
214
+ size_t n = (size_t)scr_arr_len(certs);
215
+ /* Extract + dedupe first — a throw must leave the current set intact
216
+ * (Node's own error contract, pinned by the suite's error test). */
217
+ ScrArr *next = scr_arr_new(SCR_ELEM_STR, n > 0 ? n : 1);
218
+ size_t blocks = 0;
219
+ for (size_t i = 0; i < n; i++) {
220
+ ScrStr *entry = scr_arr_get_ref(certs, (double)i); /* +1 */
221
+ size_t before = (size_t)scr_arr_len(next);
222
+ blocks += scr_ca_push_pem_blocks(next, entry->data, entry->len);
223
+ /* Dedupe the freshly pushed blocks against everything before them. */
224
+ size_t after = (size_t)scr_arr_len(next);
225
+ for (size_t j = after; j > before; j--) {
226
+ ScrStr *cand = scr_arr_get_ref(next, (double)(j - 1)); /* +1 */
227
+ bool dup = false;
228
+ for (size_t k = 0; k + 1 < j && !dup; k++) {
229
+ ScrStr *prev = scr_arr_get_ref(next, (double)k); /* +1 */
230
+ dup = prev->len == cand->len && memcmp(prev->data, cand->data, cand->len) == 0;
231
+ scr_str_release(prev);
232
+ }
233
+ scr_str_release(cand);
234
+ if (dup) {
235
+ /* Drop the duplicate: rebuild without index j-1 (rare path —
236
+ * a simple splice via slice would copy anyway; do it in place). */
237
+ ScrArr *trimmed = scr_arr_new(SCR_ELEM_STR, after);
238
+ for (size_t k = 0; k < (size_t)scr_arr_len(next); k++) {
239
+ if (k == j - 1) continue;
240
+ scr_arr_push_ref(trimmed, scr_arr_get_ref(next, (double)k));
241
+ }
242
+ scr_arr_release(next);
243
+ next = trimmed;
244
+ }
245
+ }
246
+ scr_str_release(entry);
247
+ }
248
+ if (n > 0 && blocks == 0) {
249
+ scr_arr_release(next);
250
+ static const char MSG[] = "No valid certificates found in the provided array";
251
+ scr_throw_error_msg_code(SCR_ERR_ERROR, MSG, sizeof MSG - 1, "ERR_CRYPTO_OPERATION_FAILED");
252
+ return;
253
+ }
254
+ /* Commit: the cached 'default' array and the concatenated anchor
255
+ * buffer scr_tls.c parses (NUL-terminated for mbedTLS's PEM mode). */
256
+ scr_ca_arm_teardown();
257
+ if (scr_ca_default != NULL) scr_arr_release(scr_ca_default);
258
+ scr_ca_default = next;
259
+ size_t total = 0;
260
+ size_t count = (size_t)scr_arr_len(next);
261
+ for (size_t i = 0; i < count; i++) {
262
+ ScrStr *s = scr_arr_get_ref(next, (double)i);
263
+ total += s->len;
264
+ scr_str_release(s);
265
+ }
266
+ char *buf = malloc(total + 1);
267
+ if (buf == NULL) scr_ca_oom();
268
+ size_t off = 0;
269
+ for (size_t i = 0; i < count; i++) {
270
+ ScrStr *s = scr_arr_get_ref(next, (double)i);
271
+ memcpy(buf + off, s->data, s->len);
272
+ off += s->len;
273
+ scr_str_release(s);
274
+ }
275
+ buf[off] = '\0';
276
+ free(scr_ca_override_buf);
277
+ scr_ca_override_buf = buf;
278
+ scr_ca_override_len = off;
279
+ scr_ca_override_gen++;
280
+ }
281
+
282
+ bool scr_tls_ca_default_override(const char **pem, size_t *len, uint64_t *gen) {
283
+ if (scr_ca_override_gen == 0) return false;
284
+ *pem = scr_ca_override_buf;
285
+ *len = scr_ca_override_len;
286
+ *gen = scr_ca_override_gen;
287
+ return true;
288
+ }
package/src/scr_web.c CHANGED
@@ -791,7 +791,7 @@ static const char web_prelude[] =
791
791
  " [Symbol.iterator]() { return this.entries(); }\n"
792
792
  " }\n"
793
793
  "\n"
794
- /* Event + EventTarget + CustomEvent — the DOM event plumbing Node
794
+ /* Event + EventTarget + CustomEvent — the dyn event plumbing Node
795
795
  * exposes as globals since v15. Synchronous dispatch on one target
796
796
  * (no tree, no phases — composedPath answers []), once/capture-shaped
797
797
  * options accepted, handleEvent objects honored, dispatchEvent