@blamejs/core 0.18.47 → 0.18.48

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.
@@ -161,6 +161,21 @@ var DEFAULTS = gateContract.strictDefaults(PROFILES);
161
161
  // maxRuntimeMs among them, where zero means no runtime budget.
162
162
  var INT_OPTS = ["maxBytes", "maxWidth", "maxHeight", "maxFrames"];
163
163
 
164
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
165
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
166
+ // strict one: `polyglotPolicy: "rejct"` is not "allow", so the check runs, and
167
+ // it is not "reject" either, so the finding drops to a warning.
168
+ //
169
+ // Declared up here because `gate()` below binds its own resolver and needs the
170
+ // same table. Given to the generated path alone, a bad value survived gate
171
+ // construction and surfaced as a refusal on the first image request — a deploy
172
+ // that boots clean and then refuses the route, which is the opposite of what
173
+ // checking at construction is for.
174
+ var POLICY_ENUM = gateContract.policyVocabulary([
175
+ "magicPolicy", "mismatchPolicy", "polyglotPolicy", "svgRoutingPolicy",
176
+ "dimensionsPolicy", "framesPolicy", "unknownMagicPolicy",
177
+ ], gateContract.POLICY_VALUES.rejectAuditAllow);
178
+
164
179
  var COMPLIANCE_POSTURES = gateContract.compliancePostures(PROFILES, { base: 256 });
165
180
 
166
181
  function _bytesAt(buf, offset, sig) {
@@ -676,6 +691,9 @@ function gate(opts) {
676
691
  // caller to identical rules.
677
692
  intOpts: INT_OPTS,
678
693
  nonNegativeOpts: gateContract.capKeysOf(DEFAULTS),
694
+ // And the same policy vocabulary, for the same reason as the limits: a
695
+ // door that skips it accepts a value the other doors refuse.
696
+ enumOpts: POLICY_ENUM,
679
697
  });
680
698
  return gateContract.buildGuardGate(
681
699
  opts.name || "guardImage:" + (opts.profile || "default"),
@@ -745,6 +763,7 @@ var INTEGRATION_FIXTURES = Object.freeze({
745
763
  // (operator-feeds-metadata ctx.metadata reader) is REQUIRED and carries the
746
764
  // magic-byte / polyglot / dimension chain unchanged.
747
765
  module.exports = gateContract.defineGuard({
766
+ enumOpts: POLICY_ENUM,
748
767
  name: "image",
749
768
  kind: "metadata",
750
769
  errorClass: GuardImageError,
package/lib/guard-json.js CHANGED
@@ -1083,7 +1083,30 @@ var INTEGRATION_FIXTURES = Object.freeze({
1083
1083
  // per-guard inspection surface (validate / parse) and JSON extras
1084
1084
  // (POLLUTION_KEYS, surfaced from the framework's canonical pick.POISONED_KEYS).
1085
1085
  // The bespoke `gate` carries JSON's sanitize-reparse-reserialize chain unchanged.
1086
+ // Each policy's vocabulary, taken from this guard's documented opts and
1087
+ // confirmed against what the code compares, so a misspelling is a boot error
1088
+ // rather than a runtime surprise. Read leniently, a typo takes whichever branch
1089
+ // is not the strict one: `duplicateKeyPolicy: "rejct"` is not "allow", so the
1090
+ // check runs, and it is not "reject" either, so the finding drops to a warning
1091
+ // — the operator asked to refuse a duplicate key and silently got an audit.
1092
+ //
1093
+ // The character policies are deliberately absent: they are derived for the
1094
+ // whole family, and an explicit entry here would override that derivation with
1095
+ // a narrower copy.
1096
+ var POLICY_ENUM = gateContract.policyVocabulary([
1097
+ "duplicateKeyPolicy", "nanInfinityPolicy", "commentPolicy",
1098
+ "trailingCommaPolicy", "json5SyntaxPolicy", "numericPrecisionPolicy",
1099
+ ], gateContract.POLICY_VALUES.rejectAuditAllow, {
1100
+ // No `audit-only`: the parse path decides whether to preserve a poison-named
1101
+ // key by testing for the literal "audit", so the synonym would strip
1102
+ // `__proto__` where "audit" keeps it — a silent difference in the parsed
1103
+ // data, not in a finding.
1104
+ pollutionPolicy: ["reject", "strip", "audit", "allow"],
1105
+ bomPolicy: ["reject", "strip", "allow"],
1106
+ });
1107
+
1086
1108
  module.exports = gateContract.defineGuard({
1109
+ enumOpts: POLICY_ENUM,
1087
1110
  name: "json",
1088
1111
  kind: "content",
1089
1112
  charRepair: true,
@@ -257,7 +257,7 @@ function _detectIssues(input, opts) {
257
257
  * bidiPolicy: "reject"|"audit"|"allow",
258
258
  * controlPolicy: "reject"|"audit"|"allow",
259
259
  * nullBytePolicy: "reject"|"audit"|"allow",
260
- * zeroWidthPolicy: "reject"|"strip"|"audit"|"allow",
260
+ * zeroWidthPolicy: "reject"|"audit"|"allow",
261
261
  * filterExprPolicy: "reject"|"audit"|"allow",
262
262
  * scriptExprPolicy: "reject"|"audit"|"allow",
263
263
  * dynamicHintPolicy: "reject"|"audit"|"allow",
@@ -350,7 +350,20 @@ var INTEGRATION_FIXTURES = gateContract.identifierFixtures("$.users[*].name", "$
350
350
  // surface (validate / sanitize). The gate is the factory default
351
351
  // serve/audit-only/refuse chain, reading the path from
352
352
  // `ctx.identifier` || `ctx.jsonpath` via `ctxFields`.
353
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
354
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
355
+ // strict one: `scriptExprPolicy: "rejct"` is not "allow", so the check runs,
356
+ // and it is not "reject" either, so the finding drops to a warning.
357
+ //
358
+ // The character policies are absent on purpose — they are derived for the
359
+ // whole guard family, and an entry here would shadow that derivation.
360
+ var POLICY_ENUM = gateContract.policyVocabulary([
361
+ "filterExprPolicy", "scriptExprPolicy", "dynamicHintPolicy",
362
+ "bracketNestingPolicy", "recursiveDescentPolicy",
363
+ ], gateContract.POLICY_VALUES.rejectAuditAllow);
364
+
353
365
  module.exports = gateContract.defineGuard({
366
+ enumOpts: POLICY_ENUM,
354
367
  name: "jsonpath",
355
368
  kind: "identifier",
356
369
  errorClass: GuardJsonpathError,
package/lib/guard-jwt.js CHANGED
@@ -283,8 +283,10 @@ function _detectIssues(input, opts) {
283
283
  }
284
284
  }
285
285
 
286
- // kid path-traversal.
287
- if (opts.kidTraversalPolicy !== "allow" && _hasKidTraversal(header.kid)) {
286
+ // kid path-traversal. Unconditional: `kid` steers key lookup, so a traversal
287
+ // sequence in it is critical at every profile and there is no policy value
288
+ // that turns the check off.
289
+ if (_hasKidTraversal(header.kid)) {
288
290
  issues.push({
289
291
  kind: "kid-traversal", severity: "critical",
290
292
  ruleId: "jwt.kid-traversal",
@@ -578,7 +580,23 @@ var INTEGRATION_FIXTURES = gateContract.identifierFixtures(
578
580
  // default gate (no bespoke `gate` passed) and the factory supplies the
579
581
  // error class, registry exports, buildProfile / compliancePosture /
580
582
  // loadRulePack wiring, and the kidSafe extra.
583
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
584
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
585
+ // strict one: `typConfusionPolicy: "rejct"` is not "allow", so the check runs,
586
+ // and it is not "reject" either, so the finding drops from high to warn.
587
+ //
588
+ // `algNonePolicy` and `kidTraversalPolicy` take one value each because both
589
+ // threats are unconditional, as their opts block has always said.
590
+ var POLICY_ENUM = gateContract.policyVocabulary([
591
+ "algAllowlistPolicy", "typConfusionPolicy", "expSanityPolicy",
592
+ "nbfSanityPolicy", "iatSanityPolicy", "critUnknownPolicy",
593
+ ], gateContract.POLICY_VALUES.rejectAuditAllow, {
594
+ algNonePolicy: ["reject"],
595
+ kidTraversalPolicy: ["reject"],
596
+ });
597
+
581
598
  module.exports = gateContract.defineGuard({
599
+ enumOpts: POLICY_ENUM,
582
600
  name: "jwt",
583
601
  kind: "identifier",
584
602
  errorClass: GuardJwtError,
@@ -1902,7 +1902,32 @@ function render(source, opts) {
1902
1902
  }
1903
1903
  }
1904
1904
 
1905
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
1906
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
1907
+ // strict one: `dangerousSchemePolicy: "rejct"` is not "allow", so the check
1908
+ // runs, and it is not "reject" either, so a `javascript:` link drops to a
1909
+ // warning.
1910
+ //
1911
+ // "strip" is listed only where the opts block offers it — that is, where the
1912
+ // renderer can remove the construct and still produce a document. Advertising
1913
+ // it more widely would route a finding to a repair that never happens, which
1914
+ // is the shape that lets an unrepaired document through as sanitized.
1915
+ //
1916
+ // The character policies are absent on purpose — they are derived for the
1917
+ // whole guard family, and an entry here would shadow that derivation.
1918
+ var REPAIRABLE = ["dangerousTagPolicy", "dangerousSchemePolicy",
1919
+ "autolinkSchemePolicy", "imageSchemePolicy",
1920
+ "referenceLinkPolicy", "doctypePolicy", "codeFenceLangPolicy"];
1921
+ var REPORT_ONLY = ["rawHtmlPolicy", "htmlCommentPolicy", "frontMatterPolicy",
1922
+ "emphasisRunPolicy", "filePolicy"];
1923
+
1924
+ var POLICY_ENUM = gateContract.policyVocabulary(
1925
+ REPAIRABLE, gateContract.POLICY_VALUES.rejectStripAuditAllow,
1926
+ gateContract.policyVocabulary(
1927
+ REPORT_ONLY, gateContract.POLICY_VALUES.rejectAuditAllow));
1928
+
1905
1929
  module.exports = gateContract.defineGuard({
1930
+ enumOpts: POLICY_ENUM,
1906
1931
  name: "markdown",
1907
1932
  kind: "content",
1908
1933
  charRepair: true,
package/lib/guard-mime.js CHANGED
@@ -328,15 +328,15 @@ function _detectIssues(input, opts) {
328
328
  * @opts
329
329
  * profile: "strict"|"balanced"|"permissive",
330
330
  * compliancePosture: "hipaa"|"pci-dss"|"gdpr"|"soc2",
331
- * bidiPolicy: "reject"|"strip"|"audit"|"allow",
332
- * controlPolicy: "reject"|"strip"|"allow",
333
- * nullBytePolicy: "reject"|"strip"|"allow",
334
- * zeroWidthPolicy: "reject"|"strip"|"allow",
331
+ * bidiPolicy: "reject"|"audit"|"allow",
332
+ * controlPolicy: "reject"|"audit"|"allow",
333
+ * nullBytePolicy: "reject"|"audit"|"allow",
334
+ * zeroWidthPolicy: "reject"|"audit"|"allow",
335
335
  * wildcardPolicy: "reject"|"audit"|"allow",
336
336
  * vendorTreePolicy: "reject"|"audit"|"allow",
337
337
  * personalTreePolicy: "reject"|"audit"|"allow",
338
338
  * unregisteredTreePolicy: "reject"|"audit"|"allow",
339
- * riskyTypesPolicy: "reject"|"audit"|"allow",
339
+ * riskyTypePolicy: "reject"|"audit"|"allow",
340
340
  * parameterPolicy: "reject"|"audit"|"allow",
341
341
  * maxBytes: number, // default 256 (RFC-recommended cap)
342
342
  *
@@ -418,7 +418,17 @@ var INTEGRATION_FIXTURES = gateContract.identifierFixtures("application/json", "
418
418
  // compliancePosture / loadRulePack wiring, plus the per-guard inspection
419
419
  // surface (validate / sanitize). The gate is the factory default chain,
420
420
  // dispatched to `ctx.identifier` / `ctx.mime` via ctxFields.
421
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
422
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
423
+ // strict one: `wildcardPolicy: "rejct"` is not "allow", so the check runs, and
424
+ // it is not "reject" either, so the finding drops from high to warn.
425
+ var POLICY_ENUM = gateContract.policyVocabulary([
426
+ "wildcardPolicy", "vendorTreePolicy", "personalTreePolicy",
427
+ "unregisteredTreePolicy", "riskyTypePolicy", "parameterPolicy",
428
+ ], gateContract.POLICY_VALUES.rejectAuditAllow);
429
+
421
430
  module.exports = gateContract.defineGuard({
431
+ enumOpts: POLICY_ENUM,
422
432
  name: "mime",
423
433
  kind: "identifier",
424
434
  errorClass: GuardMimeError,
@@ -574,7 +574,30 @@ var INTEGRATION_FIXTURES = Object.freeze({
574
574
  // surface (validate / sanitize / bespoke gate) passed through verbatim.
575
575
  // The custom KIND ("oauth-flow") is accepted because the bespoke gate
576
576
  // reads its own ctx fields (ctx.oauthFlow / ctx.flow).
577
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
578
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
579
+ // strict one: `statePolicy: "requre"` is not "allow", so the check runs, and
580
+ // it is not "require" either, so a missing state parameter drops to a warning.
581
+ //
582
+ // Several of these open with "require" rather than "reject" because the
583
+ // finding is an absence — no PKCE challenge, no state, no issuer on the
584
+ // callback — so the operator is demanding a parameter, not refusing one.
585
+ var POLICY_ENUM = gateContract.policyVocabulary(
586
+ ["scopeTamperingPolicy"], gateContract.POLICY_VALUES.rejectAuditAllow, {
587
+ // No `audit-only`: the PKCE check tests for the literal "audit", so the
588
+ // synonym would take the enforcing branch and refuse a flow that plain
589
+ // "audit" serves with a finding. Refusing the value is the honest answer
590
+ // — the operator picks a spelling that means what they want.
591
+ pkcePolicy: ["require-s256", "require-any", "audit", "allow"],
592
+ statePolicy: ["require", "audit", "audit-only", "allow"],
593
+ redirectUriPolicy: ["require-exact-allowlist", "audit", "audit-only", "allow"],
594
+ responseTypePolicy: ["require-allowlist", "audit", "audit-only", "allow"],
595
+ issuerOnCallbackPolicy: ["require", "audit", "audit-only", "allow"],
596
+ codeReusePolicy: ["reject", "allow"],
597
+ });
598
+
577
599
  var _guard = module.exports = gateContract.defineGuard({
600
+ enumOpts: POLICY_ENUM,
578
601
  name: "oauth",
579
602
  kind: "oauth-flow",
580
603
  errorClass: GuardOauthError,
package/lib/guard-pdf.js CHANGED
@@ -141,6 +141,20 @@ var DEFAULTS = gateContract.strictDefaults(PROFILES);
141
141
  // generated validate() does. See guard-image for the full reasoning.
142
142
  var INT_OPTS = ["maxBytes", "maxPageCount"];
143
143
 
144
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
145
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
146
+ // strict one: `javascriptPolicy: "rejct"` is not "allow", so the check runs,
147
+ // and it is not "reject" either, so an embedded script drops to a warning.
148
+ //
149
+ // Declared up here because `gate()` below binds its own resolver and needs the
150
+ // same table. Given to the generated path alone, a bad value survived gate
151
+ // construction and surfaced as a refusal on the first PDF request.
152
+ var POLICY_ENUM = gateContract.policyVocabulary([
153
+ "magicPolicy", "javascriptPolicy", "launchActionPolicy", "openActionPolicy",
154
+ "embeddedFilePolicy", "embeddedFileCountPolicy", "encryptedPolicy",
155
+ "polyglotPolicy", "pageCountPolicy",
156
+ ], gateContract.POLICY_VALUES.rejectAuditAllow);
157
+
144
158
  var COMPLIANCE_POSTURES = gateContract.compliancePostures(PROFILES, { base: 512 });
145
159
 
146
160
  function _hasPdfMagic(buf) {
@@ -441,9 +455,12 @@ function gate(opts) {
441
455
  defaults: DEFAULTS,
442
456
  errorClass: GuardPdfError,
443
457
  errCodePrefix: "pdf",
444
- // Own resolver, own cap declaration — see guard-image.gate.
458
+ // Own resolver, own cap declaration — see guard-image.gate. The policy
459
+ // vocabulary comes with it, for the same reason: a door that skips it
460
+ // accepts a value the other doors refuse.
445
461
  intOpts: INT_OPTS,
446
462
  nonNegativeOpts: gateContract.capKeysOf(DEFAULTS),
463
+ enumOpts: POLICY_ENUM,
447
464
  });
448
465
  return gateContract.buildGuardGate(
449
466
  opts.name || "guardPdf:" + (opts.profile || "default"),
@@ -512,6 +529,7 @@ var INTEGRATION_FIXTURES = Object.freeze({
512
529
  // (operator-feeds-metadata ctx.metadata reader) is REQUIRED and carries the
513
530
  // JavaScript / launch-action / embedded-file chain unchanged.
514
531
  module.exports = gateContract.defineGuard({
532
+ enumOpts: POLICY_ENUM,
515
533
  name: "pdf",
516
534
  kind: "metadata",
517
535
  errorClass: GuardPdfError,
@@ -2586,7 +2586,7 @@ function _detectNestedExtglob(input, opts, issues) {
2586
2586
  * bidiPolicy: "reject"|"audit"|"allow",
2587
2587
  * controlPolicy: "reject"|"audit"|"allow",
2588
2588
  * nullBytePolicy: "reject"|"audit"|"allow",
2589
- * zeroWidthPolicy: "reject"|"strip"|"audit"|"allow",
2589
+ * zeroWidthPolicy: "reject"|"audit"|"allow",
2590
2590
  * nestedQuantPolicy: "reject"|"audit"|"allow",
2591
2591
  * alternationQuantPolicy: "reject"|"audit"|"allow",
2592
2592
  * boundedRepeatPolicy: "reject"|"audit"|"allow",
@@ -2840,7 +2840,22 @@ function assertSafe(input, label, ErrorClass, code, opts) {
2840
2840
  // compliancePosture / loadRulePack wiring, plus the per-guard inspection
2841
2841
  // surface (validate / sanitize / gate). The bespoke `gate` carries
2842
2842
  // guardRegex's ctx.identifier || ctx.pattern dispatch unchanged.
2843
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
2844
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
2845
+ // strict one: `nestedQuantPolicy: "rejct"` is not "allow", so the check runs,
2846
+ // and it is not "reject" either, so a catastrophic-backtracking pattern drops
2847
+ // from high to warn.
2848
+ //
2849
+ // The character policies are absent on purpose — they are derived for the
2850
+ // whole guard family, and an entry here would shadow that derivation.
2851
+ var POLICY_ENUM = gateContract.policyVocabulary([
2852
+ "nestedQuantPolicy", "alternationQuantPolicy", "boundedRepeatPolicy",
2853
+ "lookaroundQuantPolicy", "consecutiveStarPolicy", "nestedExtglobPolicy",
2854
+ "unanchoredScanPolicy",
2855
+ ], gateContract.POLICY_VALUES.rejectAuditAllow);
2856
+
2843
2857
  var _guard = module.exports = gateContract.defineGuard({
2858
+ enumOpts: POLICY_ENUM,
2844
2859
  name: "regex",
2845
2860
  kind: "identifier",
2846
2861
  errorClass: GuardRegexError,
@@ -241,7 +241,7 @@ function _detectIssues(input, opts) {
241
241
  * bidiPolicy: "reject"|"audit"|"allow",
242
242
  * controlPolicy: "reject"|"audit"|"allow",
243
243
  * nullBytePolicy: "reject"|"audit"|"allow",
244
- * zeroWidthPolicy: "reject"|"strip"|"audit"|"allow",
244
+ * zeroWidthPolicy: "reject"|"audit"|"allow",
245
245
  * posixMetaPolicy: "reject"|"audit"|"allow",
246
246
  * cmdMetaPolicy: "reject"|"audit"|"allow",
247
247
  * dollarSubstPolicy: "reject"|"audit"|"allow",
@@ -338,7 +338,20 @@ var INTEGRATION_FIXTURES = gateContract.identifierFixtures("safe-arg-value", "sa
338
338
  // compliancePosture / loadRulePack wiring, plus the per-guard inspection
339
339
  // surface (validate / sanitize). The gate is the factory default chain,
340
340
  // dispatched to `ctx.identifier` / `ctx.arg` via ctxFields.
341
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
342
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
343
+ // strict one: `backtickPolicy: "rejct"` is not "allow", so the check runs, and
344
+ // it is not "reject" either, so command substitution drops to a warning.
345
+ //
346
+ // The character policies are absent on purpose — they are derived for the
347
+ // whole guard family, and an entry here would shadow that derivation.
348
+ var POLICY_ENUM = gateContract.policyVocabulary([
349
+ "posixMetaPolicy", "cmdMetaPolicy", "dollarSubstPolicy", "processSubstPolicy",
350
+ "backtickPolicy", "newlinePolicy", "argHyphenPolicy",
351
+ ], gateContract.POLICY_VALUES.rejectAuditAllow);
352
+
341
353
  module.exports = gateContract.defineGuard({
354
+ enumOpts: POLICY_ENUM,
342
355
  name: "shell",
343
356
  kind: "identifier",
344
357
  errorClass: GuardShellError,
package/lib/guard-svg.js CHANGED
@@ -1005,7 +1005,24 @@ var INTEGRATION_FIXTURES = Object.freeze({
1005
1005
  // unchanged; neither reduces to the dynamic
1006
1006
  // detect→throwOnRefusalSeverity→transform path (same-severity findings
1007
1007
  // split throw-vs-strip), so both stay bespoke.
1008
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
1009
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
1010
+ // strict one: `cdataPolicy: "rejct"` is not "allow", so the check runs, and it
1011
+ // is not "reject" either, so the finding drops from critical to warn.
1012
+ //
1013
+ // `svgzPolicy` takes one value: a gzip signature is refused before any parse
1014
+ // and sanitize throws on it, so no other value has ever meant anything.
1015
+ //
1016
+ // The character policies are absent on purpose — they are derived for the whole
1017
+ // guard family, and an entry here would shadow that derivation.
1018
+ var POLICY_ENUM = gateContract.policyVocabulary([
1019
+ "cssPolicy", "doctypePolicy", "cdataPolicy", "processingInstrPolicy",
1020
+ ], gateContract.POLICY_VALUES.rejectStripAuditAllow, {
1021
+ svgzPolicy: ["reject"],
1022
+ });
1023
+
1008
1024
  var _guard = module.exports = gateContract.defineGuard({
1025
+ enumOpts: POLICY_ENUM,
1009
1026
  name: "svg",
1010
1027
  kind: "content",
1011
1028
  charRepair: true,
@@ -228,7 +228,7 @@ function _detectIssues(input, opts) {
228
228
  * bidiPolicy: "reject"|"audit"|"allow",
229
229
  * controlPolicy: "reject"|"audit"|"allow",
230
230
  * nullBytePolicy: "reject"|"audit"|"allow",
231
- * zeroWidthPolicy: "reject"|"strip"|"audit"|"allow",
231
+ * zeroWidthPolicy: "reject"|"audit"|"allow",
232
232
  * jinjaPolicy: "reject"|"audit"|"allow",
233
233
  * erbPolicy: "reject"|"audit"|"allow",
234
234
  * pugPolicy: "reject"|"audit"|"allow",
@@ -316,7 +316,20 @@ var INTEGRATION_FIXTURES = gateContract.identifierFixtures("Hello world", "Hello
316
316
  // factory default serve -> audit-only -> refuse chain; ctxFields names the
317
317
  // ctx fields it reads (ctx.identifier, then ctx.text) so untrusted strings on
318
318
  // either field reach the SSTI validator before any engine renders them.
319
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
320
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
321
+ // strict one: `jinjaPolicy: "rejct"` is not "allow", so the check runs, and it
322
+ // is not "reject" either, so the finding drops to a warning.
323
+ //
324
+ // The character policies are absent on purpose — they are derived for the
325
+ // whole guard family, and an entry here would shadow that derivation.
326
+ var POLICY_ENUM = gateContract.policyVocabulary([
327
+ "jinjaPolicy", "erbPolicy", "pugPolicy", "dollarBracePolicy",
328
+ "velocityDirectivePolicy",
329
+ ], gateContract.POLICY_VALUES.rejectAuditAllow);
330
+
319
331
  module.exports = gateContract.defineGuard({
332
+ enumOpts: POLICY_ENUM,
320
333
  name: "template",
321
334
  kind: "identifier",
322
335
  errorClass: GuardTemplateError,
package/lib/guard-text.js CHANGED
@@ -118,6 +118,20 @@ var COMPLIANCE_POSTURES = gateContract.compliancePostures(PROFILES, { base: 256
118
118
  // so this guard's hand-bound resolver holds a caller to the same rules the
119
119
  // generated validate() does. maxRuntimeMs is deliberately absent: zero there
120
120
  // means no runtime budget. See guard-image for the full reasoning.
121
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
122
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
123
+ // strict one: `encodingPolicy: "rejct"` is not "allow", so the check runs, and
124
+ // it is not "reject" either, so malformed UTF-8 drops to a warning.
125
+ var POLICY_ENUM = gateContract.policyVocabulary(
126
+ ["confusablePolicy", "encodingPolicy"],
127
+ gateContract.POLICY_VALUES.rejectAuditAllow);
128
+
129
+ // The same table for the entry points that bind their own resolver, with the
130
+ // family's character policies folded in — defineGuard adds those itself, a
131
+ // hand-bound resolver has to be given them.
132
+ var RESOLVER_ENUMS = Object.freeze(Object.assign({},
133
+ gateContract.charPolicyEnums(DEFAULTS, { canRepair: true }), POLICY_ENUM));
134
+
121
135
  var INT_OPTS = ["maxBytes"];
122
136
 
123
137
  // ---- Internal helpers ----
@@ -135,7 +149,7 @@ function _resolveOpts(opts) {
135
149
  // generated path refuses it.
136
150
  intOpts: INT_OPTS,
137
151
  nonNegativeOpts: gateContract.capKeysOf(DEFAULTS),
138
- enumOpts: gateContract.charPolicyEnums(DEFAULTS, { canRepair: true }),
152
+ enumOpts: RESOLVER_ENUMS,
139
153
  });
140
154
  }
141
155
 
@@ -575,6 +589,7 @@ var INTEGRATION_FIXTURES = Object.freeze({
575
589
  // inspection surface (validate / sanitize / gate). The bespoke `gate` carries
576
590
  // the text sanitize-then-refuse chain (confusables are non-repairable).
577
591
  module.exports = gateContract.defineGuard({
592
+ enumOpts: POLICY_ENUM,
578
593
  name: "text",
579
594
  kind: "content",
580
595
  charRepair: true,
package/lib/guard-time.js CHANGED
@@ -299,10 +299,10 @@ function _detectIssues(input, opts) {
299
299
  * @opts
300
300
  * profile: "strict"|"balanced"|"permissive",
301
301
  * compliancePosture: "hipaa"|"pci-dss"|"gdpr"|"soc2",
302
- * bidiPolicy: "reject"|"strip"|"audit"|"allow",
303
- * controlPolicy: "reject"|"strip"|"allow",
304
- * nullBytePolicy: "reject"|"strip"|"allow",
305
- * zeroWidthPolicy: "reject"|"strip"|"allow",
302
+ * bidiPolicy: "reject"|"audit"|"allow",
303
+ * controlPolicy: "reject"|"audit"|"allow",
304
+ * nullBytePolicy: "reject"|"audit"|"allow",
305
+ * zeroWidthPolicy: "reject"|"audit"|"allow",
306
306
  * naiveDatetimePolicy: "reject"|"audit"|"allow",
307
307
  * nonUtcOffsetPolicy: "reject"|"audit"|"allow",
308
308
  * leapSecondPolicy: "reject"|"audit"|"allow",
@@ -393,7 +393,23 @@ var INTEGRATION_FIXTURES = gateContract.identifierFixtures("2026-05-05T12:34:56Z
393
393
  // surface (validate / sanitize). The gate is the factory default — the
394
394
  // standard serve -> audit-only -> refuse chain — reading
395
395
  // ctx.identifier / ctx.timestamp / ctx.time via ctxFields.
396
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
397
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
398
+ // strict one: `naiveDatetimePolicy: "rejct"` is not "allow", so the check
399
+ // runs, and it is not "reject" either, so a zone-less timestamp drops to a
400
+ // warning.
401
+ //
402
+ // `fractionalDigitsPolicy` carries "truncate" because over-precise fractional
403
+ // seconds are the one finding here that can be repaired rather than reported.
404
+ var POLICY_ENUM = gateContract.policyVocabulary([
405
+ "naiveDatetimePolicy", "nonUtcOffsetPolicy", "leapSecondPolicy",
406
+ "dateOnlyPolicy", "timeOnlyPolicy",
407
+ ], gateContract.POLICY_VALUES.rejectAuditAllow, {
408
+ fractionalDigitsPolicy: ["reject", "truncate", "audit", "audit-only", "allow"],
409
+ });
410
+
396
411
  module.exports = gateContract.defineGuard({
412
+ enumOpts: POLICY_ENUM,
397
413
  name: "time",
398
414
  kind: "identifier",
399
415
  errorClass: GuardTimeError,
package/lib/guard-uuid.js CHANGED
@@ -286,10 +286,10 @@ function _detectIssues(input, opts) {
286
286
  * @opts
287
287
  * profile: "strict"|"balanced"|"permissive",
288
288
  * compliancePosture: "hipaa"|"pci-dss"|"gdpr"|"soc2",
289
- * bidiPolicy: "reject"|"strip"|"audit"|"allow",
290
- * controlPolicy: "reject"|"strip"|"allow",
291
- * nullBytePolicy: "reject"|"strip"|"allow",
292
- * zeroWidthPolicy: "reject"|"strip"|"allow",
289
+ * bidiPolicy: "reject"|"audit"|"allow",
290
+ * controlPolicy: "reject"|"audit"|"allow",
291
+ * nullBytePolicy: "reject"|"audit"|"allow",
292
+ * zeroWidthPolicy: "reject"|"audit"|"allow",
293
293
  * formatPolicy: "hyphenated"|"hyphenless"|"braced"|"urn"|"hyphenated-only"|"any",
294
294
  * versionPolicy: "reject-unassigned"|"audit"|"allow",
295
295
  * variantPolicy: "reject-non-rfc"|"audit"|"allow",
@@ -371,10 +371,34 @@ var INTEGRATION_FIXTURES = gateContract.identifierFixtures("550e8400-e29b-41d4-a
371
371
  // surface (validate / sanitize). The gate is the factory default — the
372
372
  // standard serve -> audit-only -> refuse chain — reading ctx.identifier ||
373
373
  // ctx.uuid via ctxFields.
374
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
375
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
376
+ // strict one: `versionPolicy: "reject-unassinged"` is not "allow", so the check
377
+ // runs, and it is not the reject spelling either, so the finding drops to a
378
+ // warning — the operator asked to refuse an unassigned version and silently got
379
+ // an audit. The values are the ones written beside each profile entry above,
380
+ // confirmed against what the code compares.
381
+ //
382
+ // `audit-only` rides with `audit` because it behaves identically here and the
383
+ // framework treats them as synonyms everywhere else; refusing it here while
384
+ // accepting it elsewhere would be a fresh disagreement between two spellings of
385
+ // one setting.
386
+ var POLICY_ENUM = gateContract.policyVocabulary([
387
+ "nilPolicy", "maxPolicy", "urnPolicy", "bracedPolicy",
388
+ ], gateContract.POLICY_VALUES.rejectAuditAllow, {
389
+ // These three name their own values because their findings are not "this
390
+ // construct appeared" but "the identifier is the wrong shape", and the
391
+ // refusal has to say which shape it wanted.
392
+ formatPolicy: ["hyphenated", "hyphenless", "braced", "urn", "hyphenated-only", "any"],
393
+ versionPolicy: ["reject-unassigned", "audit", "audit-only", "allow"],
394
+ variantPolicy: ["reject-non-rfc", "audit", "audit-only", "allow"],
395
+ });
396
+
374
397
  module.exports = gateContract.defineGuard({
375
398
  name: "uuid",
376
399
  kind: "identifier",
377
400
  errorClass: GuardUuidError,
401
+ enumOpts: POLICY_ENUM,
378
402
  profiles: PROFILES,
379
403
  defaults: DEFAULTS,
380
404
  postures: COMPLIANCE_POSTURES,
package/lib/guard-xml.js CHANGED
@@ -733,7 +733,26 @@ var INTEGRATION_FIXTURES = Object.freeze({
733
733
  // ENTITY / external / parameter-entity throwing while strip-able char-class
734
734
  // threats are repaired. The bespoke `gate` carries XML's per-policy
735
735
  // canSanitize matrix unchanged.
736
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
737
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
738
+ // strict one: `externalEntityPolicy: "rejct"` is not "allow", so the check
739
+ // runs, and it is not "reject" either, so an XXE vector drops to a warning.
740
+ //
741
+ // `xmlDsigPolicy` has no "reject": a signature block is a legitimate part of a
742
+ // signed document, so the guard reports its presence and leaves verification
743
+ // to the caller rather than refusing the document for carrying one.
744
+ //
745
+ // The character policies are absent on purpose — they are derived for the
746
+ // whole guard family, and an entry here would shadow that derivation.
747
+ var POLICY_ENUM = gateContract.policyVocabulary([
748
+ "doctypePolicy", "entityPolicy", "externalEntityPolicy", "xincludePolicy",
749
+ "processingInstrPolicy", "cdataPolicy", "schemaLocationPolicy",
750
+ ], gateContract.POLICY_VALUES.rejectAuditAllow, {
751
+ xmlDsigPolicy: ["audit", "audit-only", "allow"],
752
+ });
753
+
736
754
  var _guard = module.exports = gateContract.defineGuard({
755
+ enumOpts: POLICY_ENUM,
737
756
  name: "xml",
738
757
  kind: "content",
739
758
  charRepair: true,
package/lib/guard-yaml.js CHANGED
@@ -262,6 +262,25 @@ var COMPLIANCE_POSTURES = gateContract.compliancePostures(PROFILES, { base: 256
262
262
  // The options that are genuinely CAPS, where zero is not a setting — named once
263
263
  // so this guard's hand-bound parse() and gate() hold a caller to the same rules
264
264
  // the generated validate() does. See guard-image for the full reasoning.
265
+ // Each policy's vocabulary, so a misspelling is a boot error rather than a
266
+ // runtime surprise. Read leniently, a typo takes whichever branch is not the
267
+ // strict one: `aliasPolicy: "rejct"` is not "allow", so the check runs, and it
268
+ // is not "reject" either, so a billion-laughs alias drops to a warning.
269
+ //
270
+ // `audit-only` is absent: tagPolicy tests for "audit" exactly, and a guard
271
+ // whose policies do not all take the same values is worse than one that takes
272
+ // fewer.
273
+ var POLICY_ENUM = gateContract.policyVocabulary([
274
+ "tagPolicy", "aliasPolicy", "multiDocPolicy", "norwayPolicy",
275
+ "leadingZeroPolicy", "duplicateKeyPolicy", "mergeKeyPolicy",
276
+ ], ["reject", "audit", "allow"]);
277
+
278
+ // The same table for the entry points that bind their own resolver, with the
279
+ // family's character policies folded in — defineGuard adds those itself, a
280
+ // hand-bound resolver has to be given them.
281
+ var RESOLVER_ENUMS = Object.freeze(Object.assign({},
282
+ gateContract.charPolicyEnums(DEFAULTS, { canRepair: true }), POLICY_ENUM));
283
+
265
284
  var INT_OPTS = ["maxBytes", "maxDepth", "maxAnchors", "maxAliasDepth",
266
285
  "maxDocuments", "maxNodes", "maxScalarLength"];
267
286
 
@@ -653,7 +672,7 @@ function parse(input, opts) {
653
672
  // `bidiPolicy: "rejct"` here while the generated path refuses it.
654
673
  intOpts: INT_OPTS,
655
674
  nonNegativeOpts: gateContract.capKeysOf(DEFAULTS),
656
- enumOpts: gateContract.charPolicyEnums(DEFAULTS, { canRepair: true }),
675
+ enumOpts: RESOLVER_ENUMS,
657
676
  });
658
677
  if (typeof input !== "string") {
659
678
  throw _err("yaml.bad-input", "parse requires string input");
@@ -805,7 +824,7 @@ function gate(opts) {
805
824
  // `bidiPolicy: "rejct"` here while the generated path refuses it.
806
825
  intOpts: INT_OPTS,
807
826
  nonNegativeOpts: gateContract.capKeysOf(DEFAULTS),
808
- enumOpts: gateContract.charPolicyEnums(DEFAULTS, { canRepair: true }),
827
+ enumOpts: RESOLVER_ENUMS,
809
828
  });
810
829
  return gateContract.buildContentGate({
811
830
  name: opts.name || "guardYaml:" + (opts.profile || "default"),
@@ -817,6 +836,7 @@ function gate(opts) {
817
836
  }
818
837
 
819
838
  module.exports = gateContract.defineGuard({
839
+ enumOpts: POLICY_ENUM,
820
840
  name: "yaml",
821
841
  kind: "content",
822
842
  charRepair: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.18.47",
3
+ "version": "0.18.48",
4
4
  "description": "The Node framework that owns its stack.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",