@blamejs/core 0.14.27 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +2 -2
  3. package/index.js +4 -0
  4. package/lib/ai-content-detect.js +9 -10
  5. package/lib/api-key.js +158 -77
  6. package/lib/atomic-file.js +29 -1
  7. package/lib/audit-chain.js +47 -11
  8. package/lib/audit-sign.js +77 -2
  9. package/lib/audit-tools.js +79 -51
  10. package/lib/audit.js +228 -100
  11. package/lib/backup/index.js +13 -10
  12. package/lib/break-glass.js +202 -144
  13. package/lib/cache.js +174 -105
  14. package/lib/chain-writer.js +38 -16
  15. package/lib/cli.js +19 -14
  16. package/lib/cluster-provider-db.js +130 -104
  17. package/lib/cluster-storage.js +119 -22
  18. package/lib/cluster.js +119 -71
  19. package/lib/compliance.js +22 -0
  20. package/lib/consent.js +82 -29
  21. package/lib/constants.js +16 -11
  22. package/lib/crypto-field.js +387 -91
  23. package/lib/db-declare-row-policy.js +35 -22
  24. package/lib/db-file-lifecycle.js +3 -2
  25. package/lib/db-query.js +517 -256
  26. package/lib/db-schema.js +209 -44
  27. package/lib/db.js +202 -95
  28. package/lib/external-db-migrate.js +229 -139
  29. package/lib/external-db.js +25 -15
  30. package/lib/framework-error.js +11 -0
  31. package/lib/framework-files.js +73 -0
  32. package/lib/framework-schema.js +695 -394
  33. package/lib/gate-contract.js +596 -1
  34. package/lib/guard-agent-registry.js +26 -44
  35. package/lib/guard-all.js +1 -0
  36. package/lib/guard-auth.js +42 -112
  37. package/lib/guard-cidr.js +33 -154
  38. package/lib/guard-csv.js +46 -113
  39. package/lib/guard-domain.js +34 -157
  40. package/lib/guard-dsn.js +27 -43
  41. package/lib/guard-email.js +47 -69
  42. package/lib/guard-envelope.js +19 -32
  43. package/lib/guard-event-bus-payload.js +24 -42
  44. package/lib/guard-event-bus-topic.js +25 -43
  45. package/lib/guard-filename.js +42 -106
  46. package/lib/guard-graphql.js +42 -123
  47. package/lib/guard-html.js +53 -108
  48. package/lib/guard-idempotency-key.js +24 -42
  49. package/lib/guard-image.js +46 -103
  50. package/lib/guard-imap-command.js +18 -32
  51. package/lib/guard-jmap.js +16 -30
  52. package/lib/guard-json.js +38 -108
  53. package/lib/guard-jsonpath.js +38 -171
  54. package/lib/guard-jwt.js +49 -179
  55. package/lib/guard-list-id.js +25 -41
  56. package/lib/guard-list-unsubscribe.js +27 -43
  57. package/lib/guard-mail-compose.js +24 -42
  58. package/lib/guard-mail-move.js +26 -44
  59. package/lib/guard-mail-query.js +28 -46
  60. package/lib/guard-mail-reply.js +24 -42
  61. package/lib/guard-mail-sieve.js +24 -42
  62. package/lib/guard-managesieve-command.js +17 -31
  63. package/lib/guard-markdown.js +37 -104
  64. package/lib/guard-message-id.js +26 -45
  65. package/lib/guard-mime.js +39 -151
  66. package/lib/guard-oauth.js +54 -135
  67. package/lib/guard-pdf.js +45 -101
  68. package/lib/guard-pop3-command.js +21 -31
  69. package/lib/guard-posture-chain.js +24 -42
  70. package/lib/guard-regex.js +33 -107
  71. package/lib/guard-saga-config.js +24 -42
  72. package/lib/guard-shell.js +42 -172
  73. package/lib/guard-smtp-command.js +48 -54
  74. package/lib/guard-snapshot-envelope.js +24 -42
  75. package/lib/guard-sql.js +1491 -0
  76. package/lib/guard-stream-args.js +24 -43
  77. package/lib/guard-svg.js +47 -65
  78. package/lib/guard-template.js +35 -172
  79. package/lib/guard-tenant-id.js +26 -45
  80. package/lib/guard-time.js +32 -154
  81. package/lib/guard-trace-context.js +25 -44
  82. package/lib/guard-uuid.js +32 -153
  83. package/lib/guard-xml.js +38 -113
  84. package/lib/guard-yaml.js +51 -163
  85. package/lib/http-client.js +14 -0
  86. package/lib/inbox.js +120 -107
  87. package/lib/legal-hold.js +107 -50
  88. package/lib/log-stream-cloudwatch.js +47 -31
  89. package/lib/log-stream-otlp.js +32 -18
  90. package/lib/mail-crypto-smime.js +2 -6
  91. package/lib/mail-greylist.js +2 -6
  92. package/lib/mail-helo.js +2 -6
  93. package/lib/mail-journal.js +85 -64
  94. package/lib/mail-rbl.js +2 -6
  95. package/lib/mail-scan.js +2 -6
  96. package/lib/mail-spam-score.js +2 -6
  97. package/lib/mail-store.js +293 -154
  98. package/lib/middleware/fetch-metadata.js +17 -7
  99. package/lib/middleware/idempotency-key.js +54 -38
  100. package/lib/middleware/rate-limit.js +102 -32
  101. package/lib/middleware/security-headers.js +21 -5
  102. package/lib/migrations.js +108 -66
  103. package/lib/network-heartbeat.js +7 -0
  104. package/lib/nonce-store.js +31 -9
  105. package/lib/object-store/azure-blob-bucket-ops.js +9 -4
  106. package/lib/object-store/azure-blob.js +31 -3
  107. package/lib/object-store/sigv4.js +10 -0
  108. package/lib/outbox.js +136 -82
  109. package/lib/pqc-agent.js +44 -0
  110. package/lib/pubsub-cluster.js +42 -20
  111. package/lib/queue-local.js +202 -139
  112. package/lib/queue-redis.js +9 -1
  113. package/lib/queue-sqs.js +6 -0
  114. package/lib/retention.js +82 -39
  115. package/lib/safe-dns.js +29 -45
  116. package/lib/safe-ical.js +18 -33
  117. package/lib/safe-icap.js +27 -43
  118. package/lib/safe-sieve.js +21 -40
  119. package/lib/safe-sql.js +124 -3
  120. package/lib/safe-vcard.js +18 -33
  121. package/lib/scheduler.js +35 -12
  122. package/lib/seeders.js +122 -74
  123. package/lib/session-stores.js +42 -14
  124. package/lib/session.js +116 -72
  125. package/lib/sql.js +3885 -0
  126. package/lib/static.js +45 -7
  127. package/lib/subject.js +89 -49
  128. package/lib/vault/index.js +3 -2
  129. package/lib/vault/passphrase-ops.js +3 -2
  130. package/lib/vault/rotate.js +104 -64
  131. package/lib/vendor-data.js +2 -0
  132. package/lib/websocket.js +16 -0
  133. package/package.json +1 -1
  134. package/sbom.cdx.json +6 -6
@@ -25,6 +25,7 @@
25
25
  */
26
26
 
27
27
  var { defineClass } = require("./framework-error");
28
+ var gateContract = require("./gate-contract");
28
29
 
29
30
  var GuardEventBusTopicError = defineClass("GuardEventBusTopicError", { alwaysPermanent: true });
30
31
 
@@ -36,12 +37,7 @@ var PROFILES = Object.freeze({
36
37
  permissive: { maxBytes: 512, minDots: 1 },
37
38
  });
38
39
 
39
- var COMPLIANCE_POSTURES = Object.freeze({
40
- hipaa: "strict",
41
- "pci-dss": "strict",
42
- gdpr: "strict",
43
- soc2: "strict",
44
- });
40
+ var COMPLIANCE_POSTURES = gateContract.ALL_STRICT_POSTURES;
45
41
 
46
42
  var RESERVED_PREFIXES = Object.freeze(["framework.", "FRAMEWORK."]);
47
43
 
@@ -109,42 +105,28 @@ function validate(name, opts) {
109
105
  return name;
110
106
  }
111
107
 
112
- /**
113
- * @primitive b.guardEventBusTopic.compliancePosture
114
- * @signature b.guardEventBusTopic.compliancePosture(posture)
115
- * @since 0.9.25
116
- * @status stable
117
- *
118
- * Return the effective profile for a given compliance posture name.
119
- * Returns `null` for unknown posture names so operator typos surface
120
- * here instead of silently falling through to the default profile.
121
- *
122
- * @example
123
- * b.guardEventBusTopic.compliancePosture("hipaa"); // → "strict"
124
- */
125
- function compliancePosture(posture) {
126
- return COMPLIANCE_POSTURES[posture] || null;
127
- }
108
+ // compliancePosture is assembled by gateContract.defineParser below; its
109
+ // wiki section renders from the single-sourced @abiTemplate (defineParser)
110
+ // block in gate-contract.js, instantiated for this guard by the page
111
+ // generator.
128
112
 
129
- function _resolveProfile(opts) {
130
- if (opts.posture && COMPLIANCE_POSTURES[opts.posture]) {
131
- return COMPLIANCE_POSTURES[opts.posture];
132
- }
133
- var p = opts.profile || DEFAULT_PROFILE;
134
- if (!PROFILES[p]) {
135
- throw new GuardEventBusTopicError("event-bus-topic/bad-profile",
136
- "guardEventBusTopic: unknown profile '" + p + "'");
137
- }
138
- return p;
139
- }
113
+ var _resolveProfile = gateContract.makeProfileResolver({
114
+ profiles: PROFILES,
115
+ postures: COMPLIANCE_POSTURES,
116
+ defaults: DEFAULT_PROFILE,
117
+ errorClass: GuardEventBusTopicError,
118
+ codePrefix: "event-bus-topic",
119
+ });
140
120
 
141
- module.exports = {
142
- validate: validate,
143
- compliancePosture: compliancePosture,
144
- PROFILES: PROFILES,
145
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
146
- RESERVED_PREFIXES: RESERVED_PREFIXES,
147
- GuardEventBusTopicError: GuardEventBusTopicError,
148
- NAME: "eventBusTopic",
149
- KIND: "event-bus-topic",
150
- };
121
+ module.exports = gateContract.defineParser({
122
+ name: "event-bus-topic",
123
+ entry: validate,
124
+ errorClass: GuardEventBusTopicError,
125
+ profiles: PROFILES,
126
+ postures: COMPLIANCE_POSTURES,
127
+ extra: {
128
+ RESERVED_PREFIXES: RESERVED_PREFIXES,
129
+ NAME: "eventBusTopic",
130
+ KIND: "event-bus-topic",
131
+ },
132
+ });
@@ -871,80 +871,10 @@ function gate(opts) {
871
871
  });
872
872
  }
873
873
 
874
- /**
875
- * @primitive b.guardFilename.buildProfile
876
- * @signature b.guardFilename.buildProfile(opts)
877
- * @since 0.7.5
878
- * @status stable
879
- * @related b.guardFilename.gate, b.guardFilename.compliancePosture
880
- *
881
- * Compose a derived profile from one or more named bases plus inline
882
- * overrides. `opts.extends` is a profile name (`"strict"` /
883
- * `"balanced"` / `"permissive"`) or an array of names; later entries
884
- * shadow earlier ones. Inline `opts` keys win last.
885
- *
886
- * @opts
887
- * extends: string|string[], // base profile name(s) to compose
888
- *
889
- * @example
890
- * var custom = b.guardFilename.buildProfile({
891
- * extends: "balanced",
892
- * extensionAllowlist: [".pdf", ".png", ".jpg"],
893
- * });
894
- * custom.extensionAllowlist.length; // → 3
895
- * custom.traversalPolicy; // → "reject"
896
- */
897
- var buildProfile = gateContract.makeProfileBuilder(PROFILES);
898
-
899
- /**
900
- * @primitive b.guardFilename.compliancePosture
901
- * @signature b.guardFilename.compliancePosture(name)
902
- * @since 0.7.5
903
- * @status stable
904
- * @compliance hipaa, pci-dss, gdpr, soc2
905
- * @related b.guardFilename.gate, b.guardFilename.buildProfile
906
- *
907
- * Look up a compliance-posture overlay by name (`"hipaa"` /
908
- * `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns the posture object —
909
- * the caller may mutate freely. Throws
910
- * `GuardFilenameError("filename.bad-posture")` on unknown name.
911
- *
912
- * @example
913
- * var posture = b.guardFilename.compliancePosture("hipaa");
914
- * posture.requireAscii; // → true
915
- * posture.shellExecExtPolicy; // → "reject"
916
- */
917
- function compliancePosture(name) {
918
- return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES, _err, "filename");
919
- }
920
-
921
- var _filenameRulePacks = gateContract.makeRulePackLoader(GuardFilenameError, "filename");
922
- /**
923
- * @primitive b.guardFilename.loadRulePack
924
- * @signature b.guardFilename.loadRulePack(pack)
925
- * @since 0.7.5
926
- * @status stable
927
- * @related b.guardFilename.gate
928
- *
929
- * Register an operator-supplied rule pack with the guard-filename
930
- * registry. The pack is identified by `pack.id` (non-empty string)
931
- * and stored for later inspection / dispatch by gates that opt in
932
- * via `opts.rulePackId`. Returns the pack object unchanged on
933
- * success; throws `GuardFilenameError("filename.bad-opt")` when
934
- * `pack` is missing or `pack.id` is not a non-empty string.
935
- *
936
- * @example
937
- * var pack = b.guardFilename.loadRulePack({
938
- * id: "tenant-uploads-policy",
939
- * rules: [
940
- * { id: "tenant-prefix", severity: "high",
941
- * detect: function (n) { return n.indexOf("tenant_") !== 0; },
942
- * reason: "tenant policy: filenames must be tenant_-prefixed" },
943
- * ],
944
- * });
945
- * pack.id; // → "tenant-uploads-policy"
946
- */
947
- var loadRulePack = _filenameRulePacks.load;
874
+ // buildProfile / compliancePosture / loadRulePack are assembled by
875
+ // gateContract.defineGuard below; their wiki sections render from the
876
+ // single-sourced @abiTemplate blocks in gate-contract.js, instantiated
877
+ // per guard by the page generator.
948
878
 
949
879
  // ---- verifyExtractionPath -------------------------------------------------
950
880
 
@@ -1212,35 +1142,41 @@ function verifyExtractionPath(entryName, extractionRoot, opts) {
1212
1142
  return stringResolved;
1213
1143
  }
1214
1144
 
1215
- module.exports = {
1216
- // ---- guard-* family identity ----
1217
- // Filename is a different axis from content-bytes (operators
1218
- // typically apply both: guardFilename on the upload's name, plus
1219
- // guardCsv / guardHtml / guardSvg / etc. on the body). guard-filename
1220
- // is therefore a STANDALONE primitive it does NOT register into
1221
- // b.guardAll's content-type-routed dispatch (no canonical mime / ext
1222
- // per the registry contract). Operators wire it directly via
1223
- // b.fileUpload({ filenameSafety: gate }) and similar host opts.
1224
- NAME: "filename",
1225
- KIND: "filename", // filename-string guard (consumes ctx.filename)
1226
- INTEGRATION_FIXTURES: Object.freeze({
1227
- kind: "filename",
1228
- benignFilename: "report-2026-Q1.txt",
1229
- // Hostile: path-traversal in filename (CWE-22 class).
1230
- hostileFilename: "../etc/passwd",
1231
- }),
1232
- // ---- primitive surface ----
1233
- validate: validate,
1234
- sanitize: sanitize,
1235
- gate: gate,
1236
- buildProfile: buildProfile,
1237
- compliancePosture: compliancePosture,
1238
- loadRulePack: loadRulePack,
1239
- PROFILES: PROFILES,
1240
- DEFAULTS: DEFAULTS,
1241
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
1242
- WIN_RESERVED_NAMES: WIN_RESERVED_NAMES,
1243
- SHELL_EXEC_EXTS: SHELL_EXEC_EXTS,
1244
- GuardFilenameError: GuardFilenameError,
1245
- verifyExtractionPath: verifyExtractionPath,
1246
- };
1145
+ // ---- guard-* family identity ----
1146
+ // Filename is a different axis from content-bytes (operators typically
1147
+ // apply both: guardFilename on the upload's name, plus guardCsv /
1148
+ // guardHtml / guardSvg / etc. on the body). guard-filename is therefore
1149
+ // a STANDALONE primitive it does NOT register into b.guardAll's
1150
+ // content-type-routed dispatch (no canonical mime / ext per the registry
1151
+ // contract). Operators wire it directly via b.fileUpload({ filenameSafety:
1152
+ // gate }) and similar host opts.
1153
+ var INTEGRATION_FIXTURES = Object.freeze({
1154
+ kind: "filename",
1155
+ benignFilename: "report-2026-Q1.txt",
1156
+ // Hostile: path-traversal in filename (CWE-22 class).
1157
+ hostileFilename: "../etc/passwd",
1158
+ });
1159
+
1160
+ // Assembled from the gate-contract guard factory. KIND "filename" makes
1161
+ // the default gate read ctx.filename || ctx.name, but this guard passes
1162
+ // its own bespoke `gate` (the per-policy canSanitize matrix), so the
1163
+ // factory only supplies the error class, registry exports, buildProfile /
1164
+ // compliancePosture / loadRulePack wiring, and the verifyExtractionPath /
1165
+ // WIN_RESERVED_NAMES / SHELL_EXEC_EXTS extras.
1166
+ module.exports = gateContract.defineGuard({
1167
+ name: "filename",
1168
+ kind: "filename",
1169
+ errorClass: GuardFilenameError,
1170
+ profiles: PROFILES,
1171
+ defaults: DEFAULTS,
1172
+ postures: COMPLIANCE_POSTURES,
1173
+ integrationFixtures: INTEGRATION_FIXTURES,
1174
+ validate: validate,
1175
+ sanitize: sanitize,
1176
+ gate: gate,
1177
+ extra: {
1178
+ WIN_RESERVED_NAMES: WIN_RESERVED_NAMES,
1179
+ SHELL_EXEC_EXTS: SHELL_EXEC_EXTS,
1180
+ verifyExtractionPath: verifyExtractionPath,
1181
+ },
1182
+ });
@@ -87,8 +87,6 @@ var { GuardGraphqlError } = require("./framework-error");
87
87
  var observability = lazyRequire(function () { return require("./observability"); });
88
88
  void observability;
89
89
 
90
- var _err = GuardGraphqlError.factory;
91
-
92
90
  // Query-body proto-poison literal (CVE-2026-32621). Matches the bare
93
91
  // identifier in field / alias / variable-declaration positions —
94
92
  // `$__proto__: String`, `__proto__: realField`, `__proto__ { ... }`,
@@ -544,12 +542,7 @@ function validate(input, opts) {
544
542
  function sanitize(input, opts) {
545
543
  opts = _resolveOpts(opts);
546
544
  var issues = _detectIssues(input, opts);
547
- for (var i = 0; i < issues.length; i += 1) {
548
- if (issues[i].severity === "critical" || issues[i].severity === "high") {
549
- throw _err(issues[i].ruleId || "graphql.refused",
550
- "guardGraphql.sanitize: " + issues[i].snippet);
551
- }
552
- }
545
+ gateContract.throwOnRefusalSeverity(issues, { errorClass: GuardGraphqlError, codePrefix: "graphql" });
553
546
  return input;
554
547
  }
555
548
 
@@ -612,120 +605,46 @@ function gate(opts) {
612
605
  });
613
606
  }
614
607
 
615
- /**
616
- * @primitive b.guardGraphql.buildProfile
617
- * @signature b.guardGraphql.buildProfile(opts)
618
- * @since 0.7.49
619
- * @status stable
620
- * @related b.guardGraphql.gate, b.guardGraphql.compliancePosture
621
- *
622
- * Compose a derived profile from one or more named bases plus
623
- * inline overrides. `opts.extends` is a profile name (`"strict"` /
624
- * `"balanced"` / `"permissive"`) or an array of names; later
625
- * entries shadow earlier ones, and inline `opts` keys win last.
626
- * Operators stage profile overlays here so the final shape is
627
- * traceable to a baseline rather than a hand-typed dictionary.
628
- *
629
- * @opts
630
- * extends: string|string[], // base profile name(s) to compose
631
- * ...: any guardGraphql key, // inline override of resolved keys
632
- *
633
- * @example
634
- * var custom = b.guardGraphql.buildProfile({
635
- * extends: "balanced",
636
- * introspectionPolicy: "reject",
637
- * maxDepth: 6,
638
- * });
639
- * custom.introspectionPolicy; // → "reject"
640
- * custom.maxDepth; // → 6
641
- */
642
- var buildProfile = gateContract.makeProfileBuilder(PROFILES);
643
-
644
- /**
645
- * @primitive b.guardGraphql.compliancePosture
646
- * @signature b.guardGraphql.compliancePosture(name)
647
- * @since 0.7.49
648
- * @status stable
649
- * @compliance hipaa, pci-dss, gdpr, soc2
650
- * @related b.guardGraphql.gate, b.guardGraphql.buildProfile
651
- *
652
- * Look up a compliance-posture overlay by name (`"hipaa"` /
653
- * `"pci-dss"` / `"gdpr"` / `"soc2"`). Returns a shallow clone of
654
- * the posture object — the caller may mutate freely. Throws
655
- * `GuardGraphqlError("graphql.bad-posture")` on unknown name.
656
- * Postures extend the strict profile (or balanced for `gdpr`)
657
- * with a `forensicSnippetBytes` cap appropriate to the regime.
658
- *
659
- * @example
660
- * var posture = b.guardGraphql.compliancePosture("soc2");
661
- * posture.introspectionPolicy; // → "reject"
662
- * posture.forensicSnippetBytes; // → 1024
663
- */
664
- function compliancePosture(name) {
665
- return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES,
666
- _err, "graphql");
667
- }
608
+ // buildProfile / compliancePosture / loadRulePack are assembled by
609
+ // gateContract.defineGuard below — their wiki sections render from the
610
+ // single-sourced @abiTemplate blocks in gate-contract.js.
668
611
 
669
- var _gqlRulePacks = gateContract.makeRulePackLoader(GuardGraphqlError, "graphql");
670
- /**
671
- * @primitive b.guardGraphql.loadRulePack
672
- * @signature b.guardGraphql.loadRulePack(pack)
673
- * @since 0.7.49
674
- * @status stable
675
- * @related b.guardGraphql.gate
676
- *
677
- * Register an operator-supplied rule pack with the guard-graphql
678
- * registry. The pack is identified by `pack.id` (non-empty
679
- * string) and stored for later inspection / dispatch by gates
680
- * that opt in via `opts.rulePackId`. Returns the pack object
681
- * unchanged on success; throws `GuardGraphqlError("graphql.bad-opt")`
682
- * when `pack` is missing or `pack.id` is not a non-empty string.
683
- *
684
- * @example
685
- * var pack = b.guardGraphql.loadRulePack({
686
- * id: "no-mutation-on-read-replica",
687
- * rules: [
688
- * { id: "no-mutation", severity: "high",
689
- * detect: function (req) { return /^\s*mutation\b/.test(req.query || ""); },
690
- * reason: "read-replica refuses mutation operations" },
691
- * ],
692
- * });
693
- * pack.id; // → "no-mutation-on-read-replica"
694
- */
695
- var loadRulePack = _gqlRulePacks.load;
612
+ // ---- adaptive integration-test fixtures (consumed by layer-5 host harness) ----
613
+ var INTEGRATION_FIXTURES = Object.freeze({
614
+ kind: "graphql-request",
615
+ benignBytes: Buffer.from(JSON.stringify({
616
+ query: "query GetMe { me { id name } }",
617
+ operationName: "GetMe",
618
+ }), "utf8"),
619
+ hostileBytes: Buffer.from(JSON.stringify({
620
+ query: "query Inspect { __schema { types { name } } }",
621
+ operationName: "Inspect",
622
+ }), "utf8"),
623
+ benignGraphqlRequest: {
624
+ query: "query GetMe { me { id name } }",
625
+ operationName: "GetMe",
626
+ },
627
+ hostileGraphqlRequest: {
628
+ query: "query Inspect { __schema { types { name } } }",
629
+ operationName: "Inspect",
630
+ },
631
+ });
696
632
 
697
- module.exports = {
698
- // ---- guard-* family registry exports ----
699
- NAME: "graphql",
700
- KIND: "graphql-request",
701
- INTEGRATION_FIXTURES: Object.freeze({
702
- kind: "graphql-request",
703
- benignBytes: Buffer.from(JSON.stringify({
704
- query: "query GetMe { me { id name } }",
705
- operationName: "GetMe",
706
- }), "utf8"),
707
- hostileBytes: Buffer.from(JSON.stringify({
708
- query: "query Inspect { __schema { types { name } } }",
709
- operationName: "Inspect",
710
- }), "utf8"),
711
- benignGraphqlRequest: {
712
- query: "query GetMe { me { id name } }",
713
- operationName: "GetMe",
714
- },
715
- hostileGraphqlRequest: {
716
- query: "query Inspect { __schema { types { name } } }",
717
- operationName: "Inspect",
718
- },
719
- }),
720
- // ---- primitive surface ----
721
- validate: validate,
722
- sanitize: sanitize,
723
- gate: gate,
724
- buildProfile: buildProfile,
725
- compliancePosture: compliancePosture,
726
- loadRulePack: loadRulePack,
727
- PROFILES: PROFILES,
728
- DEFAULTS: DEFAULTS,
729
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
730
- GuardGraphqlError: GuardGraphqlError,
731
- };
633
+ // Assembled from the gate-contract guard factory: error class, registry
634
+ // exports (NAME / KIND / INTEGRATION_FIXTURES), buildProfile /
635
+ // compliancePosture / loadRulePack wiring, plus the per-guard inspection
636
+ // surface (validate / sanitize / bespoke gate) passed through verbatim.
637
+ // The custom KIND ("graphql-request") is accepted because the bespoke
638
+ // gate reads its own ctx fields (ctx.graphqlRequest / ctx.gql).
639
+ module.exports = gateContract.defineGuard({
640
+ name: "graphql",
641
+ kind: "graphql-request",
642
+ errorClass: GuardGraphqlError,
643
+ profiles: PROFILES,
644
+ defaults: DEFAULTS,
645
+ postures: COMPLIANCE_POSTURES,
646
+ integrationFixtures: INTEGRATION_FIXTURES,
647
+ validate: validate,
648
+ sanitize: sanitize,
649
+ gate: gate,
650
+ });
package/lib/guard-html.js CHANGED
@@ -1096,114 +1096,59 @@ function gate(opts) {
1096
1096
  });
1097
1097
  }
1098
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
- */
1119
- var buildProfile = gateContract.makeProfileBuilder(PROFILES);
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
- */
1139
- function compliancePosture(name) {
1140
- return gateContract.lookupCompliancePosture(name, COMPLIANCE_POSTURES, _err, "html");
1141
- }
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
- */
1163
- var _htmlRulePacks = gateContract.makeRulePackLoader(GuardHtmlError, "html");
1164
- var loadRulePack = _htmlRulePacks.load;
1099
+ // buildProfile / compliancePosture / loadRulePack are assembled by
1100
+ // gateContract.defineGuard below (makeProfileBuilder(PROFILES) /
1101
+ // lookupCompliancePosture(_, COMPLIANCE_POSTURES) / makeRulePackLoader).
1102
+ // Their wiki sections render from the single-sourced @abiTemplate blocks
1103
+ // in gate-contract.js, instantiated per guard by the page generator.
1165
1104
 
1166
1105
  void safeUrl; // reserved for future scheme-allowlist composition
1167
1106
 
1168
- module.exports = {
1169
- // ---- guard-* family registry exports ----
1170
- NAME: "html",
1171
- KIND: "content",
1172
- MIME_TYPES: Object.freeze(["text/html", "application/xhtml+xml"]),
1173
- EXTENSIONS: Object.freeze([".html", ".htm", ".xhtml"]),
1174
- INTEGRATION_FIXTURES: Object.freeze({
1175
- kind: "content",
1176
- contentType: "text/html",
1177
- extension: ".html",
1178
- benignBytes: Buffer.from("<p>hello world</p>", "utf8"),
1179
- // Hostile: <script> tag is in the dangerous-tag denylist; refused
1180
- // unconditionally regardless of profile.
1181
- hostileBytes: Buffer.from('<p>hi</p><script>alert(1)</script>', "utf8"),
1182
- }),
1183
- // ---- primitive surface ----
1184
- validate: validate,
1185
- sanitize: sanitize,
1186
- escapeText: escapeText,
1187
- escapeAttr: escapeAttr,
1188
- gate: gate,
1189
- buildProfile: buildProfile,
1190
- compliancePosture: compliancePosture,
1191
- loadRulePack: loadRulePack,
1192
- PROFILES: PROFILES,
1193
- DEFAULTS: DEFAULTS,
1194
- COMPLIANCE_POSTURES: COMPLIANCE_POSTURES,
1195
- DANGEROUS_TAGS: DANGEROUS_TAGS,
1196
- STRICT_ALLOWED_TAGS: STRICT_ALLOWED_TAGS,
1197
- BALANCED_ALLOWED_TAGS: BALANCED_ALLOWED_TAGS,
1198
- PERMISSIVE_ALLOWED_TAGS: PERMISSIVE_ALLOWED_TAGS,
1199
- DANGEROUS_ATTRS: DANGEROUS_ATTRS,
1200
- URL_ATTRS: URL_ATTRS,
1201
- SAFE_SCHEMES: SAFE_SCHEMES,
1202
- DANGEROUS_SCHEMES: DANGEROUS_SCHEMES,
1203
- CLOBBER_GLOBALS: CLOBBER_GLOBALS,
1204
- CLOBBER_PRONE_TAGS: CLOBBER_PRONE_TAGS,
1205
- // WCAG 2.2 audit-only mode (b.guardHtml.wcag.audit) — accessibility
1206
- // scanner that emits violations without modifying HTML.
1207
- wcag: guardHtmlWcag,
1208
- GuardHtmlError: GuardHtmlError,
1209
- };
1107
+ var INTEGRATION_FIXTURES = Object.freeze({
1108
+ kind: "content",
1109
+ contentType: "text/html",
1110
+ extension: ".html",
1111
+ benignBytes: Buffer.from("<p>hello world</p>", "utf8"),
1112
+ // Hostile: <script> tag is in the dangerous-tag denylist; refused
1113
+ // unconditionally regardless of profile.
1114
+ hostileBytes: Buffer.from('<p>hi</p><script>alert(1)</script>', "utf8"),
1115
+ });
1116
+
1117
+ // Assembled from the gate-contract guard factory: error class, registry
1118
+ // exports (NAME / KIND / MIME_TYPES / EXTENSIONS / INTEGRATION_FIXTURES),
1119
+ // buildProfile / compliancePosture / loadRulePack wiring, plus the
1120
+ // per-guard inspection surface (validate / sanitize) and HTML extras
1121
+ // (escapeText / escapeAttr / wcag + the tag/attr/scheme/clobber tables)
1122
+ // passed through verbatim. The bespoke `gate` carries HTML's
1123
+ // sanitize-and-reemit chain unchanged.
1124
+ module.exports = gateContract.defineGuard({
1125
+ name: "html",
1126
+ kind: "content",
1127
+ errorClass: GuardHtmlError,
1128
+ profiles: PROFILES,
1129
+ defaults: DEFAULTS,
1130
+ postures: COMPLIANCE_POSTURES,
1131
+ mimeTypes: ["text/html", "application/xhtml+xml"],
1132
+ extensions: [".html", ".htm", ".xhtml"],
1133
+ integrationFixtures: INTEGRATION_FIXTURES,
1134
+ validate: validate,
1135
+ sanitize: sanitize,
1136
+ gate: gate,
1137
+ extra: {
1138
+ escapeText: escapeText,
1139
+ escapeAttr: escapeAttr,
1140
+ DANGEROUS_TAGS: DANGEROUS_TAGS,
1141
+ STRICT_ALLOWED_TAGS: STRICT_ALLOWED_TAGS,
1142
+ BALANCED_ALLOWED_TAGS: BALANCED_ALLOWED_TAGS,
1143
+ PERMISSIVE_ALLOWED_TAGS: PERMISSIVE_ALLOWED_TAGS,
1144
+ DANGEROUS_ATTRS: DANGEROUS_ATTRS,
1145
+ URL_ATTRS: URL_ATTRS,
1146
+ SAFE_SCHEMES: SAFE_SCHEMES,
1147
+ DANGEROUS_SCHEMES: DANGEROUS_SCHEMES,
1148
+ CLOBBER_GLOBALS: CLOBBER_GLOBALS,
1149
+ CLOBBER_PRONE_TAGS: CLOBBER_PRONE_TAGS,
1150
+ // WCAG 2.2 audit-only mode (b.guardHtml.wcag.audit) — accessibility
1151
+ // scanner that emits violations without modifying HTML.
1152
+ wcag: guardHtmlWcag,
1153
+ },
1154
+ });