@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.
Files changed (222) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +10 -10
  3. package/index.js +52 -0
  4. package/lib/a2a.js +159 -34
  5. package/lib/acme.js +762 -0
  6. package/lib/ai-pref.js +166 -43
  7. package/lib/api-key.js +108 -47
  8. package/lib/api-snapshot.js +157 -40
  9. package/lib/app-shutdown.js +113 -77
  10. package/lib/archive.js +337 -40
  11. package/lib/arg-parser.js +697 -0
  12. package/lib/asyncapi.js +99 -55
  13. package/lib/atomic-file.js +465 -104
  14. package/lib/audit-chain.js +123 -34
  15. package/lib/audit-daily-review.js +389 -0
  16. package/lib/audit-sign.js +302 -56
  17. package/lib/audit-tools.js +412 -63
  18. package/lib/audit.js +656 -35
  19. package/lib/auth/jwt-external.js +17 -0
  20. package/lib/auth/oauth.js +7 -0
  21. package/lib/auth-bot-challenge.js +505 -0
  22. package/lib/auth-header.js +92 -25
  23. package/lib/backup/bundle.js +26 -0
  24. package/lib/backup/index.js +512 -89
  25. package/lib/backup/manifest.js +168 -7
  26. package/lib/break-glass.js +415 -39
  27. package/lib/budr.js +103 -30
  28. package/lib/bundler.js +86 -66
  29. package/lib/cache.js +192 -72
  30. package/lib/chain-writer.js +65 -40
  31. package/lib/circuit-breaker.js +56 -33
  32. package/lib/cli-helpers.js +106 -75
  33. package/lib/cli.js +6 -30
  34. package/lib/cloud-events.js +99 -32
  35. package/lib/cluster-storage.js +162 -37
  36. package/lib/cluster.js +340 -49
  37. package/lib/codepoint-class.js +66 -0
  38. package/lib/compliance.js +424 -24
  39. package/lib/config-drift.js +111 -46
  40. package/lib/config.js +94 -40
  41. package/lib/consent.js +165 -18
  42. package/lib/constants.js +1 -0
  43. package/lib/content-credentials.js +153 -48
  44. package/lib/cookies.js +154 -62
  45. package/lib/credential-hash.js +133 -61
  46. package/lib/crypto-field.js +702 -18
  47. package/lib/crypto-hpke.js +256 -0
  48. package/lib/crypto.js +744 -22
  49. package/lib/csv.js +178 -35
  50. package/lib/daemon.js +456 -0
  51. package/lib/dark-patterns.js +186 -55
  52. package/lib/db-query.js +79 -2
  53. package/lib/db.js +1431 -60
  54. package/lib/ddl-change-control.js +523 -0
  55. package/lib/deprecate.js +195 -40
  56. package/lib/dev.js +82 -39
  57. package/lib/dora.js +67 -48
  58. package/lib/dr-runbook.js +368 -0
  59. package/lib/dsr.js +142 -11
  60. package/lib/dual-control.js +91 -56
  61. package/lib/events.js +120 -41
  62. package/lib/external-db-migrate.js +192 -2
  63. package/lib/external-db.js +795 -50
  64. package/lib/fapi2.js +122 -1
  65. package/lib/fda-21cfr11.js +395 -0
  66. package/lib/fdx.js +132 -2
  67. package/lib/file-type.js +87 -0
  68. package/lib/file-upload.js +93 -0
  69. package/lib/flag.js +82 -20
  70. package/lib/forms.js +132 -29
  71. package/lib/framework-error.js +169 -0
  72. package/lib/framework-schema.js +163 -35
  73. package/lib/gate-contract.js +849 -175
  74. package/lib/graphql-federation.js +68 -7
  75. package/lib/guard-all.js +172 -55
  76. package/lib/guard-archive.js +286 -124
  77. package/lib/guard-auth.js +194 -21
  78. package/lib/guard-cidr.js +190 -28
  79. package/lib/guard-csv.js +397 -51
  80. package/lib/guard-domain.js +213 -91
  81. package/lib/guard-email.js +236 -29
  82. package/lib/guard-filename.js +307 -75
  83. package/lib/guard-graphql.js +263 -30
  84. package/lib/guard-html.js +310 -116
  85. package/lib/guard-image.js +243 -30
  86. package/lib/guard-json.js +260 -54
  87. package/lib/guard-jsonpath.js +235 -23
  88. package/lib/guard-jwt.js +284 -30
  89. package/lib/guard-markdown.js +204 -22
  90. package/lib/guard-mime.js +190 -26
  91. package/lib/guard-oauth.js +277 -28
  92. package/lib/guard-pdf.js +251 -27
  93. package/lib/guard-regex.js +226 -18
  94. package/lib/guard-shell.js +229 -26
  95. package/lib/guard-svg.js +177 -10
  96. package/lib/guard-template.js +232 -21
  97. package/lib/guard-time.js +195 -29
  98. package/lib/guard-uuid.js +189 -30
  99. package/lib/guard-xml.js +259 -36
  100. package/lib/guard-yaml.js +241 -44
  101. package/lib/honeytoken.js +63 -27
  102. package/lib/html-balance.js +83 -0
  103. package/lib/http-client.js +486 -59
  104. package/lib/http-message-signature.js +582 -0
  105. package/lib/i18n.js +102 -49
  106. package/lib/iab-mspa.js +112 -32
  107. package/lib/iab-tcf.js +107 -2
  108. package/lib/inbox.js +90 -52
  109. package/lib/keychain.js +865 -0
  110. package/lib/legal-hold.js +374 -0
  111. package/lib/local-db-thin.js +320 -0
  112. package/lib/log-stream.js +281 -51
  113. package/lib/log.js +184 -86
  114. package/lib/mail-bounce.js +107 -62
  115. package/lib/mail.js +295 -58
  116. package/lib/mcp.js +108 -27
  117. package/lib/metrics.js +98 -89
  118. package/lib/middleware/age-gate.js +36 -0
  119. package/lib/middleware/ai-act-disclosure.js +37 -0
  120. package/lib/middleware/api-encrypt.js +45 -0
  121. package/lib/middleware/assetlinks.js +40 -0
  122. package/lib/middleware/asyncapi-serve.js +35 -0
  123. package/lib/middleware/attach-user.js +40 -0
  124. package/lib/middleware/bearer-auth.js +40 -0
  125. package/lib/middleware/body-parser.js +230 -0
  126. package/lib/middleware/bot-disclose.js +34 -0
  127. package/lib/middleware/bot-guard.js +39 -0
  128. package/lib/middleware/compression.js +37 -0
  129. package/lib/middleware/cookies.js +32 -0
  130. package/lib/middleware/cors.js +40 -0
  131. package/lib/middleware/csp-nonce.js +40 -0
  132. package/lib/middleware/csp-report.js +34 -0
  133. package/lib/middleware/csrf-protect.js +43 -0
  134. package/lib/middleware/daily-byte-quota.js +53 -85
  135. package/lib/middleware/db-role-for.js +40 -0
  136. package/lib/middleware/dpop.js +40 -0
  137. package/lib/middleware/error-handler.js +37 -14
  138. package/lib/middleware/fetch-metadata.js +39 -0
  139. package/lib/middleware/flag-context.js +34 -0
  140. package/lib/middleware/gpc.js +33 -0
  141. package/lib/middleware/headers.js +35 -0
  142. package/lib/middleware/health.js +46 -0
  143. package/lib/middleware/host-allowlist.js +30 -0
  144. package/lib/middleware/network-allowlist.js +38 -0
  145. package/lib/middleware/openapi-serve.js +34 -0
  146. package/lib/middleware/rate-limit.js +160 -18
  147. package/lib/middleware/request-id.js +36 -18
  148. package/lib/middleware/request-log.js +37 -0
  149. package/lib/middleware/require-aal.js +29 -0
  150. package/lib/middleware/require-auth.js +32 -0
  151. package/lib/middleware/require-bound-key.js +41 -0
  152. package/lib/middleware/require-content-type.js +32 -0
  153. package/lib/middleware/require-methods.js +27 -0
  154. package/lib/middleware/require-mtls.js +33 -0
  155. package/lib/middleware/require-step-up.js +37 -0
  156. package/lib/middleware/security-headers.js +44 -0
  157. package/lib/middleware/security-txt.js +38 -0
  158. package/lib/middleware/span-http-server.js +37 -0
  159. package/lib/middleware/sse.js +36 -0
  160. package/lib/middleware/trace-log-correlation.js +33 -0
  161. package/lib/middleware/trace-propagate.js +32 -0
  162. package/lib/middleware/tus-upload.js +90 -0
  163. package/lib/middleware/web-app-manifest.js +53 -0
  164. package/lib/mtls-ca.js +100 -70
  165. package/lib/network-byte-quota.js +308 -0
  166. package/lib/network-heartbeat.js +135 -0
  167. package/lib/network-tls.js +534 -4
  168. package/lib/network.js +103 -0
  169. package/lib/notify.js +114 -43
  170. package/lib/ntp-check.js +192 -51
  171. package/lib/observability.js +145 -47
  172. package/lib/openapi.js +90 -44
  173. package/lib/outbox.js +99 -1
  174. package/lib/pagination.js +168 -86
  175. package/lib/parsers/index.js +16 -5
  176. package/lib/permissions.js +93 -40
  177. package/lib/pqc-agent.js +84 -8
  178. package/lib/pqc-software.js +94 -60
  179. package/lib/process-spawn.js +95 -21
  180. package/lib/pubsub.js +96 -66
  181. package/lib/queue.js +375 -54
  182. package/lib/redact.js +793 -21
  183. package/lib/render.js +139 -47
  184. package/lib/request-helpers.js +485 -121
  185. package/lib/restore-bundle.js +142 -39
  186. package/lib/restore-rollback.js +136 -45
  187. package/lib/retention.js +178 -50
  188. package/lib/retry.js +116 -33
  189. package/lib/router.js +475 -23
  190. package/lib/safe-async.js +543 -94
  191. package/lib/safe-buffer.js +337 -41
  192. package/lib/safe-json.js +467 -62
  193. package/lib/safe-jsonpath.js +285 -0
  194. package/lib/safe-schema.js +631 -87
  195. package/lib/safe-sql.js +221 -59
  196. package/lib/safe-url.js +278 -46
  197. package/lib/sandbox-worker.js +135 -0
  198. package/lib/sandbox.js +358 -0
  199. package/lib/scheduler.js +135 -70
  200. package/lib/self-update.js +647 -0
  201. package/lib/session-device-binding.js +431 -0
  202. package/lib/session.js +259 -49
  203. package/lib/slug.js +138 -26
  204. package/lib/ssrf-guard.js +316 -56
  205. package/lib/storage.js +433 -70
  206. package/lib/subject.js +405 -23
  207. package/lib/template.js +148 -8
  208. package/lib/tenant-quota.js +545 -0
  209. package/lib/testing.js +440 -53
  210. package/lib/time.js +291 -23
  211. package/lib/tls-exporter.js +239 -0
  212. package/lib/tracing.js +90 -74
  213. package/lib/uuid.js +97 -22
  214. package/lib/vault/index.js +284 -22
  215. package/lib/vault/seal-pem-file.js +66 -0
  216. package/lib/watcher.js +368 -0
  217. package/lib/webhook.js +196 -63
  218. package/lib/websocket.js +393 -68
  219. package/lib/wiki-concepts.js +338 -0
  220. package/lib/worker-pool.js +464 -0
  221. package/package.json +3 -3
  222. package/sbom.cyclonedx.json +7 -7
package/lib/guard-html.js CHANGED
@@ -1,120 +1,95 @@
1
1
  "use strict";
2
2
  /**
3
- * guard-html — HTML content-safety primitive (b.guardHtml).
3
+ * @module b.guardHtml
4
+ * @nav Guards
5
+ * @title Guard Html
4
6
  *
5
- * Threat catalog grounded in 2026 sanitizer research (DOMPurify CVE-
6
- * series, OWASP XSS / DOM-Clobbering / HTML5 Security cheat sheets,
7
- * PortSwigger / Sonar / trace37 mXSS write-ups, html5sec.org).
7
+ * @intro
8
+ * HTML / XSS defense DOM-clobbering, mXSS, and entity-encoding
9
+ * bypasses. Tag and attribute allowlists; URL-scheme allowlist on
10
+ * every URL-bearing attribute; bidi / control / zero-width
11
+ * stripping. Threat catalog grounded in 2026 sanitizer research
12
+ * (DOMPurify CVE series, OWASP XSS / DOM-Clobbering / HTML5
13
+ * Security cheat sheets, PortSwigger / Sonar / trace37 mXSS
14
+ * write-ups, html5sec.org).
8
15
  *
9
- * var rv = b.guardHtml.validate(input, { profile: "strict" });
10
- * var safe = b.guardHtml.sanitize(input, { profile: "balanced" });
11
- * var html = b.guardHtml.escapeText("<oops>"); // &lt;oops&gt;
12
- * var attr = b.guardHtml.escapeAttr('say "hi"'); // say &quot;hi&quot;
13
- * var g = b.guardHtml.gate({ profile: "strict" });
16
+ * Three profiles ship `strict` / `balanced` / `permissive` —
17
+ * plus four compliance postures (`hipaa` / `pci-dss` / `gdpr` /
18
+ * `soc2`) that compose on top via the strictest-wins rule.
19
+ * `b.guardHtml.gate(opts)` returns a guard descriptor that plugs
20
+ * into `b.fileUpload.contentSafety` / `b.staticServe.contentSafety`
21
+ * / `b.guardAll`.
22
+ *
23
+ * Threat catalog covered:
24
+ *
25
+ * 1. Dangerous tags — script / style / link / meta / base /
26
+ * iframe / object / embed / applet / form / input / button /
27
+ * textarea / select / isindex / marquee / blink / layer /
28
+ * ilayer / plaintext / listing / xmp / audio / video / source
29
+ * / track / math / svg / template / noscript / noembed /
30
+ * noframes / portal / dialog / keygen / menuitem / command /
31
+ * frame / frameset.
32
+ * 2. on* event-handler attributes — every attribute matching
33
+ * /^on[a-z]/ is denied unconditionally.
34
+ * 3. Form-override attributes — formaction / formmethod /
35
+ * formenctype / formtarget / formnovalidate (CWE-1021).
36
+ * 4. Iframe inline-HTML — srcdoc on iframe always denied.
37
+ * 5. Custom-element registration — `is="..."` always denied.
38
+ * 6. CSP-bypass-shaped attributes — nonce / integrity /
39
+ * crossorigin stripped from sanitized output.
40
+ * 7. URL scheme validation on URL-bearing attributes — href /
41
+ * src / action / cite / longdesc / manifest / archive /
42
+ * codebase / data / classid / code / profile / ping / dynsrc
43
+ * / lowsrc / background / poster / icon / xlink:href.
44
+ * Per-profile allowlist; denied schemes (always):
45
+ * javascript / vbscript / livescript / mocha / data (outside
46
+ * image context) / file / mhtml / jar / intent / view-source.
47
+ * 8. CSS-injection inside style="..." values — expression( (IE),
48
+ * behavior: (IE), -moz-binding (Firefox legacy), javascript: /
49
+ * vbscript: / livescript: inside url(), @import, @namespace.
50
+ * 9. DOM clobbering — id and name attributes whose values match
51
+ * a well-known JS global (document / window / location /
52
+ * cookie / __proto__ / constructor / ...) on clobber-prone
53
+ * elements (form / input / button / a / img / iframe /
54
+ * object / embed / select / textarea).
55
+ * 10. mXSS hint detection — namespace-context-shift parents
56
+ * (svg / math), CDATA inside HTML mode, template content
57
+ * fragments with entity-encoded payloads.
58
+ * 11. Unicode bidi (CVE-2021-42574 Trojan Source) inside text
59
+ * and attribute values.
60
+ * 12. C0 control characters, null bytes, zero-width chars —
61
+ * strip-or-reject per profile.
62
+ * 13. IE conditional comments — <!--[if ...]>...<![endif]-->
63
+ * refused in strict, stripped in balanced.
64
+ * 14. <base href> / <base target> — silently redirects every
65
+ * relative URL on the page; always denied.
66
+ * 15. <meta http-equiv> with refresh / Set-Cookie / X-XSS-
67
+ * Protection — silent navigation + cookie injection.
68
+ * 16. ARIA spoofing — flagged for audit when role mismatches
69
+ * the semantic tag.
70
+ * 17. Image-context data: URLs — sanitize allows data:image/png
71
+ * / jpeg / gif / webp; svg+xml requires explicit opt-in.
72
+ * 18. Total-document size cap, per-attribute-value size cap,
73
+ * max-tag-depth (anti-recursion), max-attribute-count-per-
74
+ * tag.
14
75
  *
15
- * Threat catalog covered:
16
- *
17
- * 1. Dangerous tags <script>, <style>, <link>, <meta>, <base>,
18
- * <iframe>, <object>, <embed>, <applet>, <form>, <input>,
19
- * <button>, <textarea>, <select>, <isindex>, <marquee>, <blink>,
20
- * <layer>, <ilayer>, <plaintext>, <listing>, <xmp>, <audio>,
21
- * <video>, <source>, <track>, <math>, <svg>, <template>,
22
- * <noscript>, <noembed>, <noframes>, <portal>, <dialog>,
23
- * <keygen>, <menuitem>, <command>, <frame>, <frameset>.
24
- * Every match per profile triggers refuse OR sanitize-strip.
25
- *
26
- * 2. on* event-handler attributes every attribute matching
27
- * /^on[a-z]/ is denied unconditionally. Catches the entire HTML5
28
- * event-handler family (onclick, onerror, onload, onmouseover,
29
- * onbeforeunload, onpaste, onwheel, onpointerdown, ontoggle, ...)
30
- * without requiring a manual allowlist that rots the moment the
31
- * WHATWG specs a new event.
32
- *
33
- * 3. Form-override attributes — formaction / formmethod /
34
- * formenctype / formtarget / formnovalidate on <button> / <input>
35
- * override the parent form's submission target (CWE-1021); always
36
- * denied.
37
- *
38
- * 4. Iframe inline-HTML — srcdoc on <iframe> ships executable HTML
39
- * directly into the document; always denied.
40
- *
41
- * 5. Custom-element registration — `is="..."` attribute mutates the
42
- * element class via document.createElement(..., { is }); always
43
- * denied.
44
- *
45
- * 6. CSP-bypass-shaped attributes — nonce, integrity, crossorigin
46
- * stripped from sanitized output (operator-controlled only).
47
- *
48
- * 7. URL scheme validation on URL-bearing attributes — href, src,
49
- * action, cite, longdesc, manifest, archive, codebase, data,
50
- * classid, code, profile, ping, dynsrc, lowsrc, background,
51
- * poster, icon, xlink:href. Per-attribute scheme allowlist:
52
- * strict → http / https / mailto / tel only;
53
- * balanced → +data:image/* + ftp;
54
- * permissive → +ftp / sftp / ws / wss.
55
- * Denied schemes (always): javascript / vbscript / livescript /
56
- * mocha / data (outside image context) / file / mhtml / jar /
57
- * intent / view-source.
58
- *
59
- * 8. CSS-injection inside style="..." attribute values — denies
60
- * expression( (IE), behavior: (IE), -moz-binding (Firefox legacy),
61
- * javascript: / vbscript: / livescript: inside url(), @import,
62
- * @charset / @namespace (CSS-source-map confusion).
63
- *
64
- * 9. DOM clobbering — id and name attributes whose values match a
65
- * well-known JS global (document, window, location, cookie,
66
- * __proto__, constructor, ...) on form / input / button / anchor /
67
- * img / iframe elements. Catches the form-element + input-name
68
- * payload + named-property-access exfil chain.
69
- *
70
- * 10. mXSS hint detection — common parser-mode-shift vectors:
71
- * <svg><p>...</svg>, <math><p>...</math>, <noscript>...</noscript>
72
- * with quote+entity confusion, CDATA inside HTML mode, <template>
73
- * content fragment with entity-encoded payloads. Surfaced as
74
- * "mxss-hint" issues; refused in strict, audited in balanced.
75
- *
76
- * 11. Unicode bidi (CVE-2021-42574 Trojan Source) inside text and
77
- * attribute values — same codepoint catalog as guard-csv.
78
- *
79
- * 12. C0 control characters, null bytes, zero-width chars in input —
80
- * strip-or-reject per profile.
81
- *
82
- * 13. IE conditional comments — <!--[if ...]>...<![endif]--> can carry
83
- * executable script in the legacy IE rendering path; refused in
84
- * strict, stripped in balanced.
85
- *
86
- * 14. <base href> / <base target> — silently redirects every relative
87
- * URL on the page; always denied.
88
- *
89
- * 15. <meta http-equiv> with refresh / Set-Cookie / X-XSS-Protection
90
- * values — silent navigation + cookie injection; always denied.
91
- *
92
- * 16. ARIA spoofing — `role="button"` on a non-button element with
93
- * attached event handlers (caught upstream by the on* handler
94
- * strip); flagged for audit when role mismatches semantic tag.
95
- *
96
- * 17. Image-context data: URLs — sanitize allows data:image/png ;
97
- * data:image/jpeg ; data:image/gif ; data:image/webp ;
98
- * data:image/svg+xml requires explicit opt-in (svg embed is its
99
- * own threat surface).
100
- *
101
- * 18. Total-document size cap (anti-DoS), per-attribute-value size
102
- * cap, max-tag-depth (prevents recursion-shape parsers from
103
- * stack-blowing), max-attribute-count-per-tag.
104
- *
105
- * Threat-detection regex literals are composed PROGRAMMATICALLY from
106
- * numeric codepoint range tables (BIDI_RANGES / C0_CTRL_RANGES /
107
- * ZERO_WIDTH_RANGES). Source file never embeds the attack characters
108
- * themselves.
109
- *
110
- * Sanitize discipline: this module ships a token-level rewriter that
111
- * preserves the allowlisted tag set and strips the rest. For HOSTILE
112
- * sources, the documented correct response is validate + reject — not
113
- * sanitize. mXSS bypasses against any non-DOM sanitizer are a known
114
- * arms-race; the gate's "refuse" path is the one with strong invariants.
115
- * Operators with display-of-untrusted-html requirements should
116
- * additionally serve content under a strict CSP (default-src 'none' or
117
- * sandboxed iframe).
76
+ * Threat-detection regex literals are composed PROGRAMMATICALLY
77
+ * from numeric codepoint range tables (BIDI_RANGES /
78
+ * C0_CTRL_RANGES / ZERO_WIDTH_RANGES). The source file never
79
+ * embeds the attack characters themselves.
80
+ *
81
+ * Sanitize discipline: this module ships a token-level rewriter
82
+ * that preserves the allowlisted tag set and strips the rest.
83
+ * For HOSTILE sources, the documented correct response is
84
+ * validate + reject not sanitize. mXSS bypasses against any
85
+ * non-DOM sanitizer are a known arms-race; the gate's `refuse`
86
+ * path is the one with strong invariants. Operators with
87
+ * display-of-untrusted-html requirements should additionally
88
+ * serve content under a strict CSP (default-src 'none' or
89
+ * sandboxed iframe).
90
+ *
91
+ * @card
92
+ * HTML / XSS defense — DOM-clobbering, mXSS, and entity-encoding bypasses.
118
93
  */
119
94
 
120
95
  var codepointClass = require("./codepoint-class");
@@ -376,8 +351,25 @@ function _resolveOpts(opts) {
376
351
  });
377
352
  }
378
353
 
379
- // HTML entity escape — text-content context. Encodes & < > " ' so the
380
- // output is safe for embedding inside an element's text body.
354
+ /**
355
+ * @primitive b.guardHtml.escapeText
356
+ * @signature b.guardHtml.escapeText(value)
357
+ * @since 0.7.6
358
+ * @related b.guardHtml.escapeAttr, b.guardHtml.sanitize
359
+ *
360
+ * HTML entity-escape for text-content context. Encodes the five
361
+ * core characters `&` `<` `>` `"` `'` so the result is safe to
362
+ * embed inside an element's text body. `null` / `undefined` coerce
363
+ * to an empty string. Use this for plain interpolation of
364
+ * untrusted strings into rendered HTML.
365
+ *
366
+ * @example
367
+ * var html = b.guardHtml.escapeText("<oops>");
368
+ * // → "&lt;oops&gt;"
369
+ *
370
+ * var blank = b.guardHtml.escapeText(null);
371
+ * // → ""
372
+ */
381
373
  function escapeText(value) {
382
374
  var s = value == null ? "" : String(value);
383
375
  return s
@@ -388,8 +380,25 @@ function escapeText(value) {
388
380
  .replace(/'/g, "&#39;");
389
381
  }
390
382
 
391
- // HTML entity escape — attribute-value context. Same encoding plus
392
- // backtick (legacy IE attribute terminator) and = (unquoted-attr edge).
383
+ /**
384
+ * @primitive b.guardHtml.escapeAttr
385
+ * @signature b.guardHtml.escapeAttr(value)
386
+ * @since 0.7.6
387
+ * @related b.guardHtml.escapeText, b.guardHtml.sanitize
388
+ *
389
+ * HTML entity-escape for attribute-value context. Same five
390
+ * characters as `escapeText` plus backtick (legacy IE attribute
391
+ * terminator) and `=` (unquoted-attribute edge). Use this when
392
+ * interpolating an untrusted string between double-quoted attribute
393
+ * delimiters.
394
+ *
395
+ * @example
396
+ * var attr = b.guardHtml.escapeAttr('say "hi"');
397
+ * // → "say &quot;hi&quot;"
398
+ *
399
+ * var ie = b.guardHtml.escapeAttr("a`b=c");
400
+ * // → "a&#96;b&#61;c"
401
+ */
393
402
  function escapeAttr(value) {
394
403
  var s = value == null ? "" : String(value);
395
404
  return s
@@ -905,6 +914,53 @@ function _sanitize(input, opts) {
905
914
 
906
915
  // ---- Public surface ----
907
916
 
917
+ /**
918
+ * @primitive b.guardHtml.validate
919
+ * @signature b.guardHtml.validate(input, opts?)
920
+ * @since 0.7.6
921
+ * @related b.guardHtml.sanitize, b.guardHtml.gate
922
+ *
923
+ * Tokenize `input` (string or Buffer of HTML) and walk every
924
+ * element / attribute against the resolved profile. Returns
925
+ * `{ ok, issues }` where `issues` is an array of
926
+ * `{ kind, severity, ruleId, location, snippet }` records.
927
+ * Never modifies the input — call `sanitize` for that. Anti-DoS
928
+ * caps (`maxBytes` / `maxAttrValueBytes` / `maxTagDepth` /
929
+ * `maxAttrsPerTag`) are validated as positive finite integers;
930
+ * passing `Infinity` throws.
931
+ *
932
+ * @opts
933
+ * profile: string, // "strict" | "balanced" | "permissive"
934
+ * compliancePosture: string, // "hipaa" | "pci-dss" | "gdpr" | "soc2"
935
+ * allowedTags: Array<string>,
936
+ * allowedAttrs: Array<string>,
937
+ * urlSchemes: Array<string>,
938
+ * allowImageData: boolean,
939
+ * allowComments: boolean,
940
+ * bidiPolicy: string, // "reject" | "strip" | "audit"
941
+ * controlPolicy: string,
942
+ * nullBytePolicy: string,
943
+ * zeroWidthPolicy: string,
944
+ * cssPolicy: string,
945
+ * domClobberPolicy: string,
946
+ * mxssHintPolicy: string,
947
+ * maxBytes: number,
948
+ * maxAttrValueBytes: number,
949
+ * maxTagDepth: number,
950
+ * maxAttrsPerTag: number,
951
+ *
952
+ * @example
953
+ * var rv = b.guardHtml.validate("<p>hi</p><script>alert(1)</script>",
954
+ * { profile: "strict" });
955
+ * rv.ok; // → false
956
+ * rv.issues[0].kind; // → "dangerous-tag"
957
+ * rv.issues[0].severity; // → "critical"
958
+ *
959
+ * var clean = b.guardHtml.validate("<p>just text</p>",
960
+ * { profile: "strict" });
961
+ * clean.ok; // → true
962
+ * clean.issues.length; // → 0
963
+ */
908
964
  function validate(input, opts) {
909
965
  opts = _resolveOpts(opts);
910
966
  numericBounds.requireAllPositiveFiniteIntIfPresent(opts,
@@ -914,6 +970,46 @@ function validate(input, opts) {
914
970
  return gateContract.runIssueValidator(input, opts, _detectIssues);
915
971
  }
916
972
 
973
+ /**
974
+ * @primitive b.guardHtml.sanitize
975
+ * @signature b.guardHtml.sanitize(input, opts?)
976
+ * @since 0.7.6
977
+ * @related b.guardHtml.validate, b.guardHtml.gate
978
+ *
979
+ * Token-level rewriter that drops every tag NOT in the resolved
980
+ * profile's `allowedTags`, every attribute NOT in `allowedAttrs`,
981
+ * every URL whose scheme falls outside the profile allowlist,
982
+ * every event-handler / form-override / clobbering attribute, and
983
+ * every body of a body-drop tag (script / style / template / svg
984
+ * / math / iframe / object / embed / applet). For HOSTILE
985
+ * sources, prefer `validate` + `refuse` — `sanitize` is best
986
+ * effort against the documented arms-race.
987
+ *
988
+ * @opts
989
+ * profile: string, // "strict" | "balanced" | "permissive"
990
+ * compliancePosture: string, // "hipaa" | "pci-dss" | "gdpr" | "soc2"
991
+ * allowedTags: Array<string>,
992
+ * allowedAttrs: Array<string>,
993
+ * urlSchemes: Array<string>,
994
+ * allowImageData: boolean,
995
+ * allowComments: boolean,
996
+ * maxBytes: number,
997
+ * maxAttrValueBytes: number,
998
+ * maxTagDepth: number,
999
+ * maxAttrsPerTag: number,
1000
+ *
1001
+ * @example
1002
+ * var clean = b.guardHtml.sanitize(
1003
+ * "<p>hi</p><script>alert(1)</script>",
1004
+ * { profile: "balanced" });
1005
+ * // → "<p>hi</p>"
1006
+ *
1007
+ * // Event-handler attribute is stripped, text content preserved.
1008
+ * var stripped = b.guardHtml.sanitize(
1009
+ * '<a href="https://example.com" onclick="alert(1)">go</a>',
1010
+ * { profile: "balanced" });
1011
+ * // → '<a href="https://example.com">go</a>'
1012
+ */
917
1013
  function sanitize(input, opts) {
918
1014
  opts = _resolveOpts(opts);
919
1015
  var text = typeof input === "string"
@@ -925,6 +1021,46 @@ function sanitize(input, opts) {
925
1021
  return _sanitize(text, opts);
926
1022
  }
927
1023
 
1024
+ /**
1025
+ * @primitive b.guardHtml.gate
1026
+ * @signature b.guardHtml.gate(opts?)
1027
+ * @since 0.7.6
1028
+ * @related b.guardHtml.validate, b.guardHtml.sanitize, b.guardAll
1029
+ *
1030
+ * Returns a guard descriptor that plugs into the framework's
1031
+ * content-safety wiring (`b.fileUpload.contentSafety` /
1032
+ * `b.staticServe.contentSafety` / `b.guardAll`). The descriptor's
1033
+ * `inspect(ctx)` resolves to one of four actions: `serve` (no
1034
+ * issues), `audit-only` (low-severity issues observed), `sanitize`
1035
+ * (sanitized buffer attached when no policy is "reject"), or
1036
+ * `refuse` (critical issue with at least one reject-policy active).
1037
+ *
1038
+ * @opts
1039
+ * name: string,
1040
+ * profile: string, // "strict" | "balanced" | "permissive"
1041
+ * compliancePosture: string, // "hipaa" | "pci-dss" | "gdpr" | "soc2"
1042
+ * mode: string, // "enforce" | "observe"
1043
+ * allowedTags: Array<string>,
1044
+ * allowedAttrs: Array<string>,
1045
+ * urlSchemes: Array<string>,
1046
+ * bidiPolicy: string,
1047
+ * controlPolicy: string,
1048
+ * cssPolicy: string,
1049
+ * domClobberPolicy: string,
1050
+ * mxssHintPolicy: string,
1051
+ * maxBytes: number,
1052
+ * maxRuntimeMs: number,
1053
+ *
1054
+ * @example
1055
+ * var g = b.guardHtml.gate({ profile: "strict" });
1056
+ * g.name; // → "guardHtml:strict"
1057
+ *
1058
+ * // Refuse on tag-budget exceeded — strict profile rejects <script>.
1059
+ * var hostileBuf = Buffer.from("<p>hi</p><script>alert(1)</script>", "utf8");
1060
+ * var rv = await g.inspect({ bytes: hostileBuf, contentType: "text/html" });
1061
+ * rv.ok; // → false
1062
+ * rv.action; // → "refuse"
1063
+ */
928
1064
  function gate(opts) {
929
1065
  opts = _resolveOpts(opts);
930
1066
  return gateContract.buildGuardGate(
@@ -960,12 +1096,70 @@ function gate(opts) {
960
1096
  });
961
1097
  }
962
1098
 
1099
+ /**
1100
+ * @primitive b.guardHtml.buildProfile
1101
+ * @signature b.guardHtml.buildProfile(opts)
1102
+ * @since 0.7.6
1103
+ * @related b.guardHtml.compliancePosture, b.guardHtml.gate
1104
+ *
1105
+ * Resolve a named profile against `PROFILES` and return the merged
1106
+ * options bag. Operators introspecting the active limits (without
1107
+ * calling `validate` / `sanitize` / `gate`) call this. Throws
1108
+ * `GuardHtmlError` with code `html.bad-profile` when the name
1109
+ * doesn't appear in the profile catalog.
1110
+ *
1111
+ * @opts
1112
+ * profile: string, // "strict" | "balanced" | "permissive"
1113
+ *
1114
+ * @example
1115
+ * var resolved = b.guardHtml.buildProfile({ profile: "strict" });
1116
+ * resolved.maxBytes; // → 2097152 (2 MiB)
1117
+ * resolved.maxAttrValueBytes; // → 8192 (8 KiB)
1118
+ */
963
1119
  var buildProfile = gateContract.makeProfileBuilder(PROFILES);
964
1120
 
1121
+ /**
1122
+ * @primitive b.guardHtml.compliancePosture
1123
+ * @signature b.guardHtml.compliancePosture(name)
1124
+ * @since 0.7.6
1125
+ * @related b.guardHtml.buildProfile, b.guardHtml.gate
1126
+ *
1127
+ * Return the option overlay for a named compliance posture
1128
+ * (`hipaa` / `pci-dss` / `gdpr` / `soc2`). Operators compose this
1129
+ * over a base profile to harden the default per regulatory regime.
1130
+ * Throws `GuardHtmlError` with code `html.bad-posture` on unknown
1131
+ * names.
1132
+ *
1133
+ * @example
1134
+ * var hipaa = b.guardHtml.compliancePosture("hipaa");
1135
+ * hipaa.bidiPolicy; // → "reject"
1136
+ * hipaa.cssPolicy; // → "reject"
1137
+ * hipaa.mxssHintPolicy; // → "reject"
1138
+ */
965
1139
  function compliancePosture(name) {
966
1140
  return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES, _err, "html");
967
1141
  }
968
1142
 
1143
+ /**
1144
+ * @primitive b.guardHtml.loadRulePack
1145
+ * @signature b.guardHtml.loadRulePack(pack)
1146
+ * @since 0.7.6
1147
+ * @related b.guardHtml.gate, b.guardHtml.buildProfile
1148
+ *
1149
+ * Register an operator-supplied rule pack (a versioned bundle of
1150
+ * extra tag / attribute / scheme overrides) into the guard's
1151
+ * private store. Subsequent `gate` calls referencing the pack by
1152
+ * its `id` overlay these rules on top of the resolved profile.
1153
+ * Validates pack shape and throws `GuardHtmlError` on malformed
1154
+ * input.
1155
+ *
1156
+ * @example
1157
+ * b.guardHtml.loadRulePack({
1158
+ * id: "kb-2026-html",
1159
+ * version: "1.0.0",
1160
+ * extraDangerousTags: ["custom-element"],
1161
+ * });
1162
+ */
969
1163
  var _htmlRulePacks = gateContract.makeRulePackLoader(GuardHtmlError, "html");
970
1164
  var loadRulePack = _htmlRulePacks.load;
971
1165