@blamejs/core 0.8.43 → 0.8.50
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/CHANGELOG.md +93 -0
- package/README.md +10 -10
- package/index.js +52 -0
- package/lib/a2a.js +159 -34
- package/lib/acme.js +762 -0
- package/lib/ai-pref.js +166 -43
- package/lib/api-key.js +108 -47
- package/lib/api-snapshot.js +157 -40
- package/lib/app-shutdown.js +113 -77
- package/lib/archive.js +337 -40
- package/lib/arg-parser.js +697 -0
- package/lib/asyncapi.js +99 -55
- package/lib/atomic-file.js +465 -104
- package/lib/audit-chain.js +123 -34
- package/lib/audit-daily-review.js +389 -0
- package/lib/audit-sign.js +302 -56
- package/lib/audit-tools.js +412 -63
- package/lib/audit.js +656 -35
- package/lib/auth/jwt-external.js +17 -0
- package/lib/auth/oauth.js +7 -0
- package/lib/auth-bot-challenge.js +505 -0
- package/lib/auth-header.js +92 -25
- package/lib/backup/bundle.js +26 -0
- package/lib/backup/index.js +512 -89
- package/lib/backup/manifest.js +168 -7
- package/lib/break-glass.js +415 -39
- package/lib/budr.js +103 -30
- package/lib/bundler.js +86 -66
- package/lib/cache.js +192 -72
- package/lib/chain-writer.js +65 -40
- package/lib/circuit-breaker.js +56 -33
- package/lib/cli-helpers.js +106 -75
- package/lib/cli.js +6 -30
- package/lib/cloud-events.js +99 -32
- package/lib/cluster-storage.js +162 -37
- package/lib/cluster.js +340 -49
- package/lib/codepoint-class.js +66 -0
- package/lib/compliance.js +424 -24
- package/lib/config-drift.js +111 -46
- package/lib/config.js +94 -40
- package/lib/consent.js +165 -18
- package/lib/constants.js +1 -0
- package/lib/content-credentials.js +153 -48
- package/lib/cookies.js +154 -62
- package/lib/credential-hash.js +133 -61
- package/lib/crypto-field.js +702 -18
- package/lib/crypto-hpke.js +256 -0
- package/lib/crypto.js +744 -22
- package/lib/csv.js +178 -35
- package/lib/daemon.js +456 -0
- package/lib/dark-patterns.js +186 -55
- package/lib/db-query.js +79 -2
- package/lib/db.js +1431 -60
- package/lib/ddl-change-control.js +523 -0
- package/lib/deprecate.js +195 -40
- package/lib/dev.js +82 -39
- package/lib/dora.js +67 -48
- package/lib/dr-runbook.js +368 -0
- package/lib/dsr.js +142 -11
- package/lib/dual-control.js +91 -56
- package/lib/events.js +120 -41
- package/lib/external-db-migrate.js +192 -2
- package/lib/external-db.js +795 -50
- package/lib/fapi2.js +122 -1
- package/lib/fda-21cfr11.js +395 -0
- package/lib/fdx.js +132 -2
- package/lib/file-type.js +87 -0
- package/lib/file-upload.js +93 -0
- package/lib/flag.js +82 -20
- package/lib/forms.js +132 -29
- package/lib/framework-error.js +169 -0
- package/lib/framework-schema.js +163 -35
- package/lib/gate-contract.js +849 -175
- package/lib/graphql-federation.js +68 -7
- package/lib/guard-all.js +172 -55
- package/lib/guard-archive.js +286 -124
- package/lib/guard-auth.js +194 -21
- package/lib/guard-cidr.js +190 -28
- package/lib/guard-csv.js +397 -51
- package/lib/guard-domain.js +213 -91
- package/lib/guard-email.js +236 -29
- package/lib/guard-filename.js +307 -75
- package/lib/guard-graphql.js +263 -30
- package/lib/guard-html.js +310 -116
- package/lib/guard-image.js +243 -30
- package/lib/guard-json.js +260 -54
- package/lib/guard-jsonpath.js +235 -23
- package/lib/guard-jwt.js +284 -30
- package/lib/guard-markdown.js +204 -22
- package/lib/guard-mime.js +190 -26
- package/lib/guard-oauth.js +277 -28
- package/lib/guard-pdf.js +251 -27
- package/lib/guard-regex.js +226 -18
- package/lib/guard-shell.js +229 -26
- package/lib/guard-svg.js +177 -10
- package/lib/guard-template.js +232 -21
- package/lib/guard-time.js +195 -29
- package/lib/guard-uuid.js +189 -30
- package/lib/guard-xml.js +259 -36
- package/lib/guard-yaml.js +241 -44
- package/lib/honeytoken.js +63 -27
- package/lib/html-balance.js +83 -0
- package/lib/http-client.js +486 -59
- package/lib/http-message-signature.js +582 -0
- package/lib/i18n.js +102 -49
- package/lib/iab-mspa.js +112 -32
- package/lib/iab-tcf.js +107 -2
- package/lib/inbox.js +90 -52
- package/lib/keychain.js +865 -0
- package/lib/legal-hold.js +374 -0
- package/lib/local-db-thin.js +320 -0
- package/lib/log-stream.js +281 -51
- package/lib/log.js +184 -86
- package/lib/mail-bounce.js +107 -62
- package/lib/mail.js +295 -58
- package/lib/mcp.js +108 -27
- package/lib/metrics.js +98 -89
- package/lib/middleware/age-gate.js +36 -0
- package/lib/middleware/ai-act-disclosure.js +37 -0
- package/lib/middleware/api-encrypt.js +45 -0
- package/lib/middleware/assetlinks.js +40 -0
- package/lib/middleware/asyncapi-serve.js +35 -0
- package/lib/middleware/attach-user.js +40 -0
- package/lib/middleware/bearer-auth.js +40 -0
- package/lib/middleware/body-parser.js +230 -0
- package/lib/middleware/bot-disclose.js +34 -0
- package/lib/middleware/bot-guard.js +39 -0
- package/lib/middleware/compression.js +37 -0
- package/lib/middleware/cookies.js +32 -0
- package/lib/middleware/cors.js +40 -0
- package/lib/middleware/csp-nonce.js +40 -0
- package/lib/middleware/csp-report.js +34 -0
- package/lib/middleware/csrf-protect.js +43 -0
- package/lib/middleware/daily-byte-quota.js +53 -85
- package/lib/middleware/db-role-for.js +40 -0
- package/lib/middleware/dpop.js +40 -0
- package/lib/middleware/error-handler.js +37 -14
- package/lib/middleware/fetch-metadata.js +39 -0
- package/lib/middleware/flag-context.js +34 -0
- package/lib/middleware/gpc.js +33 -0
- package/lib/middleware/headers.js +35 -0
- package/lib/middleware/health.js +46 -0
- package/lib/middleware/host-allowlist.js +30 -0
- package/lib/middleware/network-allowlist.js +38 -0
- package/lib/middleware/openapi-serve.js +34 -0
- package/lib/middleware/rate-limit.js +160 -18
- package/lib/middleware/request-id.js +36 -18
- package/lib/middleware/request-log.js +37 -0
- package/lib/middleware/require-aal.js +29 -0
- package/lib/middleware/require-auth.js +32 -0
- package/lib/middleware/require-bound-key.js +41 -0
- package/lib/middleware/require-content-type.js +32 -0
- package/lib/middleware/require-methods.js +27 -0
- package/lib/middleware/require-mtls.js +33 -0
- package/lib/middleware/require-step-up.js +37 -0
- package/lib/middleware/security-headers.js +44 -0
- package/lib/middleware/security-txt.js +38 -0
- package/lib/middleware/span-http-server.js +37 -0
- package/lib/middleware/sse.js +36 -0
- package/lib/middleware/trace-log-correlation.js +33 -0
- package/lib/middleware/trace-propagate.js +32 -0
- package/lib/middleware/tus-upload.js +90 -0
- package/lib/middleware/web-app-manifest.js +53 -0
- package/lib/mtls-ca.js +100 -70
- package/lib/network-byte-quota.js +308 -0
- package/lib/network-heartbeat.js +135 -0
- package/lib/network-tls.js +534 -4
- package/lib/network.js +103 -0
- package/lib/notify.js +114 -43
- package/lib/ntp-check.js +192 -51
- package/lib/observability.js +145 -47
- package/lib/openapi.js +90 -44
- package/lib/outbox.js +99 -1
- package/lib/pagination.js +168 -86
- package/lib/parsers/index.js +16 -5
- package/lib/permissions.js +93 -40
- package/lib/pqc-agent.js +84 -8
- package/lib/pqc-software.js +94 -60
- package/lib/process-spawn.js +95 -21
- package/lib/pubsub.js +96 -66
- package/lib/queue.js +375 -54
- package/lib/redact.js +793 -21
- package/lib/render.js +139 -47
- package/lib/request-helpers.js +485 -121
- package/lib/restore-bundle.js +142 -39
- package/lib/restore-rollback.js +136 -45
- package/lib/retention.js +178 -50
- package/lib/retry.js +116 -33
- package/lib/router.js +475 -23
- package/lib/safe-async.js +543 -94
- package/lib/safe-buffer.js +337 -41
- package/lib/safe-json.js +467 -62
- package/lib/safe-jsonpath.js +285 -0
- package/lib/safe-schema.js +631 -87
- package/lib/safe-sql.js +221 -59
- package/lib/safe-url.js +278 -46
- package/lib/sandbox-worker.js +135 -0
- package/lib/sandbox.js +358 -0
- package/lib/scheduler.js +135 -70
- package/lib/self-update.js +647 -0
- package/lib/session-device-binding.js +431 -0
- package/lib/session.js +259 -49
- package/lib/slug.js +138 -26
- package/lib/ssrf-guard.js +316 -56
- package/lib/storage.js +433 -70
- package/lib/subject.js +405 -23
- package/lib/template.js +148 -8
- package/lib/tenant-quota.js +545 -0
- package/lib/testing.js +440 -53
- package/lib/time.js +291 -23
- package/lib/tls-exporter.js +239 -0
- package/lib/tracing.js +90 -74
- package/lib/uuid.js +97 -22
- package/lib/vault/index.js +284 -22
- package/lib/vault/seal-pem-file.js +66 -0
- package/lib/watcher.js +368 -0
- package/lib/webhook.js +196 -63
- package/lib/websocket.js +393 -68
- package/lib/wiki-concepts.js +338 -0
- package/lib/worker-pool.js +464 -0
- package/package.json +3 -3
- package/sbom.cyclonedx.json +7 -7
package/lib/safe-sql.js
CHANGED
|
@@ -1,53 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* IDENTIFIER injection
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
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
|
|
3
|
+
* @module b.safeSql
|
|
4
|
+
* @nav Validation
|
|
5
|
+
* @title Safe Sql
|
|
6
|
+
*
|
|
7
|
+
* @intro
|
|
8
|
+
* SQL identifier validation + dialect-aware quoting + allowlist
|
|
9
|
+
* gating. Defends against IDENTIFIER injection — the residual attack
|
|
10
|
+
* surface left over when a TABLE name or COLUMN name flows from
|
|
11
|
+
* operator-supplied config into a SQL string. Values bind through
|
|
12
|
+
* parameterized queries everywhere in the framework, but parameters
|
|
13
|
+
* can't carry identifiers; that interpolation is what this module
|
|
14
|
+
* guards.
|
|
15
|
+
*
|
|
16
|
+
* Default identifier shape: `^[A-Za-z_][A-Za-z0-9_]*$`, length 1–63
|
|
17
|
+
* (Postgres NAMEDATALEN — the strictest of the supported dialects).
|
|
18
|
+
* Reserved words (SELECT / DROP / PRAGMA / ATTACH / …) and the
|
|
19
|
+
* SQLite-internal `sqlite_` prefix are refused unless the caller
|
|
20
|
+
* explicitly opts in. Quoting follows dialect convention: SQLite +
|
|
21
|
+
* Postgres double-quote, MySQL backtick. Multi-segment names
|
|
22
|
+
* (`schema.table`) validate + quote each segment independently so
|
|
23
|
+
* the dotted form `"schema"."table"` resolves correctly instead of
|
|
24
|
+
* collapsing into one literal identifier with a dot in it.
|
|
25
|
+
*
|
|
26
|
+
* Recommended pattern is the closed allowlist:
|
|
27
|
+
*
|
|
28
|
+
* var ALLOWED = new Set(["audit_log", "consent_log"]);
|
|
29
|
+
* b.safeSql.assertOneOf(name, ALLOWED);
|
|
30
|
+
* var sql = "INSERT INTO " + b.safeSql.quoteIdentifier(name) + " ...";
|
|
31
|
+
*
|
|
32
|
+
* The allowlist is the strongest guarantee. Operators with genuinely
|
|
33
|
+
* dynamic identifier needs use `validateIdentifier` alone, accepting
|
|
50
34
|
* that any string passing the regex is allowed.
|
|
35
|
+
*
|
|
36
|
+
* Validation policy: every primitive throws `SafeSqlError` on bad
|
|
37
|
+
* input — these run at SQL-composition time, well before the query
|
|
38
|
+
* reaches the database. The throw IS the security signal.
|
|
39
|
+
*
|
|
40
|
+
* @card
|
|
41
|
+
* SQL identifier validation + dialect-aware quoting + allowlist gating.
|
|
51
42
|
*/
|
|
52
43
|
|
|
53
44
|
// Reserved-word block list — the most dangerous to accept as a bare
|
|
@@ -73,6 +64,30 @@ var MAX_IDENTIFIER_LENGTH = 63;
|
|
|
73
64
|
|
|
74
65
|
var { FrameworkError } = require("./framework-error");
|
|
75
66
|
|
|
67
|
+
/**
|
|
68
|
+
* @primitive b.safeSql.SafeSqlError
|
|
69
|
+
* @signature b.safeSql.SafeSqlError
|
|
70
|
+
* @since 0.1.0
|
|
71
|
+
* @status stable
|
|
72
|
+
* @related b.safeSql.validateIdentifier, b.safeSql.quoteIdentifier, b.safeSql.assertOneOf
|
|
73
|
+
*
|
|
74
|
+
* Error class thrown by every `b.safeSql` primitive on bad input.
|
|
75
|
+
* Extends `FrameworkError`. Carries a stable `.code` —
|
|
76
|
+
* `sql/bad-type` / `sql/empty` / `sql/too-long` / `sql/null-byte` /
|
|
77
|
+
* `sql/bad-shape` / `sql/reserved-word` / `sql/internal-prefix` /
|
|
78
|
+
* `sql/not-allowed` / `sql/bad-allowlist`. Operators catch these at
|
|
79
|
+
* SQL-composition boundaries; the throw fires before the query
|
|
80
|
+
* reaches the database driver.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* var b = require("blamejs");
|
|
84
|
+
* try {
|
|
85
|
+
* b.safeSql.validateIdentifier("drop");
|
|
86
|
+
* } catch (e) {
|
|
87
|
+
* e instanceof b.safeSql.SafeSqlError; // → true
|
|
88
|
+
* e.code; // → "sql/reserved-word"
|
|
89
|
+
* }
|
|
90
|
+
*/
|
|
76
91
|
class SafeSqlError extends FrameworkError {
|
|
77
92
|
constructor(message, code) {
|
|
78
93
|
super(message);
|
|
@@ -82,6 +97,42 @@ class SafeSqlError extends FrameworkError {
|
|
|
82
97
|
}
|
|
83
98
|
}
|
|
84
99
|
|
|
100
|
+
/**
|
|
101
|
+
* @primitive b.safeSql.validateIdentifier
|
|
102
|
+
* @signature b.safeSql.validateIdentifier(name, opts?)
|
|
103
|
+
* @since 0.1.0
|
|
104
|
+
* @status stable
|
|
105
|
+
* @related b.safeSql.quoteIdentifier, b.safeSql.assertOneOf, b.safeSql.SafeSqlError
|
|
106
|
+
*
|
|
107
|
+
* Throw-on-bad-shape validator for SQL table / column / index names.
|
|
108
|
+
* Enforces the default identifier regex (`[A-Za-z_][A-Za-z0-9_]*`),
|
|
109
|
+
* a 63-character cap (Postgres NAMEDATALEN — strictest supported
|
|
110
|
+
* dialect), no embedded null byte, no SQL reserved word, no
|
|
111
|
+
* SQLite-internal `sqlite_` prefix. Returns `name` on success so the
|
|
112
|
+
* call composes inside a SQL fragment without an extra temporary.
|
|
113
|
+
*
|
|
114
|
+
* @opts
|
|
115
|
+
* pattern: RegExp, // override the default shape regex
|
|
116
|
+
* allowReserved: boolean, // default false; permit reserved words like "select"/"drop"
|
|
117
|
+
* allowSqliteInternal: boolean, // default false; permit "sqlite_..." identifiers
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* var b = require("blamejs");
|
|
121
|
+
* b.safeSql.validateIdentifier("audit_log");
|
|
122
|
+
* // → "audit_log"
|
|
123
|
+
*
|
|
124
|
+
* try { b.safeSql.validateIdentifier("drop"); }
|
|
125
|
+
* catch (e) { e.code; }
|
|
126
|
+
* // → "sql/reserved-word"
|
|
127
|
+
*
|
|
128
|
+
* try { b.safeSql.validateIdentifier("evil; DROP"); }
|
|
129
|
+
* catch (e) { e.code; }
|
|
130
|
+
* // → "sql/bad-shape"
|
|
131
|
+
*
|
|
132
|
+
* // Operator opts in to a custom shape (still ASCII-only, still capped).
|
|
133
|
+
* b.safeSql.validateIdentifier("col-1", { pattern: /^[A-Za-z][A-Za-z0-9_-]*$/ });
|
|
134
|
+
* // → "col-1"
|
|
135
|
+
*/
|
|
85
136
|
function validateIdentifier(name, opts) {
|
|
86
137
|
opts = opts || {};
|
|
87
138
|
if (typeof name !== "string") {
|
|
@@ -122,6 +173,29 @@ function validateIdentifier(name, opts) {
|
|
|
122
173
|
return name;
|
|
123
174
|
}
|
|
124
175
|
|
|
176
|
+
/**
|
|
177
|
+
* @primitive b.safeSql.quoteIdentifier
|
|
178
|
+
* @signature b.safeSql.quoteIdentifier(name, dialect?)
|
|
179
|
+
* @since 0.1.0
|
|
180
|
+
* @status stable
|
|
181
|
+
* @related b.safeSql.validateIdentifier, b.safeSql.quoteQualified
|
|
182
|
+
*
|
|
183
|
+
* Validate `name` then wrap it in dialect-appropriate quotes —
|
|
184
|
+
* double-quote for SQLite + Postgres (per SQL standard), backtick for
|
|
185
|
+
* MySQL. Default dialect is `"sqlite"`. Throws `SafeSqlError` if the
|
|
186
|
+
* identifier fails `validateIdentifier`.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* var b = require("blamejs");
|
|
190
|
+
* b.safeSql.quoteIdentifier("users");
|
|
191
|
+
* // → '"users"'
|
|
192
|
+
*
|
|
193
|
+
* b.safeSql.quoteIdentifier("Order", "postgres");
|
|
194
|
+
* // → '"Order"'
|
|
195
|
+
*
|
|
196
|
+
* b.safeSql.quoteIdentifier("users", "mysql");
|
|
197
|
+
* // → "`users`"
|
|
198
|
+
*/
|
|
125
199
|
function quoteIdentifier(name, dialect) {
|
|
126
200
|
validateIdentifier(name);
|
|
127
201
|
dialect = (dialect || "sqlite").toLowerCase();
|
|
@@ -130,18 +204,35 @@ function quoteIdentifier(name, dialect) {
|
|
|
130
204
|
return '"' + name + '"';
|
|
131
205
|
}
|
|
132
206
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
207
|
+
/**
|
|
208
|
+
* @primitive b.safeSql.quoteQualified
|
|
209
|
+
* @signature b.safeSql.quoteQualified(parts, dialect?)
|
|
210
|
+
* @since 0.1.0
|
|
211
|
+
* @status stable
|
|
212
|
+
* @related b.safeSql.quoteIdentifier, b.safeSql.validateIdentifier
|
|
213
|
+
*
|
|
214
|
+
* Quote a multi-part qualified name like `schema.table` or
|
|
215
|
+
* `database.schema.table`. Each segment is validated and quoted
|
|
216
|
+
* independently so the resulting SQL is `"schema"."table"` (three
|
|
217
|
+
* lookups against the catalog) instead of `"schema.table"` (one
|
|
218
|
+
* literal identifier with a dot in its name — a different and
|
|
219
|
+
* usually-nonexistent object). Accepts an array of parts OR a
|
|
220
|
+
* dot-separated string.
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* var b = require("blamejs");
|
|
224
|
+
* b.safeSql.quoteQualified(["public", "users"]);
|
|
225
|
+
* // → '"public"."users"'
|
|
226
|
+
*
|
|
227
|
+
* b.safeSql.quoteQualified("public.users");
|
|
228
|
+
* // → '"public"."users"'
|
|
229
|
+
*
|
|
230
|
+
* b.safeSql.quoteQualified("dbA.public.users");
|
|
231
|
+
* // → '"dbA"."public"."users"'
|
|
232
|
+
*
|
|
233
|
+
* b.safeSql.quoteQualified(["app", "orders"], "mysql");
|
|
234
|
+
* // → "`app`.`orders`"
|
|
235
|
+
*/
|
|
145
236
|
function quoteQualified(parts, dialect) {
|
|
146
237
|
var arr;
|
|
147
238
|
if (typeof parts === "string") {
|
|
@@ -167,6 +258,36 @@ function quoteQualified(parts, dialect) {
|
|
|
167
258
|
return quoted.join(".");
|
|
168
259
|
}
|
|
169
260
|
|
|
261
|
+
/**
|
|
262
|
+
* @primitive b.safeSql.assertOneOf
|
|
263
|
+
* @signature b.safeSql.assertOneOf(name, allowlist)
|
|
264
|
+
* @since 0.1.0
|
|
265
|
+
* @status stable
|
|
266
|
+
* @related b.safeSql.validateIdentifier, b.safeSql.quoteIdentifier
|
|
267
|
+
*
|
|
268
|
+
* Closed-allowlist gate — the strongest guarantee against identifier
|
|
269
|
+
* injection. `allowlist` is a `Set` or `Array` of permitted names;
|
|
270
|
+
* anything outside throws `SafeSqlError` with `.code = "sql/not-allowed"`.
|
|
271
|
+
* Returns `name` on success so the call composes inline with
|
|
272
|
+
* `quoteIdentifier`. Use this whenever the operator-supplied identifier
|
|
273
|
+
* is drawn from a known finite set (which is most cases — table names
|
|
274
|
+
* are config, not user input).
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* var b = require("blamejs");
|
|
278
|
+
* var ALLOWED = new Set(["audit_log", "consent_log", "session"]);
|
|
279
|
+
*
|
|
280
|
+
* b.safeSql.assertOneOf("audit_log", ALLOWED);
|
|
281
|
+
* // → "audit_log"
|
|
282
|
+
*
|
|
283
|
+
* try { b.safeSql.assertOneOf("users", ALLOWED); }
|
|
284
|
+
* catch (e) { e.code; }
|
|
285
|
+
* // → "sql/not-allowed"
|
|
286
|
+
*
|
|
287
|
+
* // Array form works too.
|
|
288
|
+
* b.safeSql.assertOneOf("audit_log", ["audit_log", "consent_log"]);
|
|
289
|
+
* // → "audit_log"
|
|
290
|
+
*/
|
|
170
291
|
function assertOneOf(name, allowlist) {
|
|
171
292
|
if (typeof name !== "string") {
|
|
172
293
|
throw new SafeSqlError("name must be a string", "sql/bad-type");
|
|
@@ -189,6 +310,47 @@ function assertOneOf(name, allowlist) {
|
|
|
189
310
|
return name;
|
|
190
311
|
}
|
|
191
312
|
|
|
313
|
+
/**
|
|
314
|
+
* @primitive b.safeSql.DEFAULT_IDENTIFIER_RE
|
|
315
|
+
* @signature b.safeSql.DEFAULT_IDENTIFIER_RE
|
|
316
|
+
* @since 0.1.0
|
|
317
|
+
* @status stable
|
|
318
|
+
* @related b.safeSql.validateIdentifier, b.safeSql.MAX_IDENTIFIER_LENGTH
|
|
319
|
+
*
|
|
320
|
+
* The default identifier shape regex — `/^[A-Za-z_][A-Za-z0-9_]*$/`.
|
|
321
|
+
* Exposed so operator code that needs a slightly-wider or
|
|
322
|
+
* slightly-narrower shape can compose against it instead of
|
|
323
|
+
* re-deriving the pattern. ASCII-only by design — Unicode
|
|
324
|
+
* identifiers are dialect-specific and surface in mismatched-encoding
|
|
325
|
+
* footguns we don't want to default into.
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* var b = require("blamejs");
|
|
329
|
+
* b.safeSql.DEFAULT_IDENTIFIER_RE.test("audit_log");
|
|
330
|
+
* // → true
|
|
331
|
+
*
|
|
332
|
+
* b.safeSql.DEFAULT_IDENTIFIER_RE.test("1starts_with_digit");
|
|
333
|
+
* // → false
|
|
334
|
+
*/
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* @primitive b.safeSql.MAX_IDENTIFIER_LENGTH
|
|
338
|
+
* @signature b.safeSql.MAX_IDENTIFIER_LENGTH
|
|
339
|
+
* @since 0.1.0
|
|
340
|
+
* @status stable
|
|
341
|
+
* @related b.safeSql.validateIdentifier, b.safeSql.DEFAULT_IDENTIFIER_RE
|
|
342
|
+
*
|
|
343
|
+
* Hard cap on identifier length — 63 characters. Matches Postgres'
|
|
344
|
+
* NAMEDATALEN default; SQLite and MySQL accept longer names but
|
|
345
|
+
* defaulting to the strictest dialect keeps cross-dialect SQL
|
|
346
|
+
* portable.
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* var b = require("blamejs");
|
|
350
|
+
* b.safeSql.MAX_IDENTIFIER_LENGTH;
|
|
351
|
+
* // → 63
|
|
352
|
+
*/
|
|
353
|
+
|
|
192
354
|
module.exports = {
|
|
193
355
|
validateIdentifier: validateIdentifier,
|
|
194
356
|
quoteIdentifier: quoteIdentifier,
|