@blamejs/core 0.4.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.
Files changed (160) hide show
  1. package/CHANGELOG.md +230 -0
  2. package/LICENSE +201 -0
  3. package/LTS-CALENDAR.md +29 -0
  4. package/MIGRATING.md +7 -0
  5. package/NOTICE +59 -0
  6. package/README.md +100 -0
  7. package/bin/blamejs.js +13 -0
  8. package/index.js +253 -0
  9. package/lib/api-key.js +705 -0
  10. package/lib/api-snapshot.js +335 -0
  11. package/lib/app-shutdown.js +381 -0
  12. package/lib/app.js +364 -0
  13. package/lib/atomic-file.js +525 -0
  14. package/lib/audit-chain.js +168 -0
  15. package/lib/audit-sign.js +319 -0
  16. package/lib/audit-tools.js +682 -0
  17. package/lib/audit.js +753 -0
  18. package/lib/auth/jwt.js +280 -0
  19. package/lib/auth/oauth.js +691 -0
  20. package/lib/auth/passkey.js +185 -0
  21. package/lib/auth/password.js +139 -0
  22. package/lib/auth/totp.js +17 -0
  23. package/lib/auth-header.js +81 -0
  24. package/lib/backup/bundle.js +219 -0
  25. package/lib/backup/crypto.js +174 -0
  26. package/lib/backup/index.js +490 -0
  27. package/lib/backup/manifest.js +275 -0
  28. package/lib/bundler.js +295 -0
  29. package/lib/cache.js +819 -0
  30. package/lib/chain-writer.js +234 -0
  31. package/lib/cli-helpers.js +201 -0
  32. package/lib/cli.js +1377 -0
  33. package/lib/cluster-provider-db.js +245 -0
  34. package/lib/cluster-storage.js +166 -0
  35. package/lib/cluster.js +691 -0
  36. package/lib/consent.js +222 -0
  37. package/lib/constants.js +186 -0
  38. package/lib/cookies.js +293 -0
  39. package/lib/credential-hash.js +303 -0
  40. package/lib/crypto-field.js +159 -0
  41. package/lib/crypto.js +250 -0
  42. package/lib/db-query.js +297 -0
  43. package/lib/db-schema.js +250 -0
  44. package/lib/db.js +1054 -0
  45. package/lib/deprecate.js +226 -0
  46. package/lib/dev.js +324 -0
  47. package/lib/error-page.js +424 -0
  48. package/lib/events.js +135 -0
  49. package/lib/external-db.js +422 -0
  50. package/lib/forms.js +378 -0
  51. package/lib/framework-error.js +189 -0
  52. package/lib/framework-schema.js +604 -0
  53. package/lib/handlers.js +350 -0
  54. package/lib/html-balance.js +227 -0
  55. package/lib/http-client.js +615 -0
  56. package/lib/i18n.js +780 -0
  57. package/lib/jobs.js +181 -0
  58. package/lib/lazy-require.js +48 -0
  59. package/lib/log-stream-local.js +137 -0
  60. package/lib/log-stream-webhook.js +170 -0
  61. package/lib/log-stream.js +211 -0
  62. package/lib/log.js +355 -0
  63. package/lib/mail-bounce.js +507 -0
  64. package/lib/mail.js +701 -0
  65. package/lib/metrics.js +647 -0
  66. package/lib/middleware/api-encrypt.js +553 -0
  67. package/lib/middleware/attach-user.js +156 -0
  68. package/lib/middleware/body-parser.js +883 -0
  69. package/lib/middleware/bot-guard.js +148 -0
  70. package/lib/middleware/compression.js +436 -0
  71. package/lib/middleware/cors.js +236 -0
  72. package/lib/middleware/csp-nonce.js +332 -0
  73. package/lib/middleware/csrf-protect.js +275 -0
  74. package/lib/middleware/error-handler.js +46 -0
  75. package/lib/middleware/health.js +358 -0
  76. package/lib/middleware/index.js +52 -0
  77. package/lib/middleware/rate-limit.js +319 -0
  78. package/lib/middleware/request-id.js +53 -0
  79. package/lib/middleware/require-auth.js +95 -0
  80. package/lib/middleware/security-headers.js +91 -0
  81. package/lib/migrations.js +353 -0
  82. package/lib/mtls-ca.js +333 -0
  83. package/lib/mtls-engine-default.js +285 -0
  84. package/lib/nonce-store.js +177 -0
  85. package/lib/notify.js +643 -0
  86. package/lib/ntp-check.js +178 -0
  87. package/lib/object-store/azure-blob.js +467 -0
  88. package/lib/object-store/gcs.js +469 -0
  89. package/lib/object-store/http-put.js +153 -0
  90. package/lib/object-store/index.js +140 -0
  91. package/lib/object-store/local.js +163 -0
  92. package/lib/object-store/retry.js +15 -0
  93. package/lib/object-store/sigv4.js +535 -0
  94. package/lib/observability.js +114 -0
  95. package/lib/pagination.js +371 -0
  96. package/lib/parsers/index.js +64 -0
  97. package/lib/parsers/safe-csv.js +224 -0
  98. package/lib/parsers/safe-env.js +614 -0
  99. package/lib/parsers/safe-toml.js +745 -0
  100. package/lib/parsers/safe-xml.js +379 -0
  101. package/lib/parsers/safe-yaml.js +977 -0
  102. package/lib/permissions.js +430 -0
  103. package/lib/pqc-agent.js +85 -0
  104. package/lib/pqc-gate.js +266 -0
  105. package/lib/protocol-dispatcher.js +144 -0
  106. package/lib/queue-local.js +327 -0
  107. package/lib/queue.js +430 -0
  108. package/lib/redact.js +192 -0
  109. package/lib/render.js +193 -0
  110. package/lib/request-helpers.js +178 -0
  111. package/lib/restore-bundle.js +239 -0
  112. package/lib/restore-rollback.js +254 -0
  113. package/lib/restore.js +301 -0
  114. package/lib/retry.js +329 -0
  115. package/lib/router.js +437 -0
  116. package/lib/safe-async.js +520 -0
  117. package/lib/safe-buffer.js +162 -0
  118. package/lib/safe-json.js +532 -0
  119. package/lib/safe-schema.js +1176 -0
  120. package/lib/safe-sql.js +157 -0
  121. package/lib/safe-url.js +109 -0
  122. package/lib/scheduler.js +680 -0
  123. package/lib/seeders.js +622 -0
  124. package/lib/session.js +304 -0
  125. package/lib/slug.js +243 -0
  126. package/lib/static.js +268 -0
  127. package/lib/storage.js +470 -0
  128. package/lib/subject.js +281 -0
  129. package/lib/template.js +781 -0
  130. package/lib/testing.js +621 -0
  131. package/lib/totp.js +285 -0
  132. package/lib/tracing.js +484 -0
  133. package/lib/validate-opts.js +56 -0
  134. package/lib/vault/index.js +299 -0
  135. package/lib/vault/passphrase-ops.js +311 -0
  136. package/lib/vault/passphrase-source.js +198 -0
  137. package/lib/vault/rotate.js +761 -0
  138. package/lib/vault/wrap.js +289 -0
  139. package/lib/vendor/MANIFEST.json +84 -0
  140. package/lib/vendor/argon2/argon2.cjs +466 -0
  141. package/lib/vendor/argon2/argon2.d.cts +62 -0
  142. package/lib/vendor/argon2/package.json +1 -0
  143. package/lib/vendor/argon2/prebuilds/darwin-arm64/argon2.armv8.glibc.node +0 -0
  144. package/lib/vendor/argon2/prebuilds/darwin-x64/argon2.glibc.node +0 -0
  145. package/lib/vendor/argon2/prebuilds/freebsd-arm64/argon2.armv8.glibc.node +0 -0
  146. package/lib/vendor/argon2/prebuilds/freebsd-x64/argon2.glibc.node +0 -0
  147. package/lib/vendor/argon2/prebuilds/linux-arm/argon2.armv7.glibc.node +0 -0
  148. package/lib/vendor/argon2/prebuilds/linux-arm/argon2.armv7.musl.node +0 -0
  149. package/lib/vendor/argon2/prebuilds/linux-arm64/argon2.armv8.glibc.node +0 -0
  150. package/lib/vendor/argon2/prebuilds/linux-arm64/argon2.armv8.musl.node +0 -0
  151. package/lib/vendor/argon2/prebuilds/linux-x64/argon2.glibc.node +0 -0
  152. package/lib/vendor/argon2/prebuilds/linux-x64/argon2.musl.node +0 -0
  153. package/lib/vendor/argon2/prebuilds/win32-x64/argon2.glibc.node +0 -0
  154. package/lib/vendor/noble-ciphers.cjs +9 -0
  155. package/lib/vendor/pki.cjs +181 -0
  156. package/lib/vendor/simplewebauthn-server.cjs +328 -0
  157. package/lib/webhook.js +632 -0
  158. package/lib/websocket-channels.js +413 -0
  159. package/lib/websocket.js +833 -0
  160. package/package.json +39 -0
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ /**
3
+ * SQL safety primitive — identifier validation and parameterized query
4
+ * helpers.
5
+ *
6
+ * The framework's own SQL is parameterized everywhere — values bind
7
+ * through node:sqlite or external-db drivers, never string-concatenated.
8
+ * That covers value injection. The remaining attack surface is
9
+ * IDENTIFIER injection: when a TABLE name or COLUMN name is interpolated
10
+ * into a SQL string, a malicious or misconfigured caller can break out
11
+ * of the intended structure. This is rare in framework-internal code
12
+ * (we control the names) but easy to get wrong if operator-supplied
13
+ * config flows in unchecked.
14
+ *
15
+ * This module provides the thin validator that any code interpolating
16
+ * identifiers should call first. It does NOT execute SQL; it just
17
+ * validates the inputs that get composed into SQL elsewhere.
18
+ *
19
+ * Public API:
20
+ * safeSql.validateIdentifier(name, opts?) throws on bad shape
21
+ * safeSql.quoteIdentifier(name, dialect?) returns "name" / `name`
22
+ * safeSql.assertOneOf(name, allowlist) throws unless in list
23
+ * safeSql.SafeSqlError error class
24
+ *
25
+ * Identifier rule (default): `^[A-Za-z_][A-Za-z0-9_]*$` with length
26
+ * 1–63 chars (Postgres NAMEDATALEN default; SQLite has no hard
27
+ * limit but matching the strictest dialect is the safe bound).
28
+ *
29
+ * Forbidden in identifiers regardless of regex:
30
+ * - SQL reserved words (a small allowlist, not exhaustive — operators
31
+ * who need reserved-word identifiers should quote them and accept
32
+ * the dialect-specific quoting)
33
+ * - Leading underscore prefixed `sqlite_` (SQLite-internal)
34
+ * - Embedded null byte
35
+ *
36
+ * Quoting:
37
+ * sqlite "name" (double-quote per SQL standard; SQLite
38
+ * accepts double quotes for identifiers
39
+ * per its quirks settings)
40
+ * postgres "name"
41
+ * mysql `name` (MySQL's backtick convention)
42
+ *
43
+ * Allowlist usage (recommended):
44
+ * var ALLOWED_TABLES = new Set(["audit_log", "consent_log", …]);
45
+ * safeSql.assertOneOf(operatorTableName, ALLOWED_TABLES);
46
+ * var sql = "INSERT INTO " + safeSql.quoteIdentifier(operatorTableName) + " …";
47
+ *
48
+ * The allowlist is the strongest guarantee. Operators with dynamic
49
+ * identifier needs (rare) use validateIdentifier alone, accepting
50
+ * that any string passing the regex is allowed.
51
+ */
52
+
53
+ // Reserved-word block list — the most dangerous to accept as a bare
54
+ // identifier. Not exhaustive; the allowlist pattern below is preferred.
55
+ var BANNED_IDENTIFIERS = new Set([
56
+ "select", "insert", "update", "delete", "drop", "create", "alter",
57
+ "truncate", "grant", "revoke", "union", "exec", "execute",
58
+ "where", "from", "join", "into", "values", "table", "database",
59
+ "schema", "index", "view", "trigger", "procedure", "function",
60
+ "begin", "commit", "rollback", "savepoint",
61
+ ]);
62
+
63
+ // Default identifier shape — Postgres NAMEDATALEN (63 chars) is the
64
+ // strictest of the dialects we support, so use it as the bound.
65
+ var DEFAULT_IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
66
+ var MAX_IDENTIFIER_LENGTH = 63;
67
+
68
+ var { FrameworkError } = require("./framework-error");
69
+
70
+ class SafeSqlError extends FrameworkError {
71
+ constructor(message, code) {
72
+ super(message);
73
+ this.name = "SafeSqlError";
74
+ this.code = code || "sql/invalid";
75
+ this.isSafeSqlError = true;
76
+ }
77
+ }
78
+
79
+ function validateIdentifier(name, opts) {
80
+ opts = opts || {};
81
+ if (typeof name !== "string") {
82
+ throw new SafeSqlError("identifier must be a string (got " + typeof name + ")",
83
+ "sql/bad-type");
84
+ }
85
+ if (name.length === 0) {
86
+ throw new SafeSqlError("identifier must not be empty", "sql/empty");
87
+ }
88
+ if (name.length > MAX_IDENTIFIER_LENGTH) {
89
+ throw new SafeSqlError(
90
+ "identifier '" + name + "' exceeds maxLength " + MAX_IDENTIFIER_LENGTH,
91
+ "sql/too-long"
92
+ );
93
+ }
94
+ if (name.indexOf("\0") >= 0) {
95
+ throw new SafeSqlError("identifier contains null byte", "sql/null-byte");
96
+ }
97
+ var pattern = opts.pattern || DEFAULT_IDENTIFIER_RE;
98
+ if (!pattern.test(name)) {
99
+ throw new SafeSqlError(
100
+ "identifier '" + name + "' does not match required pattern " + pattern,
101
+ "sql/bad-shape"
102
+ );
103
+ }
104
+ if (!opts.allowReserved && BANNED_IDENTIFIERS.has(name.toLowerCase())) {
105
+ throw new SafeSqlError(
106
+ "identifier '" + name + "' is a SQL reserved word",
107
+ "sql/reserved-word"
108
+ );
109
+ }
110
+ if (!opts.allowSqliteInternal && /^sqlite_/i.test(name)) {
111
+ throw new SafeSqlError(
112
+ "identifier '" + name + "' uses the SQLite-internal 'sqlite_' prefix",
113
+ "sql/internal-prefix"
114
+ );
115
+ }
116
+ return name;
117
+ }
118
+
119
+ function quoteIdentifier(name, dialect) {
120
+ validateIdentifier(name);
121
+ dialect = (dialect || "sqlite").toLowerCase();
122
+ if (dialect === "mysql") return "`" + name + "`";
123
+ // sqlite + postgres both use double-quote per SQL standard
124
+ return '"' + name + '"';
125
+ }
126
+
127
+ function assertOneOf(name, allowlist) {
128
+ if (typeof name !== "string") {
129
+ throw new SafeSqlError("name must be a string", "sql/bad-type");
130
+ }
131
+ // Accept either Set or array
132
+ var ok = false;
133
+ if (allowlist && typeof allowlist.has === "function") {
134
+ ok = allowlist.has(name);
135
+ } else if (Array.isArray(allowlist)) {
136
+ ok = allowlist.indexOf(name) !== -1;
137
+ } else {
138
+ throw new SafeSqlError("allowlist must be a Set or array", "sql/bad-allowlist");
139
+ }
140
+ if (!ok) {
141
+ throw new SafeSqlError(
142
+ "identifier '" + name + "' not in allowlist",
143
+ "sql/not-allowed"
144
+ );
145
+ }
146
+ return name;
147
+ }
148
+
149
+ module.exports = {
150
+ validateIdentifier: validateIdentifier,
151
+ quoteIdentifier: quoteIdentifier,
152
+ assertOneOf: assertOneOf,
153
+ SafeSqlError: SafeSqlError,
154
+ // Exposed so consumers can compose their own validators
155
+ DEFAULT_IDENTIFIER_RE: DEFAULT_IDENTIFIER_RE,
156
+ MAX_IDENTIFIER_LENGTH: MAX_IDENTIFIER_LENGTH,
157
+ };
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ /**
3
+ * URL-safe — validate URL scheme + shape against an allowlist.
4
+ *
5
+ * Per the framework's modernity stance: outbound network calls
6
+ * REQUIRE TLS by default. Operators with internal cleartext
7
+ * endpoints (development, behind-VPN services, internal mesh) opt in
8
+ * explicitly via opts.allowedProtocols. The framework refuses to
9
+ * silently drop bytes on the wire as cleartext.
10
+ *
11
+ * Public API:
12
+ *
13
+ * safeUrl.parse(url, opts?) → URL
14
+ * Returns a parsed URL object. Throws if the URL is malformed
15
+ * or its protocol is not in the allowlist.
16
+ *
17
+ * opts:
18
+ * allowedProtocols — array of accepted protocol strings
19
+ * (e.g. ["https:"] or safeUrl.ALLOW_HTTP_TLS).
20
+ * Default: ALLOW_HTTP_TLS.
21
+ * errorClass — FrameworkError subclass for the thrown
22
+ * error. Lets callers (object-store,
23
+ * log-stream, http-client) surface their
24
+ * own decorated error class. Default:
25
+ * SafeUrlError.
26
+ *
27
+ * Constants — pre-baked allowlists for the common caller cases:
28
+ *
29
+ * ALLOW_HTTP_TLS ["https:"] (the secure HTTP default)
30
+ * ALLOW_HTTP_ALL ["http:", "https:"] (HTTP + cleartext opt-in)
31
+ * ALLOW_WS_TLS ["wss:"] (the secure WS default)
32
+ * ALLOW_WS_ALL ["ws:", "wss:"] (WS + cleartext opt-in)
33
+ * ALLOW_ANY ["http:", "https:", "ws:", "wss:"]
34
+ *
35
+ * Why per-call constants instead of one global "secure" list:
36
+ * The http-client only speaks HTTP, so wss:// is a category error
37
+ * (operator passed a WebSocket URL to a non-WebSocket client). Each
38
+ * caller declares its own narrow allowlist; an off-protocol URL
39
+ * fails with a clear "protocol not allowed here" error rather than
40
+ * trying and failing weirdly later.
41
+ */
42
+
43
+ var { FrameworkError } = require("./framework-error");
44
+ var { URL } = require("url");
45
+
46
+ var ALLOW_HTTP_TLS = Object.freeze(["https:"]);
47
+ var ALLOW_HTTP_ALL = Object.freeze(["http:", "https:"]);
48
+ var ALLOW_WS_TLS = Object.freeze(["wss:"]);
49
+ var ALLOW_WS_ALL = Object.freeze(["ws:", "wss:"]);
50
+ var ALLOW_ANY = Object.freeze(["http:", "https:", "ws:", "wss:"]);
51
+
52
+ class SafeUrlError extends FrameworkError {
53
+ constructor(code, message) {
54
+ super(message, code);
55
+ this.name = "SafeUrlError";
56
+ this.isSafeUrlError = true;
57
+ }
58
+ }
59
+
60
+ function _makeError(errorClass, code, message) {
61
+ if (!errorClass || errorClass === SafeUrlError) {
62
+ return new SafeUrlError(code, message);
63
+ }
64
+ // Convention for operational classes (ObjectStoreError,
65
+ // LogStreamError, etc.): (code, message, permanent[, statusCode]).
66
+ // A protocol-violation URL is "permanent" — retry won't help.
67
+ return new errorClass(code, message, true);
68
+ }
69
+
70
+ function parse(url, opts) {
71
+ opts = opts || {};
72
+ var allowed = Array.isArray(opts.allowedProtocols) && opts.allowedProtocols.length > 0
73
+ ? opts.allowedProtocols
74
+ : ALLOW_HTTP_TLS;
75
+ var errClass = opts.errorClass;
76
+
77
+ if (url == null || url === "") {
78
+ throw _makeError(errClass, "safe-url/missing", "url is required");
79
+ }
80
+
81
+ var parsed;
82
+ if (url instanceof URL) {
83
+ parsed = url;
84
+ } else {
85
+ try {
86
+ parsed = new URL(String(url));
87
+ } catch (e) {
88
+ throw _makeError(errClass, "safe-url/malformed", "malformed URL: " + e.message);
89
+ }
90
+ }
91
+
92
+ if (allowed.indexOf(parsed.protocol) === -1) {
93
+ throw _makeError(errClass, "safe-url/protocol-disallowed",
94
+ "protocol '" + parsed.protocol + "' not in allowlist [" + allowed.join(", ") +
95
+ "]. Pass opts.allowedProtocols to override (e.g. safeUrl.ALLOW_HTTP_ALL for cleartext endpoints).");
96
+ }
97
+
98
+ return parsed;
99
+ }
100
+
101
+ module.exports = {
102
+ parse: parse,
103
+ SafeUrlError: SafeUrlError,
104
+ ALLOW_HTTP_TLS: ALLOW_HTTP_TLS,
105
+ ALLOW_HTTP_ALL: ALLOW_HTTP_ALL,
106
+ ALLOW_WS_TLS: ALLOW_WS_TLS,
107
+ ALLOW_WS_ALL: ALLOW_WS_ALL,
108
+ ALLOW_ANY: ALLOW_ANY,
109
+ };