@blamejs/core 0.15.12 → 0.15.14

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 (365) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/index.js +2 -0
  3. package/lib/a2a-tasks.js +45 -29
  4. package/lib/acme.js +6 -5
  5. package/lib/agent-event-bus.js +18 -4
  6. package/lib/agent-idempotency.js +7 -6
  7. package/lib/agent-orchestrator.js +2 -5
  8. package/lib/agent-saga.js +3 -5
  9. package/lib/agent-snapshot.js +32 -2
  10. package/lib/agent-tenant.js +2 -5
  11. package/lib/ai-adverse-decision.js +2 -15
  12. package/lib/ai-aedt-bias-audit.js +2 -1
  13. package/lib/ai-capability.js +1 -6
  14. package/lib/ai-content-detect.js +1 -3
  15. package/lib/ai-dp.js +1 -5
  16. package/lib/ai-frontier-protocol.js +1 -1
  17. package/lib/ai-input.js +2 -2
  18. package/lib/ai-model-manifest.js +1 -1
  19. package/lib/ai-output.js +16 -7
  20. package/lib/ai-pref.js +3 -8
  21. package/lib/ai-quota.js +3 -14
  22. package/lib/api-key.js +37 -28
  23. package/lib/api-snapshot.js +4 -1
  24. package/lib/app-shutdown.js +7 -1
  25. package/lib/archive-adapters.js +2 -4
  26. package/lib/archive-entry-policy.js +32 -0
  27. package/lib/archive-gz.js +9 -0
  28. package/lib/archive-read.js +14 -24
  29. package/lib/archive-tar-read.js +56 -24
  30. package/lib/archive.js +6 -12
  31. package/lib/arg-parser.js +7 -6
  32. package/lib/asn1-der.js +70 -22
  33. package/lib/asyncapi-traits.js +2 -6
  34. package/lib/atomic-file.js +312 -33
  35. package/lib/audit-chain.js +183 -54
  36. package/lib/audit-daily-review.js +36 -16
  37. package/lib/audit-emit.js +82 -0
  38. package/lib/audit-sign.js +258 -0
  39. package/lib/audit-tools.js +108 -23
  40. package/lib/audit.js +91 -2
  41. package/lib/auth/access-lock.js +7 -29
  42. package/lib/auth/bot-challenge.js +1 -1
  43. package/lib/auth/ciba.js +43 -4
  44. package/lib/auth/dpop.js +29 -36
  45. package/lib/auth/fido-mds3.js +14 -16
  46. package/lib/auth/jwt-external.js +26 -8
  47. package/lib/auth/jwt.js +15 -17
  48. package/lib/auth/lockout.js +24 -27
  49. package/lib/auth/oauth.js +67 -45
  50. package/lib/auth/oid4vci.js +55 -32
  51. package/lib/auth/oid4vp.js +3 -2
  52. package/lib/auth/openid-federation.js +6 -6
  53. package/lib/auth/passkey.js +4 -4
  54. package/lib/auth/password.js +8 -2
  55. package/lib/auth/saml.js +70 -40
  56. package/lib/auth/sd-jwt-vc-holder.js +2 -14
  57. package/lib/auth/sd-jwt-vc-issuer.js +2 -14
  58. package/lib/auth/sd-jwt-vc.js +25 -5
  59. package/lib/auth/status-list.js +21 -9
  60. package/lib/auth/step-up.js +15 -13
  61. package/lib/auth-bot-challenge.js +27 -39
  62. package/lib/backup/bundle.js +18 -20
  63. package/lib/backup/crypto.js +23 -8
  64. package/lib/backup/index.js +55 -67
  65. package/lib/backup/manifest.js +7 -1
  66. package/lib/bounded-map.js +112 -1
  67. package/lib/breach-deadline.js +1 -11
  68. package/lib/break-glass.js +41 -22
  69. package/lib/cache.js +7 -18
  70. package/lib/cbor.js +36 -12
  71. package/lib/cdn-cache-control.js +15 -12
  72. package/lib/cert.js +8 -13
  73. package/lib/chain-writer.js +162 -47
  74. package/lib/cli.js +10 -2
  75. package/lib/client-hints.js +7 -9
  76. package/lib/cloud-events.js +43 -33
  77. package/lib/cluster-storage.js +9 -41
  78. package/lib/cms-codec.js +2 -1
  79. package/lib/codepoint-class.js +84 -1
  80. package/lib/compliance-ai-act-logging.js +1 -6
  81. package/lib/compliance-ai-act-transparency.js +2 -4
  82. package/lib/compliance-eaa.js +2 -12
  83. package/lib/compliance-sanctions-fetcher.js +21 -28
  84. package/lib/compliance-sanctions.js +11 -21
  85. package/lib/compliance.js +3 -10
  86. package/lib/config-drift.js +24 -20
  87. package/lib/content-credentials.js +11 -8
  88. package/lib/content-digest.js +21 -12
  89. package/lib/cookies.js +15 -13
  90. package/lib/cose.js +39 -11
  91. package/lib/cra-report.js +1 -11
  92. package/lib/crdt.js +2 -1
  93. package/lib/crypto-field.js +34 -33
  94. package/lib/crypto.js +138 -7
  95. package/lib/csp.js +239 -3
  96. package/lib/daemon.js +46 -42
  97. package/lib/data-act.js +46 -13
  98. package/lib/db-file-lifecycle.js +14 -6
  99. package/lib/db-query.js +75 -49
  100. package/lib/db.js +66 -27
  101. package/lib/dbsc.js +5 -6
  102. package/lib/ddl-change-control.js +18 -14
  103. package/lib/deprecate.js +4 -5
  104. package/lib/did.js +6 -2
  105. package/lib/dora.js +43 -13
  106. package/lib/dr-runbook.js +1 -1
  107. package/lib/dsa.js +2 -1
  108. package/lib/dsr.js +30 -20
  109. package/lib/early-hints.js +19 -0
  110. package/lib/eat.js +5 -1
  111. package/lib/external-db-migrate.js +21 -22
  112. package/lib/external-db.js +74 -30
  113. package/lib/fda-21cfr11.js +42 -13
  114. package/lib/fdx.js +22 -18
  115. package/lib/file-upload.js +80 -66
  116. package/lib/flag-evaluation-context.js +5 -8
  117. package/lib/flag-providers.js +6 -2
  118. package/lib/forms.js +1 -1
  119. package/lib/framework-error.js +12 -0
  120. package/lib/framework-schema.js +19 -0
  121. package/lib/fsm.js +7 -12
  122. package/lib/gate-contract.js +911 -39
  123. package/lib/gdpr-ropa.js +22 -22
  124. package/lib/graphql-federation.js +18 -5
  125. package/lib/guard-agent-registry.js +2 -1
  126. package/lib/guard-all.js +3 -2
  127. package/lib/guard-archive.js +9 -30
  128. package/lib/guard-auth.js +23 -80
  129. package/lib/guard-cidr.js +20 -96
  130. package/lib/guard-csv.js +135 -196
  131. package/lib/guard-domain.js +23 -106
  132. package/lib/guard-dsn.js +17 -14
  133. package/lib/guard-email.js +46 -53
  134. package/lib/guard-envelope.js +2 -2
  135. package/lib/guard-event-bus-topic.js +2 -1
  136. package/lib/guard-filename.js +12 -60
  137. package/lib/guard-graphql.js +28 -75
  138. package/lib/guard-html-wcag.js +15 -2
  139. package/lib/guard-html.js +74 -128
  140. package/lib/guard-idempotency-key.js +2 -1
  141. package/lib/guard-image.js +280 -77
  142. package/lib/guard-imap-command.js +9 -10
  143. package/lib/guard-jmap.js +1 -1
  144. package/lib/guard-json.js +101 -109
  145. package/lib/guard-jsonpath.js +20 -88
  146. package/lib/guard-jwt.js +32 -114
  147. package/lib/guard-list-id.js +2 -7
  148. package/lib/guard-list-unsubscribe.js +2 -7
  149. package/lib/guard-mail-compose.js +5 -6
  150. package/lib/guard-mail-move.js +3 -2
  151. package/lib/guard-mail-query.js +5 -3
  152. package/lib/guard-mail-sieve.js +6 -7
  153. package/lib/guard-managesieve-command.js +6 -5
  154. package/lib/guard-markdown.js +76 -110
  155. package/lib/guard-message-id.js +5 -6
  156. package/lib/guard-mime.js +20 -99
  157. package/lib/guard-oauth.js +19 -73
  158. package/lib/guard-pdf.js +65 -72
  159. package/lib/guard-pop3-command.js +13 -14
  160. package/lib/guard-posture-chain.js +2 -1
  161. package/lib/guard-regex.js +24 -99
  162. package/lib/guard-saga-config.js +2 -1
  163. package/lib/guard-shell.js +22 -87
  164. package/lib/guard-smtp-command.js +9 -12
  165. package/lib/guard-sql.js +15 -13
  166. package/lib/guard-stream-args.js +2 -1
  167. package/lib/guard-svg.js +103 -149
  168. package/lib/guard-template.js +23 -80
  169. package/lib/guard-tenant-id.js +2 -1
  170. package/lib/guard-text.js +592 -0
  171. package/lib/guard-time.js +27 -95
  172. package/lib/guard-uuid.js +21 -93
  173. package/lib/guard-xml.js +76 -106
  174. package/lib/guard-yaml.js +24 -60
  175. package/lib/html-balance.js +7 -3
  176. package/lib/http-client-cache.js +5 -12
  177. package/lib/http-client-cookie-jar.js +35 -16
  178. package/lib/http-client.js +233 -74
  179. package/lib/http-message-signature.js +3 -2
  180. package/lib/i18n-messageformat.js +5 -7
  181. package/lib/i18n.js +83 -26
  182. package/lib/iab-tcf.js +3 -2
  183. package/lib/importmap-integrity.js +41 -1
  184. package/lib/inbox.js +8 -8
  185. package/lib/incident-report.js +12 -27
  186. package/lib/ip-utils.js +49 -6
  187. package/lib/jobs.js +3 -2
  188. package/lib/json-patch.js +1 -1
  189. package/lib/json-path.js +24 -3
  190. package/lib/jtd.js +2 -2
  191. package/lib/keychain.js +6 -18
  192. package/lib/legal-hold.js +30 -23
  193. package/lib/log-stream-cloudwatch.js +44 -112
  194. package/lib/log-stream-otlp-grpc.js +17 -14
  195. package/lib/log-stream-otlp.js +16 -80
  196. package/lib/log-stream-webhook.js +20 -92
  197. package/lib/log.js +2 -2
  198. package/lib/mail-agent.js +2 -2
  199. package/lib/mail-arc-sign.js +8 -3
  200. package/lib/mail-arf.js +4 -3
  201. package/lib/mail-auth.js +43 -69
  202. package/lib/mail-bimi.js +35 -55
  203. package/lib/mail-bounce.js +3 -3
  204. package/lib/mail-crypto-pgp.js +3 -2
  205. package/lib/mail-crypto-smime.js +71 -6
  206. package/lib/mail-dav.js +8 -35
  207. package/lib/mail-deploy.js +15 -14
  208. package/lib/mail-dkim.js +19 -38
  209. package/lib/mail-greylist.js +17 -30
  210. package/lib/mail-helo.js +4 -7
  211. package/lib/mail-journal.js +13 -9
  212. package/lib/mail-mdn.js +12 -7
  213. package/lib/mail-rbl.js +7 -12
  214. package/lib/mail-scan.js +5 -7
  215. package/lib/mail-send-deliver.js +33 -20
  216. package/lib/mail-server-imap.js +32 -87
  217. package/lib/mail-server-jmap.js +39 -52
  218. package/lib/mail-server-managesieve.js +29 -83
  219. package/lib/mail-server-mx.js +33 -74
  220. package/lib/mail-server-net.js +177 -0
  221. package/lib/mail-server-pop3.js +30 -83
  222. package/lib/mail-server-rate-limit.js +30 -6
  223. package/lib/mail-server-registry.js +1 -1
  224. package/lib/mail-server-submission.js +34 -73
  225. package/lib/mail-server-tls.js +98 -2
  226. package/lib/mail-spam-score.js +9 -12
  227. package/lib/mail-store-fts.js +1 -1
  228. package/lib/mail-store.js +3 -2
  229. package/lib/mail.js +28 -13
  230. package/lib/markup-escape.js +31 -0
  231. package/lib/markup-tokenizer.js +78 -0
  232. package/lib/mcp-tool-registry.js +26 -23
  233. package/lib/mcp.js +7 -5
  234. package/lib/mdoc.js +36 -14
  235. package/lib/metrics.js +46 -33
  236. package/lib/middleware/age-gate.js +3 -23
  237. package/lib/middleware/api-encrypt.js +13 -24
  238. package/lib/middleware/assetlinks.js +2 -7
  239. package/lib/middleware/bearer-auth.js +2 -1
  240. package/lib/middleware/body-parser.js +41 -52
  241. package/lib/middleware/bot-disclose.js +7 -10
  242. package/lib/middleware/bot-guard.js +28 -28
  243. package/lib/middleware/clear-site-data.js +5 -1
  244. package/lib/middleware/compression.js +9 -0
  245. package/lib/middleware/cors.js +32 -23
  246. package/lib/middleware/csrf-protect.js +73 -23
  247. package/lib/middleware/daily-byte-quota.js +12 -30
  248. package/lib/middleware/deny-response.js +19 -1
  249. package/lib/middleware/fetch-metadata.js +35 -4
  250. package/lib/middleware/idempotency-key.js +4 -20
  251. package/lib/middleware/network-allowlist.js +61 -30
  252. package/lib/middleware/rate-limit.js +45 -44
  253. package/lib/middleware/scim-server.js +5 -3
  254. package/lib/middleware/security-headers.js +33 -7
  255. package/lib/middleware/security-txt.js +2 -6
  256. package/lib/middleware/speculation-rules.js +6 -3
  257. package/lib/middleware/tus-upload.js +14 -29
  258. package/lib/middleware/web-app-manifest.js +2 -7
  259. package/lib/migrations.js +4 -13
  260. package/lib/mime-parse.js +34 -17
  261. package/lib/module-loader.js +44 -0
  262. package/lib/money.js +105 -0
  263. package/lib/mtls-ca.js +125 -41
  264. package/lib/network-byte-quota.js +4 -18
  265. package/lib/network-dane.js +8 -6
  266. package/lib/network-dns-resolver.js +98 -7
  267. package/lib/network-dns.js +9 -2
  268. package/lib/network-dnssec.js +18 -17
  269. package/lib/network-heartbeat.js +3 -2
  270. package/lib/network-smtp-policy.js +52 -46
  271. package/lib/network-tls.js +67 -47
  272. package/lib/network-tsig.js +2 -2
  273. package/lib/nis2-report.js +2 -12
  274. package/lib/nist-crosswalk.js +1 -1
  275. package/lib/nonce-store.js +81 -3
  276. package/lib/ntp-check.js +28 -0
  277. package/lib/numeric-bounds.js +31 -8
  278. package/lib/object-store/azure-blob-bucket-ops.js +2 -6
  279. package/lib/object-store/azure-blob.js +6 -47
  280. package/lib/object-store/gcs-bucket-ops.js +4 -2
  281. package/lib/object-store/gcs.js +14 -75
  282. package/lib/object-store/http-put.js +2 -7
  283. package/lib/object-store/http-request.js +157 -0
  284. package/lib/object-store/local.js +37 -17
  285. package/lib/object-store/sigv4-bucket-ops.js +2 -1
  286. package/lib/object-store/sigv4.js +10 -79
  287. package/lib/observability-otlp-exporter.js +29 -29
  288. package/lib/observability.js +95 -0
  289. package/lib/openapi-paths-builder.js +7 -3
  290. package/lib/otel-export.js +7 -10
  291. package/lib/outbox.js +54 -41
  292. package/lib/pagination.js +11 -15
  293. package/lib/parsers/safe-env.js +5 -4
  294. package/lib/parsers/safe-ini.js +10 -4
  295. package/lib/parsers/safe-toml.js +11 -9
  296. package/lib/parsers/safe-xml.js +3 -3
  297. package/lib/parsers/safe-yaml.js +28 -9
  298. package/lib/pick.js +63 -5
  299. package/lib/pipl-cn.js +69 -59
  300. package/lib/privacy-pass.js +8 -6
  301. package/lib/problem-details.js +2 -5
  302. package/lib/pubsub.js +4 -8
  303. package/lib/queue-local.js +10 -3
  304. package/lib/redact.js +9 -4
  305. package/lib/render.js +4 -2
  306. package/lib/request-helpers.js +334 -29
  307. package/lib/resource-access-lock.js +3 -3
  308. package/lib/restore-bundle.js +46 -18
  309. package/lib/restore-rollback.js +10 -4
  310. package/lib/restore.js +24 -22
  311. package/lib/retention.js +23 -9
  312. package/lib/router.js +17 -4
  313. package/lib/safe-async.js +457 -0
  314. package/lib/safe-buffer.js +137 -6
  315. package/lib/safe-decompress.js +2 -1
  316. package/lib/safe-dns.js +2 -1
  317. package/lib/safe-ical.js +14 -28
  318. package/lib/safe-icap.js +1 -1
  319. package/lib/safe-json.js +51 -8
  320. package/lib/safe-jsonpath.js +6 -5
  321. package/lib/safe-mime.js +25 -29
  322. package/lib/safe-redirect.js +2 -5
  323. package/lib/safe-schema.js +7 -6
  324. package/lib/safe-sieve.js +1 -1
  325. package/lib/safe-sql.js +126 -0
  326. package/lib/safe-vcard.js +13 -27
  327. package/lib/sandbox-worker.js +15 -2
  328. package/lib/sandbox.js +4 -3
  329. package/lib/scheduler.js +22 -13
  330. package/lib/sec-cyber.js +82 -37
  331. package/lib/seeders.js +4 -11
  332. package/lib/self-update-standalone-verifier.js +16 -0
  333. package/lib/self-update.js +92 -69
  334. package/lib/session-device-binding.js +112 -66
  335. package/lib/session.js +219 -7
  336. package/lib/sql.js +228 -81
  337. package/lib/sse.js +6 -10
  338. package/lib/static.js +201 -111
  339. package/lib/storage.js +4 -2
  340. package/lib/structured-fields.js +275 -16
  341. package/lib/template.js +7 -5
  342. package/lib/tenant-quota.js +53 -38
  343. package/lib/tsa.js +16 -17
  344. package/lib/validate-opts.js +154 -8
  345. package/lib/vault/index.js +5 -0
  346. package/lib/vault/passphrase-ops.js +22 -26
  347. package/lib/vault/passphrase-source.js +8 -3
  348. package/lib/vault/rotate.js +13 -18
  349. package/lib/vault/seal-pem-file.js +34 -49
  350. package/lib/vault-aad.js +3 -2
  351. package/lib/vc.js +3 -8
  352. package/lib/vendor/MANIFEST.json +10 -10
  353. package/lib/vendor/public-suffix-list.dat +23 -10
  354. package/lib/vendor/public-suffix-list.data.js +5498 -5494
  355. package/lib/vex.js +35 -13
  356. package/lib/web-push-vapid.js +23 -20
  357. package/lib/webhook-dispatcher.js +706 -0
  358. package/lib/webhook.js +59 -19
  359. package/lib/websocket-channels.js +9 -7
  360. package/lib/websocket.js +8 -4
  361. package/lib/worm.js +3 -4
  362. package/lib/ws-client.js +65 -56
  363. package/lib/x509-chain.js +44 -0
  364. package/package.json +1 -1
  365. package/sbom.cdx.json +6 -6
package/lib/guard-svg.js CHANGED
@@ -107,10 +107,11 @@
107
107
  */
108
108
 
109
109
  var codepointClass = require("./codepoint-class");
110
+ var markupTokenizer = require("./markup-tokenizer");
111
+ var markupEscape = require("./markup-escape").markupEscape;
110
112
  var lazyRequire = require("./lazy-require");
111
113
  var gateContract = require("./gate-contract");
112
114
  var C = require("./constants");
113
- var numericBounds = require("./numeric-bounds");
114
115
  var safeUrl = require("./safe-url");
115
116
  var { GuardSvgError } = require("./framework-error");
116
117
 
@@ -118,7 +119,6 @@ var observability = lazyRequire(function () { return require("./observability");
118
119
  void observability;
119
120
 
120
121
  var _err = GuardSvgError.factory;
121
- var HEX_RADIX = 16; // base-16 radix, not byte size
122
122
 
123
123
  // ---- Codepoint catalog (shared via lib/codepoint-class) ----
124
124
 
@@ -197,12 +197,10 @@ var URL_ATTRS = Object.freeze([
197
197
  "background", "poster", "icon",
198
198
  ]);
199
199
 
200
- var SAFE_SCHEMES = Object.freeze(["http", "https", "mailto", "tel"]);
200
+ var SAFE_SCHEMES = gateContract.SAFE_URL_SCHEMES;
201
201
 
202
- var DANGEROUS_SCHEMES = Object.freeze([
203
- "javascript", "vbscript", "livescript", "mocha", "ecmascript",
204
- "file", "mhtml", "jar", "intent", "view-source", "feed", "data",
205
- ]);
202
+ // Markup-attribute scheme denylist — the shared XSS / dangerous-resource set.
203
+ var DANGEROUS_SCHEMES = gateContract.DANGEROUS_URL_SCHEMES;
206
204
 
207
205
  var CSS_DANGEROUS_PATTERNS = Object.freeze([
208
206
  /expression\s*\(/i,
@@ -305,68 +303,14 @@ var PROFILES = Object.freeze({
305
303
  },
306
304
  });
307
305
 
308
- var DEFAULTS = Object.freeze(Object.assign({}, PROFILES["strict"], {
309
- mode: "enforce",
306
+ var DEFAULTS = gateContract.strictDefaults(PROFILES, {
310
307
  maxRuntimeMs: C.TIME.seconds(30),
311
- }));
312
-
313
- var COMPLIANCE_POSTURES = Object.freeze({
314
- "hipaa": {
315
- allowedTags: STRICT_ALLOWED_TAGS,
316
- bidiPolicy: "reject",
317
- controlPolicy: "reject",
318
- nullBytePolicy: "reject",
319
- cssPolicy: "reject",
320
- doctypePolicy: "reject",
321
- allowExternalRefs: false,
322
- allowAnimation: false,
323
- forensicSnippetBytes: C.BYTES.bytes(256),
324
- },
325
- "pci-dss": {
326
- allowedTags: STRICT_ALLOWED_TAGS,
327
- bidiPolicy: "reject",
328
- controlPolicy: "reject",
329
- nullBytePolicy: "reject",
330
- cssPolicy: "reject",
331
- doctypePolicy: "reject",
332
- allowExternalRefs: false,
333
- allowAnimation: false,
334
- urlSchemes: SAFE_SCHEMES,
335
- forensicSnippetBytes: C.BYTES.bytes(256),
336
- },
337
- "gdpr": {
338
- allowedTags: BALANCED_ALLOWED_TAGS,
339
- bidiPolicy: "strip",
340
- controlPolicy: "strip",
341
- cssPolicy: "strip",
342
- doctypePolicy: "reject",
343
- allowAnimation: false,
344
- forensicSnippetBytes: C.BYTES.bytes(128),
345
- },
346
- "soc2": {
347
- allowedTags: STRICT_ALLOWED_TAGS,
348
- bidiPolicy: "reject",
349
- controlPolicy: "reject",
350
- nullBytePolicy: "reject",
351
- cssPolicy: "reject",
352
- doctypePolicy: "reject",
353
- allowExternalRefs: false,
354
- allowAnimation: false,
355
- forensicSnippetBytes: C.BYTES.bytes(512),
356
- },
357
308
  });
358
309
 
310
+ var COMPLIANCE_POSTURES = gateContract.compliancePostures(PROFILES, { base: 256 });
311
+
359
312
  // ---- Internal helpers ----
360
313
 
361
- function _resolveOpts(opts) {
362
- return gateContract.resolveProfileAndPosture(opts, {
363
- profiles: PROFILES,
364
- compliancePostures: COMPLIANCE_POSTURES,
365
- defaults: DEFAULTS,
366
- errorClass: GuardSvgError,
367
- errCodePrefix: "svg",
368
- });
369
- }
370
314
 
371
315
  // HTML5 named-entity ASCII subset — same shape as guard-html.
372
316
  // Browsers honor these inside URL contexts; without decoding them,
@@ -384,12 +328,9 @@ var SVG_NAMED_ENTITY_ASCII = {
384
328
 
385
329
  function _extractScheme(rawUrl) {
386
330
  var s = String(rawUrl || "").trim();
387
- s = s.replace(/&#x([0-9a-f]+);/gi, function (_m, h) {
388
- return String.fromCharCode(parseInt(h, HEX_RADIX));
389
- });
390
- s = s.replace(/&#(\d+);/g, function (_m, d) {
391
- return String.fromCharCode(parseInt(d, 10));
392
- });
331
+ // Numeric entities (hex/decimal, semicolon OPTIONAL) via the shared decoder
332
+ // so guard-html / guard-svg / guard-markdown can't drift (see codepoint-class).
333
+ s = codepointClass.decodeNumericEntities(s);
393
334
  s = s.replace(/&([A-Za-z][A-Za-z0-9]+);/g, function (m, name) {
394
335
  if (Object.prototype.hasOwnProperty.call(SVG_NAMED_ENTITY_ASCII, name)) {
395
336
  return SVG_NAMED_ENTITY_ASCII[name];
@@ -436,9 +377,10 @@ function _isSvgz(input) {
436
377
 
437
378
  function _tokenize(input, maxBytes) {
438
379
  var s = String(input || "");
439
- if (s.length > maxBytes) {
380
+ var nb = Buffer.byteLength(s, "utf8");
381
+ if (nb > maxBytes) {
440
382
  throw _err("svg.too-large",
441
- "input " + s.length + " bytes exceeds maxBytes " + maxBytes);
383
+ "input " + nb + " bytes exceeds maxBytes " + maxBytes);
442
384
  }
443
385
  var tokens = [];
444
386
  var len = s.length;
@@ -455,8 +397,11 @@ function _tokenize(input, maxBytes) {
455
397
  }
456
398
 
457
399
  if (s.startsWith("<!--", lt)) {
458
- var endC = s.indexOf("-->", lt + 4);
459
- if (endC === -1) endC = len; else endC += 3;
400
+ // WHATWG comment end ("--!>" + abrupt "<!-->" / "<!--->"), not only
401
+ // "-->", so the comment boundary matches a browser parsing inline SVG
402
+ // and no element is smuggled past the sanitizer (mXSS differential).
403
+ var endC = markupTokenizer.htmlCommentEnd(s, lt);
404
+ if (endC === -1) endC = len;
460
405
  tokens.push({ type: "comment", raw: s.slice(lt, endC), start: lt, end: endC });
461
406
  pos = endC; continue;
462
407
  }
@@ -508,28 +453,16 @@ function _tokenize(input, maxBytes) {
508
453
  pos = endE; continue;
509
454
  }
510
455
 
511
- var pp = lt + 1;
512
- var inQuote = "";
513
- while (pp < len) {
514
- var ch = s.charAt(pp);
515
- if (inQuote) { if (ch === inQuote) inQuote = ""; }
516
- else {
517
- if (ch === '"' || ch === "'") inQuote = ch;
518
- else if (ch === ">") break;
519
- }
520
- pp += 1;
521
- }
456
+ var pp = markupTokenizer.scanToTagEnd(s, lt + 1, len);
522
457
  var endT = pp < len ? pp + 1 : len;
523
458
  var raw = s.slice(lt, endT);
524
459
  var inner = raw.slice(1, raw.charAt(raw.length - 1) === ">" ? raw.length - 1 : raw.length);
525
460
  var selfClosing = inner.endsWith("/");
526
461
  if (selfClosing) inner = inner.slice(0, inner.length - 1);
527
462
 
528
- var nameMatch = inner.match(/^([A-Za-z][A-Za-z0-9:_-]*)/);
529
- var tagName = nameMatch ? nameMatch[1].toLowerCase() : "";
530
- var attrSrc = nameMatch ? inner.slice(nameMatch[0].length) : "";
531
-
532
- var attrs = _parseAttrs(attrSrc);
463
+ var svgParts = markupTokenizer.splitTagNameAttrs(inner, /^([A-Za-z][A-Za-z0-9:_-]*)/);
464
+ var tagName = svgParts.tagName;
465
+ var attrs = _parseAttrs(svgParts.attrSrc);
533
466
  tokens.push({
534
467
  type: "tag", name: tagName, attrs: attrs,
535
468
  raw: raw, start: lt, end: endT, selfClosing: selfClosing,
@@ -576,6 +509,10 @@ function _parseAttrs(src) {
576
509
  // ---- Detection pass ----
577
510
 
578
511
  function _detectIssues(input, opts) {
512
+ if (typeof input !== "string" && !Buffer.isBuffer(input)) {
513
+ return [{ kind: "bad-input", severity: "high",
514
+ snippet: "input is not string or Buffer" }];
515
+ }
579
516
  if (_isSvgz(input)) {
580
517
  return [{
581
518
  kind: "svgz-compressed", severity: "critical", ruleId: "svg.svgz",
@@ -585,7 +522,7 @@ function _detectIssues(input, opts) {
585
522
  }
586
523
 
587
524
  var s = typeof input === "string" ? input : Buffer.from(input).toString("utf8");
588
- var issues = codepointClass.detectCharThreats(s, opts, "svg");
525
+ var issues = codepointClass.detectCharThreats(s, opts, "svg", "warn");
589
526
 
590
527
  var tokens;
591
528
  try { tokens = _tokenize(s, opts.maxBytes); }
@@ -708,7 +645,7 @@ function _detectIssues(input, opts) {
708
645
  for (var ai = 0; ai < attrs.length; ai += 1) {
709
646
  var a = attrs[ai];
710
647
  var an = a.name.toLowerCase();
711
- if (a.value && a.value.length > opts.maxAttrValueBytes) {
648
+ if (a.value && Buffer.byteLength(a.value, "utf8") > opts.maxAttrValueBytes) {
712
649
  issues.push({
713
650
  kind: "attr-value-too-large", severity: "high",
714
651
  ruleId: "svg.attr-size",
@@ -806,9 +743,10 @@ function _sanitize(input, opts) {
806
743
  throw _err("svg.svgz", "compressed SVGZ payload — operator must ungzip before sanitize");
807
744
  }
808
745
  var s = typeof input === "string" ? input : Buffer.from(input).toString("utf8");
809
- if (s.length > opts.maxBytes) {
746
+ var nb = Buffer.byteLength(s, "utf8");
747
+ if (nb > opts.maxBytes) {
810
748
  throw _err("svg.too-large",
811
- "input " + s.length + " bytes exceeds maxBytes " + opts.maxBytes);
749
+ "input " + nb + " bytes exceeds maxBytes " + opts.maxBytes);
812
750
  }
813
751
  codepointClass.assertNoCharThreats(s, opts, _err, "svg");
814
752
 
@@ -873,7 +811,7 @@ function _sanitize(input, opts) {
873
811
  var a = attrs[ai];
874
812
  var an = a.name.toLowerCase();
875
813
  if (EVENT_HANDLER_RE.test(an)) continue; // allow:regex-no-length-cap — `an` is a tokenized attribute name, bounded
876
- if (a.value && a.value.length > opts.maxAttrValueBytes) continue;
814
+ if (a.value && Buffer.byteLength(a.value, "utf8") > opts.maxAttrValueBytes) continue;
877
815
  if (URL_ATTRS.indexOf(an) !== -1) {
878
816
  var scheme = _extractScheme(a.value);
879
817
  var fragment = _isFragmentRef(a.value);
@@ -893,9 +831,7 @@ function _sanitize(input, opts) {
893
831
  !fragment && !opts.allowExternalRefs) continue;
894
832
  }
895
833
  if (an === "style" && _isCssDangerous(a.value)) continue;
896
- attrParts.push(an + "=\"" + a.value
897
- .replace(/&/g, "&amp;").replace(/</g, "&lt;")
898
- .replace(/>/g, "&gt;").replace(/"/g, "&quot;") + "\"");
834
+ attrParts.push(an + "=\"" + markupEscape(a.value) + "\"");
899
835
  }
900
836
  var open = "<" + tok.name + (attrParts.length ? " " + attrParts.join(" ") : "") +
901
837
  (tok.selfClosing ? "/>" : ">");
@@ -951,16 +887,12 @@ function _sanitize(input, opts) {
951
887
  * clean.ok; // → true
952
888
  * clean.issues.length; // → 0
953
889
  */
954
- function validate(input, opts) {
955
- opts = _resolveOpts(opts);
956
- numericBounds.requireAllPositiveFiniteIntIfPresent(opts,
957
- ["maxBytes", "maxElementCount", "maxUseDepth"],
958
- "guardSvg.validate", GuardSvgError, "svg.bad-opt");
959
-
960
- var bad = gateContract.badInputResultIfNotStringOrBuffer(input);
961
- if (bad) return bad;
962
- return gateContract.aggregateIssues(_detectIssues(input, opts));
963
- }
890
+ // validate is assembled by gateContract.defineGuard from `detect`
891
+ // (_detectIssues) below — `validate(input, opts) = aggregateIssues(detect(
892
+ // input, resolveOpts(opts)))`, with the maxBytes/maxElementCount/maxUseDepth
893
+ // caps declared via `intOpts`. Non-string/non-Buffer input returns a single
894
+ // `svg.bad-input` issue from _detectIssues (never throws). The @primitive
895
+ // block above documents the resulting public ABI.
964
896
 
965
897
  /**
966
898
  * @primitive b.guardSvg.sanitize
@@ -1003,7 +935,7 @@ function validate(input, opts) {
1003
935
  * /onload/.test(clean); // → false
1004
936
  */
1005
937
  function sanitize(input, opts) {
1006
- opts = _resolveOpts(opts);
938
+ opts = _guard.resolveOpts(opts);
1007
939
  if (typeof input !== "string" && !Buffer.isBuffer(input)) {
1008
940
  throw _err("svg.bad-input", "sanitize requires string or Buffer input");
1009
941
  }
@@ -1055,42 +987,57 @@ function sanitize(input, opts) {
1055
987
  * });
1056
988
  * refuse.action; // → "refuse"
1057
989
  */
1058
- function gate(opts) {
1059
- opts = _resolveOpts(opts);
1060
- return gateContract.buildGuardGate(
1061
- opts.name || "guardSvg:" + (opts.profile || "default"),
1062
- opts,
1063
- async function (ctx) {
1064
- var bytes = ctx.bytes;
1065
- if (!bytes) return { ok: true, action: "serve" };
1066
- var rv = validate(bytes, opts);
1067
- if (rv.issues.length === 0) return { ok: true, action: "serve" };
1068
- var hasCritical = rv.issues.some(function (i) {
1069
- return i.severity === "critical" || i.severity === "high";
1070
- });
1071
- if (!hasCritical) return { ok: true, action: "audit-only", issues: rv.issues };
1072
-
1073
- // SVGZ never sanitizable — refuse.
1074
- if (rv.issues.some(function (i) { return i.kind === "svgz-compressed"; })) {
1075
- return { ok: false, action: "refuse", issues: rv.issues };
1076
- }
990
+ // Disposition of each svg finding = what the operator's policy for that class
991
+ // selected. CSS injection / DOCTYPE / CDATA / processing-instruction and the
992
+ // bidi / null / control char threats follow their policies (sanitize under
993
+ // `strip`, refuse under `reject`, audit under `audit`). The always-dangerous
994
+ // classes (dangerous / animation tag, event handler, animation target,
995
+ // dangerous URL scheme, external ref, entity declaration) refuse; a gzipped
996
+ // SVGZ payload refuses (it must never reach the text sanitizer); a
997
+ // non-allowlisted but benign tag / scheme sanitizes; structural caps and a
998
+ // tokenizer failure refuse. Exhaustive over every kind _detectIssues emits.
999
+ function _gateDispositionFor(issue, opts) {
1000
+ var shared = gateContract.charThreatDisposition(issue, opts);
1001
+ if (shared) return shared;
1002
+ switch (issue.kind) {
1003
+ case "css-injection": return gateContract.policyDisposition(opts.cssPolicy);
1004
+ case "doctype": return gateContract.policyDisposition(opts.doctypePolicy);
1005
+ case "cdata": return gateContract.policyDisposition(opts.cdataPolicy);
1006
+ case "processing-instruction": return gateContract.policyDisposition(opts.processingInstrPolicy);
1007
+ case "non-allowlisted-tag":
1008
+ case "non-allowlisted-url-scheme": return "sanitize";
1009
+ case "svgz-compressed":
1010
+ case "entity-declaration":
1011
+ case "dangerous-tag":
1012
+ case "event-handler":
1013
+ case "animation-target":
1014
+ case "dangerous-url-scheme":
1015
+ case "external-ref": return "refuse";
1016
+ case "tokenize-failed":
1017
+ case "element-count-cap":
1018
+ case "attr-count-cap":
1019
+ case "use-depth-cap":
1020
+ case "attr-value-too-large":
1021
+ case "bad-input": return "refuse";
1022
+ default: return null;
1023
+ }
1024
+ }
1077
1025
 
1078
- if (opts.bidiPolicy !== "reject" &&
1079
- opts.controlPolicy !== "reject" &&
1080
- opts.nullBytePolicy !== "reject" &&
1081
- opts.cssPolicy !== "reject" &&
1082
- opts.doctypePolicy !== "reject") {
1083
- try {
1084
- var clean = sanitize(bytes, opts);
1085
- return {
1086
- ok: true, action: "sanitize",
1087
- sanitized: Buffer.from(clean, "utf8"),
1088
- issues: rv.issues,
1089
- };
1090
- } catch (_e) { /* fall through */ }
1091
- }
1092
- return { ok: false, action: "refuse", issues: rv.issues };
1093
- });
1026
+ function gate(opts) {
1027
+ opts = _guard.resolveOpts(opts);
1028
+ return gateContract.buildContentGate({
1029
+ name: opts.name || "guardSvg:" + (opts.profile || "default"),
1030
+ opts: opts,
1031
+ validate: module.exports.validate,
1032
+ dispositionFor: _gateDispositionFor,
1033
+ // SVG reads the RAW bytes (SVGZ gzip detection needs the byte signature).
1034
+ // A gzipped SVGZ must not be fed to the text sanitizer → it is refuse-
1035
+ // disposition, and sanitizeBlockingKinds is a second backstop that skips
1036
+ // the sanitize attempt for it.
1037
+ ctxField: "bytes",
1038
+ sanitizeBlockingKinds: ["svgz-compressed"],
1039
+ produceSanitized: function (bytes, o) { return sanitize(bytes, o); },
1040
+ });
1094
1041
  }
1095
1042
 
1096
1043
  // buildProfile / compliancePosture / loadRulePack are assembled by
@@ -1114,10 +1061,15 @@ var INTEGRATION_FIXTURES = Object.freeze({
1114
1061
  // Assembled from the gate-contract guard factory: error class, registry
1115
1062
  // exports (NAME / KIND / MIME_TYPES / EXTENSIONS / INTEGRATION_FIXTURES),
1116
1063
  // buildProfile / compliancePosture / loadRulePack wiring, plus the
1117
- // per-guard inspection surface (validate / sanitize / gate) and the SVG
1118
- // tag / scheme tables passed through verbatim. The bespoke `gate` carries
1119
- // SVG's sanitize-reserialize chain and SVGZ refuse unchanged.
1120
- module.exports = gateContract.defineGuard({
1064
+ // per-guard inspection surface and the SVG tag / scheme tables passed
1065
+ // through verbatim. `validate` is generated from `detect` (_detectIssues)
1066
+ // with the int caps declared via `intOpts`. The bespoke `sanitize` and
1067
+ // `gate` carry SVG's tag/attr strip-reserialize chain — which drops
1068
+ // dangerous tags rather than throwing on them — and the SVGZ refuse
1069
+ // unchanged; neither reduces to the dynamic
1070
+ // detect→throwOnRefusalSeverity→transform path (same-severity findings
1071
+ // split throw-vs-strip), so both stay bespoke.
1072
+ var _guard = module.exports = gateContract.defineGuard({
1121
1073
  name: "svg",
1122
1074
  kind: "content",
1123
1075
  errorClass: GuardSvgError,
@@ -1127,10 +1079,12 @@ module.exports = gateContract.defineGuard({
1127
1079
  mimeTypes: ["image/svg+xml"],
1128
1080
  extensions: [".svg", ".svgz"],
1129
1081
  integrationFixtures: INTEGRATION_FIXTURES,
1130
- validate: validate,
1082
+ detect: _detectIssues,
1083
+ intOpts: ["maxBytes", "maxElementCount", "maxUseDepth"],
1131
1084
  sanitize: sanitize,
1132
1085
  gate: gate,
1133
1086
  extra: {
1087
+ _gateDispositionForTest: _gateDispositionFor,
1134
1088
  DANGEROUS_TAGS: DANGEROUS_TAGS,
1135
1089
  ANIMATION_TAGS: ANIMATION_TAGS,
1136
1090
  ANIMATION_SAFE_TARGETS: ANIMATION_SAFE_TARGETS,
@@ -50,11 +50,9 @@
50
50
  * Server-Side Template Injection (SSTI) content-safety guard — refuses user-supplied strings that contain template-engine syntax BEFORE they're rendered.
51
51
  */
52
52
 
53
- var codepointClass = require("./codepoint-class");
54
53
  var lazyRequire = require("./lazy-require");
55
54
  var gateContract = require("./gate-contract");
56
55
  var C = require("./constants");
57
- var numericBounds = require("./numeric-bounds");
58
56
  var { GuardTemplateError } = require("./framework-error");
59
57
 
60
58
  var observability = lazyRequire(function () { return require("./observability"); });
@@ -74,10 +72,7 @@ var VELOCITY_DIR_RE = /#(?:set|if|else|elseif|end|foreach|parse|include|stop)\b/
74
72
 
75
73
  var PROFILES = Object.freeze({
76
74
  "strict": {
77
- bidiPolicy: "reject",
78
- controlPolicy: "reject",
79
- nullBytePolicy: "reject",
80
- zeroWidthPolicy: "reject",
75
+ ...gateContract.CHAR_THREATS_REJECT_ALL,
81
76
  jinjaPolicy: "reject",
82
77
  erbPolicy: "reject",
83
78
  pugPolicy: "reject",
@@ -87,10 +82,7 @@ var PROFILES = Object.freeze({
87
82
  maxRuntimeMs: C.TIME.seconds(2),
88
83
  },
89
84
  "balanced": {
90
- bidiPolicy: "reject",
91
- controlPolicy: "reject",
92
- nullBytePolicy: "reject",
93
- zeroWidthPolicy: "reject",
85
+ ...gateContract.CHAR_THREATS_REJECT_ALL,
94
86
  jinjaPolicy: "reject", // SSTI class — refused at every profile
95
87
  erbPolicy: "reject",
96
88
  pugPolicy: "reject",
@@ -100,10 +92,7 @@ var PROFILES = Object.freeze({
100
92
  maxRuntimeMs: C.TIME.seconds(2),
101
93
  },
102
94
  "permissive": {
103
- bidiPolicy: "reject", // BIDI refused at every profile
104
- controlPolicy: "reject", // controls refused at every profile
105
- nullBytePolicy: "reject", // null refused at every profile
106
- zeroWidthPolicy: "reject", // zero-width refused at every profile
95
+ ...gateContract.CHAR_THREATS_REJECT_ALL,
107
96
  jinjaPolicy: "reject", // SSTI class refused at every profile
108
97
  erbPolicy: "reject", // SSTI class refused at every profile
109
98
  pugPolicy: "reject", // SSTI class refused at every profile
@@ -114,51 +103,14 @@ var PROFILES = Object.freeze({
114
103
  },
115
104
  });
116
105
 
117
- var DEFAULTS = Object.freeze(Object.assign({}, PROFILES["strict"], {
118
- mode: "enforce",
119
- }));
106
+ var DEFAULTS = gateContract.strictDefaults(PROFILES);
120
107
 
121
- var COMPLIANCE_POSTURES = Object.freeze({
122
- "hipaa": Object.assign({}, PROFILES["strict"], {
123
- forensicSnippetBytes: C.BYTES.bytes(512),
124
- }),
125
- "pci-dss": Object.assign({}, PROFILES["strict"], {
126
- forensicSnippetBytes: C.BYTES.bytes(512),
127
- }),
128
- "gdpr": Object.assign({}, PROFILES["balanced"], {
129
- forensicSnippetBytes: C.BYTES.bytes(256),
130
- }),
131
- "soc2": Object.assign({}, PROFILES["strict"], {
132
- forensicSnippetBytes: C.BYTES.bytes(1024),
133
- }),
134
- });
135
-
136
- function _resolveOpts(opts) {
137
- return gateContract.resolveProfileAndPosture(opts, {
138
- profiles: PROFILES,
139
- compliancePostures: COMPLIANCE_POSTURES,
140
- defaults: DEFAULTS,
141
- errorClass: GuardTemplateError,
142
- errCodePrefix: "template",
143
- });
144
- }
108
+ var COMPLIANCE_POSTURES = gateContract.compliancePostures(PROFILES, { base: 512 });
145
109
 
146
110
  function _detectIssues(input, opts) {
147
- var issues = [];
148
- if (typeof input !== "string") {
149
- return [{ kind: "bad-input", severity: "high",
150
- ruleId: "template.bad-input",
151
- snippet: "template input is not a string" }];
152
- }
153
- if (input.length === 0) return [];
154
- if (Buffer.byteLength(input, "utf8") > opts.maxBytes) {
155
- return [{ kind: "input-cap", severity: "high",
156
- ruleId: "template.input-cap",
157
- snippet: "template input exceeds maxBytes " + opts.maxBytes }];
158
- }
159
-
160
- var charThreats = codepointClass.detectCharThreats(input, opts, "template");
161
- for (var ci = 0; ci < charThreats.length; ci += 1) issues.push(charThreats[ci]);
111
+ var pre = gateContract.detectStringInput(input, opts, { name: "template", noun: "template input", emptyMode: "ok", cap: { bytes: opts.maxBytes, kind: "input-cap", snippet: "template input exceeds maxBytes " + opts.maxBytes } });
112
+ if (pre.done) return pre.issues;
113
+ var issues = pre.issues;
162
114
 
163
115
  if (opts.jinjaPolicy !== "allow") {
164
116
  if (JINJA_EXPR_RE.test(input)) { // allow:regex-no-length-cap — input bounded by maxBytes
@@ -258,13 +210,10 @@ function _detectIssues(input, opts) {
258
210
  * hostile.ok; // → false
259
211
  * hostile.issues.some(function (i) { return i.kind === "jinja-expression"; }); // → true
260
212
  */
261
- function validate(input, opts) {
262
- opts = _resolveOpts(opts);
263
- numericBounds.requireAllPositiveFiniteIntIfPresent(opts,
264
- ["maxBytes"],
265
- "guardTemplate.validate", GuardTemplateError, "template.bad-opt");
266
- return gateContract.aggregateIssues(_detectIssues(input, opts));
267
- }
213
+ // validate is assembled by gateContract.defineGuard from `detect`
214
+ // (_detectIssues) below — `validate(input, opts) = aggregateIssues(detect(
215
+ // input, resolveOpts(opts)))`, with the maxBytes cap declared via `intOpts`.
216
+ // The @primitive block above documents the resulting public ABI.
268
217
 
269
218
  /**
270
219
  * @primitive b.guardTemplate.sanitize
@@ -305,13 +254,12 @@ function validate(input, opts) {
305
254
  * e.code; // → "template.jinja-expression"
306
255
  * }
307
256
  */
308
- function sanitize(input, opts) {
309
- opts = _resolveOpts(opts);
310
- if (typeof input !== "string") {
311
- throw _err("template.bad-input", "sanitize requires string input");
312
- }
313
- var issues = _detectIssues(input, opts);
314
- gateContract.throwOnRefusalSeverity(issues, { errorClass: GuardTemplateError, codePrefix: "template" });
257
+ // _sanitizeTransform the guard-specific normalize applied by defineGuard's
258
+ // generated sanitize AFTER resolve → detect → throw-on-refusal. Input is an
259
+ // already-validated string at this point (a non-string refuses upstream).
260
+ // Template input cannot be repaired safely, so the transform is pass-through:
261
+ // clean input is returned verbatim once no high/critical issue fired.
262
+ function _sanitizeTransform(input) {
315
263
  return input;
316
264
  }
317
265
 
@@ -323,14 +271,8 @@ function sanitize(input, opts) {
323
271
  // @abiTemplate (defineGuard) blocks in gate-contract.js, instantiated per
324
272
  // guard by the page generator.
325
273
 
326
- var INTEGRATION_FIXTURES = Object.freeze({
327
- kind: "identifier",
328
- benignBytes: Buffer.from("Hello world", "utf8"),
329
- hostileBytes: Buffer.from("Hello {{7*7}}", "utf8"),
330
- benignIdentifier: "Hello world",
331
- // Hostile: Jinja-shape SSTI probe.
332
- hostileIdentifier: "Hello {{7*7}}",
333
- });
274
+ // Hostile: Jinja-shape SSTI probe.
275
+ var INTEGRATION_FIXTURES = gateContract.identifierFixtures("Hello world", "Hello {{7*7}}");
334
276
 
335
277
  // Assembled from the gate-contract guard factory: error class, registry
336
278
  // exports (NAME / KIND / INTEGRATION_FIXTURES), the default gate, buildProfile
@@ -347,7 +289,8 @@ module.exports = gateContract.defineGuard({
347
289
  defaults: DEFAULTS,
348
290
  postures: COMPLIANCE_POSTURES,
349
291
  integrationFixtures: INTEGRATION_FIXTURES,
350
- validate: validate,
351
- sanitize: sanitize,
292
+ detect: _detectIssues,
293
+ sanitizeTransform: _sanitizeTransform,
294
+ intOpts: ["maxBytes"],
352
295
  ctxFields: ["identifier", "text"],
353
296
  });
@@ -24,6 +24,7 @@
24
24
 
25
25
  var { defineClass } = require("./framework-error");
26
26
  var gateContract = require("./gate-contract");
27
+ var codepointClass = require("./codepoint-class");
27
28
 
28
29
  var GuardTenantIdError = defineClass("GuardTenantIdError", { alwaysPermanent: true });
29
30
 
@@ -93,7 +94,7 @@ function validate(tenantId, opts) {
93
94
  throw new GuardTenantIdError("tenant-id/non-ascii",
94
95
  "guardTenantId.validate: non-ASCII codepoint at offset " + i);
95
96
  }
96
- if (c < 0x20 || c === 0x7F || c === 0x2F || c === 0x5C) { // C0/DEL/slash/backslash
97
+ if (codepointClass.isForbiddenControlChar(c, { forbidTab: true }) || c === 0x2F || c === 0x5C) { // C0/DEL/slash/backslash
97
98
  throw new GuardTenantIdError("tenant-id/bad-char",
98
99
  "guardTenantId.validate: forbidden char 0x" + c.toString(16) + " at offset " + i);
99
100
  }