@blamejs/core 0.18.46 → 0.18.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/lib/codepoint-class.js +31 -3
- package/lib/gate-contract.js +110 -2
- package/lib/guard-email.js +1 -0
- package/lib/guard-filename.js +1 -0
- package/lib/guard-html.js +4 -0
- package/lib/guard-json.js +1 -0
- package/lib/guard-markdown.js +1 -0
- package/lib/guard-svg.js +1 -0
- package/lib/guard-text.js +6 -1
- package/lib/guard-xml.js +1 -0
- package/lib/guard-yaml.js +9 -2
- package/lib/network-dns.js +25 -12
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,20 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.18.x
|
|
10
10
|
|
|
11
|
+
- v0.18.47 (2026-08-22) — **A misspelled character policy was accepted at boot and quietly meant something else.** `bidiPolicy: "rejct"` was accepted by every door the framework offers — the guard's own option resolver and the gate built from it — and then read leniently at runtime: the scan runs because the value is not `allow`, an issue is raised, and the disposition falls through to refuse. Nothing is let through, so it was never a hole; the operator simply asked for one disposition, got another, and was told nothing at the moment a boot error would have cost them a restart. Ninety-three settings across the guard family behaved that way. **Fixed:** *A character policy refuses a value outside its vocabulary* — The five character-threat policies every guard shares — `bidiPolicy`, `nullBytePolicy`, `controlPolicy`, `zeroWidthPolicy`, `tagsPolicy` — now accept only `allow`, `audit`, `audit-only`, `reject`, and `strip` where the guard can carry a repair out. Anything else is refused where options are resolved, which is boot, not the first request that trips the scan. Every entry point takes the check: the generated resolver, and the `gate()` and `validate()` of the guards that bind their own.
|
|
12
|
+
|
|
13
|
+
The check is derived from each guard's own declared defaults rather than hand-listed per guard, for the same reason the size caps beside it are. Hand-listing is what produced the gap: `b.guardCountry` added this for its own three options after `{ reservedPolicy: "rejcet" }` served a reserved country code and reported success, and the other two hundred and fifty-nine policy options across the family never followed. A guard that declares its own vocabulary explicitly still wins, so that one is unchanged.
|
|
14
|
+
|
|
15
|
+
Whether a guard can perform a repair is now DECLARED rather than inferred from whether it exports a `sanitize`. An identifier guard exports one that validates and throws — a UUID with a zero-width character in it is not a UUID, and there is nothing to repair — so inferring the capability let `b.guardRegex`, `b.guardJwt`, `b.guardShell` and `b.guardTemplate` accept `strip` at construction and then refuse at runtime, which is the same silent substitution. Guards that repair say so, and the claim is held from both ends: `b.gateContract.defineGuard` refuses a spec that declares repair without a `sanitize` — or a `sanitizeTransform` for one to be built from — and the family sweep separately checks that a guard claiming it actually removes the character, while one that does not claim it refuses the instruction.
|
|
16
|
+
|
|
17
|
+
What this does not yet cover: the guard-specific policies — formula injection, homoglyphs, DOCTYPE, aliases, and the rest — still accept anything. Their vocabularies genuinely differ from one another, and the set a profile happens to select is not the set the code accepts, so inferring them would refuse values that work today. Each needs its own list, read from the guard rather than guessed. · *`audit-only` did not mean what `audit` means* — The two have been synonyms wherever a policy is turned into a disposition. The severity calculation recognised only the literal `audit`, so on the public validation path they disagreed: `zeroWidthPolicy: "audit"` returned a pass with a warning, while `audit-only` returned a failure with a high-severity issue. The same split affected the control-character and Unicode Tags policies.
|
|
18
|
+
|
|
19
|
+
One predicate decides it now, in the module that owns the character scanning, and the disposition mapper asks the same one. Two spellings of a single setting is how they come to disagree; asking one question is how they stop. · *The DNSSEC algorithm table called RSASHA512 current* — RFC 9904 replaced RFC 8624 as the DNSSEC algorithm requirements, and it reads RSASHA512 as NOT RECOMMENDED for signing while still MUST for validation. `b.network.dns.classifyDnskeyAlgorithm(10)` reported it as `current`, so an operator asking which algorithm to sign a zone with got the opposite of what the current document says.
|
|
20
|
+
|
|
21
|
+
It is not marked deprecated, and deliberately: validation remains mandatory, and a caller using that flag to decide whether to ACCEPT a zone must keep accepting it. One boolean cannot carry a requirement that differs between signing and validation, so the reason names both levels and the flag follows the side an operator can still choose.
|
|
22
|
+
|
|
23
|
+
The citations moved with it — DSA, DSA-NSEC3-SHA1 and ECC-GOST to RFC 9904's Table 2, the DS digest vocabulary and GOST R 34.11-94 to Table 3, and RSAMD5 off RFC 6944, which had been obsoleted twice over, onto RFC 6725. Nothing asserted on algorithm 10 before, which is how a stale classification sat in a table everything else in the file kept current.
|
|
24
|
+
|
|
11
25
|
- v0.18.46 (2026-08-22) — **A failing test reported the handle it leaked instead of the check that failed.** Thirty test files ran their assertions inside a `try` whose `finally` drained open handles. A throw from a `finally` replaces the error the body raised, so when a test failed and its skipped teardown left a socket behind, what surfaced was "a handle leaked" — the consequence — while the check that actually failed was discarded. That is the shape of failure that reads as an unexplained flake, and it was reachable in every suite that opens a server. **Fixed:** *A drain no longer replaces the failure it followed* — `try { ...tests... } finally { await drain() }` reads as correct. It is not. When the body throws, every teardown after the throw is skipped, so the drain finds the servers those teardowns would have closed and throws too — and a throw from a `finally` replaces the body's error. The run reports a leaked handle, which is true and useless: the check that failed is gone, and the leak looks like the bug.
|
|
12
26
|
|
|
13
27
|
The drain now runs through one helper that keeps the body's error and appends the others to it, so a run that failed for a reason reports that reason, and a run that only leaked still reports the leak. Thirty files moved onto it.
|
package/lib/codepoint-class.js
CHANGED
|
@@ -207,6 +207,33 @@ function firstInRanges(text, ranges, from) {
|
|
|
207
207
|
// The shape the detectors want: `{ index, char, codePoint }` for the first
|
|
208
208
|
// codepoint of `text` in `ranges`, or null. `char` is the WHOLE codepoint,
|
|
209
209
|
// which for an astral hit is a surrogate pair.
|
|
210
|
+
/**
|
|
211
|
+
* @primitive b.codepointClass.isAuditPolicy
|
|
212
|
+
* @signature b.codepointClass.isAuditPolicy(policy)
|
|
213
|
+
* @since 0.18.47
|
|
214
|
+
* @status stable
|
|
215
|
+
* @related b.gateContract.policyDisposition, b.gateContract.charPolicyEnums
|
|
216
|
+
*
|
|
217
|
+
* Whether a policy value asks for an AUDIT — reporting the finding rather than
|
|
218
|
+
* refusing or repairing. `audit-only` is the framework's own synonym for
|
|
219
|
+
* `audit`, and this is the one place that decides so.
|
|
220
|
+
*
|
|
221
|
+
* It exists because the two spellings disagreed. The disposition mapper treated
|
|
222
|
+
* them as the same thing while the severity calculation recognised only the
|
|
223
|
+
* literal `audit`, so `zeroWidthPolicy: "audit-only"` produced a high-severity
|
|
224
|
+
* issue and a failed validation where `audit` produced a warning and a pass.
|
|
225
|
+
* Two spellings of one setting is how they come to disagree; asking one
|
|
226
|
+
* predicate is how they stop.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* b.codepointClass.isAuditPolicy("audit"); // → true
|
|
230
|
+
* b.codepointClass.isAuditPolicy("audit-only"); // → true
|
|
231
|
+
* b.codepointClass.isAuditPolicy("reject"); // → false
|
|
232
|
+
*/
|
|
233
|
+
function isAuditPolicy(policy) {
|
|
234
|
+
return policy === "audit" || policy === "audit-only";
|
|
235
|
+
}
|
|
236
|
+
|
|
210
237
|
function _firstHit(text, ranges) {
|
|
211
238
|
var i = firstInRanges(text, ranges);
|
|
212
239
|
if (i === -1) return null;
|
|
@@ -817,7 +844,7 @@ function detectCharThreats(text, opts, codePrefix) {
|
|
|
817
844
|
// reported, so the audit setting did nothing an operator could
|
|
818
845
|
// observe except the thing they asked it not to do.
|
|
819
846
|
severity: opts.controlPolicy === "reject" ? "high"
|
|
820
|
-
: opts.controlPolicy
|
|
847
|
+
: isAuditPolicy(opts.controlPolicy) ? "warn" : "high",
|
|
821
848
|
ruleId: codePrefix + ".control",
|
|
822
849
|
location: ctrlMatch.index,
|
|
823
850
|
// DEL is a control character here but is not in the C0 block, so the
|
|
@@ -849,7 +876,7 @@ function detectCharThreats(text, opts, codePrefix) {
|
|
|
849
876
|
issues.push({
|
|
850
877
|
kind: "zero-width",
|
|
851
878
|
severity: opts.zeroWidthPolicy === "reject" ? "critical"
|
|
852
|
-
: opts.zeroWidthPolicy
|
|
879
|
+
: isAuditPolicy(opts.zeroWidthPolicy) ? "warn" : "high",
|
|
853
880
|
ruleId: codePrefix + ".zero-width",
|
|
854
881
|
location: zwMatch.index,
|
|
855
882
|
snippet: "zero-width / invisible-formatting char U+" +
|
|
@@ -877,7 +904,7 @@ function detectCharThreats(text, opts, codePrefix) {
|
|
|
877
904
|
issues.push({
|
|
878
905
|
kind: "unicode-tags",
|
|
879
906
|
severity: tagsPolicy === "reject" ? "critical"
|
|
880
|
-
: tagsPolicy
|
|
907
|
+
: isAuditPolicy(tagsPolicy) ? "warn" : "high",
|
|
881
908
|
ruleId: codePrefix + ".unicode-tags",
|
|
882
909
|
location: tagMatch.index,
|
|
883
910
|
snippet: "Unicode Tags block char U+" +
|
|
@@ -1724,6 +1751,7 @@ module.exports = {
|
|
|
1724
1751
|
hex4: hex4,
|
|
1725
1752
|
charClass: charClass,
|
|
1726
1753
|
inRanges: inRanges,
|
|
1754
|
+
isAuditPolicy: isAuditPolicy,
|
|
1727
1755
|
firstInRanges: firstInRanges,
|
|
1728
1756
|
stripRanges: stripRanges,
|
|
1729
1757
|
replaceRanges: replaceRanges,
|
package/lib/gate-contract.js
CHANGED
|
@@ -2143,13 +2143,97 @@ function buildContentGate(spec) {
|
|
|
2143
2143
|
* b.gateContract.policyDisposition("audit-only"); // → "audit"
|
|
2144
2144
|
* b.gateContract.policyDisposition("rejet"); // → "refuse" (fail closed)
|
|
2145
2145
|
*/
|
|
2146
|
+
// The character-threat policies every guard in the family shares, and the
|
|
2147
|
+
// vocabulary each accepts. A value outside it is a typo, and a typo is a
|
|
2148
|
+
// CONFIG-TIME error: read leniently at runtime it takes whichever branch is not
|
|
2149
|
+
// the strict one — the scan runs because the value is not "allow", an issue is
|
|
2150
|
+
// raised, and policyDisposition falls through to "refuse". That fails closed,
|
|
2151
|
+
// so it is not a hole, but the operator asked for one disposition and silently
|
|
2152
|
+
// got another. This turns it into a boot error instead.
|
|
2153
|
+
//
|
|
2154
|
+
// `strip` is an instruction to repair, so it is legal only where the guard has
|
|
2155
|
+
// a `sanitize` to repair with — the same rule the family's performable-actions
|
|
2156
|
+
// invariant asserts. An `entries` guard has none by design: a hostile archive
|
|
2157
|
+
// member cannot be made safe.
|
|
2158
|
+
var CHAR_POLICY_KEYS = Object.freeze([
|
|
2159
|
+
"bidiPolicy", "nullBytePolicy", "controlPolicy", "zeroWidthPolicy", "tagsPolicy",
|
|
2160
|
+
]);
|
|
2161
|
+
var CHAR_POLICY_BASE = Object.freeze(["allow", "audit", "audit-only", "reject"]);
|
|
2162
|
+
|
|
2163
|
+
/**
|
|
2164
|
+
* @primitive b.gateContract.charPolicyEnums
|
|
2165
|
+
* @signature b.gateContract.charPolicyEnums(defaults, opts?)
|
|
2166
|
+
* @since 0.18.47
|
|
2167
|
+
* @status stable
|
|
2168
|
+
* @related b.gateContract.resolveProfileAndPosture, b.gateContract.defineGuard
|
|
2169
|
+
*
|
|
2170
|
+
* The `enumOpts` entry for every character-threat policy present in `defaults`,
|
|
2171
|
+
* so a guard binding its own resolver holds callers to the same vocabulary the
|
|
2172
|
+
* generated one does. `opts.canRepair` adds `strip`, which is an instruction to
|
|
2173
|
+
* repair and so legal only where the guard has a `sanitize` to perform it.
|
|
2174
|
+
*
|
|
2175
|
+
* Pass the result as `enumOpts` to `resolveProfileAndPosture`. A guard whose
|
|
2176
|
+
* gate or validate binds its own resolver and omits this accepts a misspelled
|
|
2177
|
+
* policy on that entry point while the generated path refuses it — the two
|
|
2178
|
+
* doors disagreeing is how the check came to cover only one of them.
|
|
2179
|
+
*
|
|
2180
|
+
* @opts
|
|
2181
|
+
* canRepair: boolean, // default: false — adds `strip` to every entry
|
|
2182
|
+
*
|
|
2183
|
+
* @example
|
|
2184
|
+
* gateContract.resolveProfileAndPosture(opts, {
|
|
2185
|
+
* profiles: PROFILES,
|
|
2186
|
+
* defaults: DEFAULTS,
|
|
2187
|
+
* enumOpts: gateContract.charPolicyEnums(DEFAULTS, { canRepair: true }),
|
|
2188
|
+
* });
|
|
2189
|
+
*/
|
|
2190
|
+
function charPolicyEnums(defaults, opts) {
|
|
2191
|
+
var canRepair = !!(opts && opts.canRepair);
|
|
2192
|
+
var out = null;
|
|
2193
|
+
for (var i = 0; i < CHAR_POLICY_KEYS.length; i += 1) {
|
|
2194
|
+
var key = CHAR_POLICY_KEYS[i];
|
|
2195
|
+
// `tagsPolicy` is usually ABSENT from a guard's defaults because it
|
|
2196
|
+
// INHERITS from zeroWidthPolicy (resolveTagsPolicy reads one when the other
|
|
2197
|
+
// is unset). It is still a supported inline override, so a guard exposing
|
|
2198
|
+
// the inherited behaviour must hold a typo in it to the same vocabulary —
|
|
2199
|
+
// keying only on presence left `{ tagsPolicy: "strp" }` unchecked on every
|
|
2200
|
+
// guard that inherits, and the Tags detector then read the typo as a
|
|
2201
|
+
// high-severity refusal instead of the promised boot error.
|
|
2202
|
+
var governs = defaults && (typeof defaults[key] === "string" ||
|
|
2203
|
+
(key === "tagsPolicy" && typeof defaults.zeroWidthPolicy === "string"));
|
|
2204
|
+
if (!governs) continue;
|
|
2205
|
+
if (!out) out = {};
|
|
2206
|
+
out[key] = canRepair ? CHAR_POLICY_BASE.concat(["strip"]) : CHAR_POLICY_BASE.slice();
|
|
2207
|
+
}
|
|
2208
|
+
return out;
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
function _withCharPolicyEnums(defaults, spec) {
|
|
2212
|
+
var declared = spec.enumOpts && typeof spec.enumOpts === "object" ? spec.enumOpts : null;
|
|
2213
|
+
// DECLARED, not inferred from the presence of a `sanitize`. An identifier
|
|
2214
|
+
// guard exports one that validates and throws — there is nothing to repair,
|
|
2215
|
+
// the value either is a UUID or is not — so inferring the capability let
|
|
2216
|
+
// guardRegex, guardJwt, guardShell and guardTemplate accept `strip` at
|
|
2217
|
+
// construction and refuse at runtime, which is the silent substitution this
|
|
2218
|
+
// check exists to stop. A guard says whether it can repair a character, and
|
|
2219
|
+
// the family sweep holds it to the claim.
|
|
2220
|
+
var derived = charPolicyEnums(defaults, { canRepair: spec.charRepair === true });
|
|
2221
|
+
if (!derived) return declared || null;
|
|
2222
|
+
var out = declared ? Object.assign({}, declared) : {};
|
|
2223
|
+
Object.keys(derived).forEach(function (key) {
|
|
2224
|
+
if (declared && declared[key]) return; // an explicit declaration wins
|
|
2225
|
+
out[key] = derived[key];
|
|
2226
|
+
});
|
|
2227
|
+
return out;
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2146
2230
|
var MITIGATION_POLICIES = Object.freeze({
|
|
2147
2231
|
strip: true, "prefix-tab": true, "prefix-quote": true,
|
|
2148
2232
|
"wrap-with-quotes-and-prefix": true, allowlist: true, redact: true, trim: true,
|
|
2149
2233
|
});
|
|
2150
2234
|
function policyDisposition(policy) {
|
|
2151
2235
|
if (policy === "reject") return "refuse";
|
|
2152
|
-
if (policy
|
|
2236
|
+
if (codepointClass.isAuditPolicy(policy)) return "audit";
|
|
2153
2237
|
if (MITIGATION_POLICIES[policy] === true) return "sanitize";
|
|
2154
2238
|
return "refuse";
|
|
2155
2239
|
}
|
|
@@ -3168,7 +3252,12 @@ function defineGuard(spec) {
|
|
|
3168
3252
|
// validate() below.
|
|
3169
3253
|
nonNegativeOpts: _capKeys(defaults, spec.intOpts),
|
|
3170
3254
|
// Options restricted to a fixed vocabulary, checked at the same funnel.
|
|
3171
|
-
|
|
3255
|
+
// The character policies are derived rather than hand-listed, for the
|
|
3256
|
+
// reason the caps above are: declared per guard, they were declared by
|
|
3257
|
+
// one. guard-country wrote `enumOpts` for its own three opts after
|
|
3258
|
+
// `{ reservedPolicy: "rejcet" }` served a reserved code and reported ok,
|
|
3259
|
+
// and the other 259 policy opts across the family never followed.
|
|
3260
|
+
enumOpts: _withCharPolicyEnums(defaults, spec),
|
|
3172
3261
|
});
|
|
3173
3262
|
};
|
|
3174
3263
|
if (typeof spec.detect === "function") {
|
|
@@ -3360,6 +3449,10 @@ function defineGuard(spec) {
|
|
|
3360
3449
|
var out = {
|
|
3361
3450
|
NAME: spec.name,
|
|
3362
3451
|
KIND: spec.kind,
|
|
3452
|
+
// Surfaced so a caller — and the family sweep — can ask whether `strip` is
|
|
3453
|
+
// an instruction this guard can carry out, rather than inferring it from
|
|
3454
|
+
// the presence of a `sanitize` that may only validate and throw.
|
|
3455
|
+
CHAR_REPAIR: spec.charRepair === true,
|
|
3363
3456
|
// The options this guard DECLARES to be caps, where zero is refused. Every
|
|
3364
3457
|
// other numeric option is derived from the defaults and held only to
|
|
3365
3458
|
// "non-negative integer", because derivation cannot tell a cap from a
|
|
@@ -3379,6 +3472,20 @@ function defineGuard(spec) {
|
|
|
3379
3472
|
out.EXTENSIONS = Object.freeze((spec.extensions || []).slice());
|
|
3380
3473
|
}
|
|
3381
3474
|
if (spec.integrationFixtures) out.INTEGRATION_FIXTURES = spec.integrationFixtures;
|
|
3475
|
+
// A declaration has to be backed by something. `charRepair: true` says the
|
|
3476
|
+
// guard can carry out `strip`, which puts that value in its character-policy
|
|
3477
|
+
// vocabulary — so a spec that declares it without a `sanitize` (or a
|
|
3478
|
+
// `sanitizeTransform` for one to be built from) would accept the instruction
|
|
3479
|
+
// at boot and refuse it at runtime, which is the substitution the declaration
|
|
3480
|
+
// was introduced to stop. Checked HERE rather than earlier because
|
|
3481
|
+
// sanitizeTransform generates the sanitize further up, and checking before
|
|
3482
|
+
// that would refuse a spec that does supply a repair path.
|
|
3483
|
+
if (spec.charRepair === true && typeof spec.sanitize !== "function") {
|
|
3484
|
+
throw ErrorClass.factory(prefix + ".bad-spec",
|
|
3485
|
+
prefix + ": charRepair is declared but the guard has no sanitize — " +
|
|
3486
|
+
"`strip` on a character policy is an instruction to repair, so declare a " +
|
|
3487
|
+
"sanitize or a sanitizeTransform, or drop charRepair.");
|
|
3488
|
+
}
|
|
3382
3489
|
if (typeof spec.sanitize === "function") out.sanitize = spec.sanitize;
|
|
3383
3490
|
out.gate = gateFn;
|
|
3384
3491
|
// Error class exported under its own constructor name (GuardCsvError etc.)
|
|
@@ -3713,6 +3820,7 @@ module.exports = {
|
|
|
3713
3820
|
// defineGuard: the cap keys to declare, derived from its defaults so the
|
|
3714
3821
|
// list cannot drift away from them.
|
|
3715
3822
|
capKeysOf: capKeysOf,
|
|
3823
|
+
charPolicyEnums: charPolicyEnums,
|
|
3716
3824
|
identitySanitize: identitySanitize,
|
|
3717
3825
|
ctxValueFrom: ctxValueFrom,
|
|
3718
3826
|
runIssueValidator: runIssueValidator,
|
package/lib/guard-email.js
CHANGED
package/lib/guard-filename.js
CHANGED
|
@@ -1306,6 +1306,7 @@ var INTEGRATION_FIXTURES = Object.freeze({
|
|
|
1306
1306
|
module.exports = gateContract.defineGuard({
|
|
1307
1307
|
name: "filename",
|
|
1308
1308
|
kind: "filename",
|
|
1309
|
+
charRepair: true,
|
|
1309
1310
|
errorClass: GuardFilenameError,
|
|
1310
1311
|
profiles: PROFILES,
|
|
1311
1312
|
defaults: DEFAULTS,
|
package/lib/guard-html.js
CHANGED
|
@@ -983,6 +983,10 @@ var INTEGRATION_FIXTURES = Object.freeze({
|
|
|
983
983
|
module.exports = gateContract.defineGuard({
|
|
984
984
|
name: "html",
|
|
985
985
|
kind: "content",
|
|
986
|
+
// sanitize rewrites the document, so `strip` on a character policy is an
|
|
987
|
+
// instruction this guard can carry out. Declared rather than inferred from
|
|
988
|
+
// the presence of a sanitize — an identifier guard has one that only throws.
|
|
989
|
+
charRepair: true,
|
|
986
990
|
errorClass: GuardHtmlError,
|
|
987
991
|
profiles: PROFILES,
|
|
988
992
|
defaults: DEFAULTS,
|
package/lib/guard-json.js
CHANGED
package/lib/guard-markdown.js
CHANGED
package/lib/guard-svg.js
CHANGED
|
@@ -1008,6 +1008,7 @@ var INTEGRATION_FIXTURES = Object.freeze({
|
|
|
1008
1008
|
var _guard = module.exports = gateContract.defineGuard({
|
|
1009
1009
|
name: "svg",
|
|
1010
1010
|
kind: "content",
|
|
1011
|
+
charRepair: true,
|
|
1011
1012
|
errorClass: GuardSvgError,
|
|
1012
1013
|
profiles: PROFILES,
|
|
1013
1014
|
defaults: DEFAULTS,
|
package/lib/guard-text.js
CHANGED
|
@@ -129,9 +129,13 @@ function _resolveOpts(opts) {
|
|
|
129
129
|
defaults: DEFAULTS,
|
|
130
130
|
errorClass: GuardTextError,
|
|
131
131
|
errCodePrefix: "text",
|
|
132
|
-
// Own resolver, own cap declaration — see guard-image.gate.
|
|
132
|
+
// Own resolver, own cap declaration — see guard-image.gate. The policy
|
|
133
|
+
// vocabulary comes with it for the same reason: a hand-bound resolver that
|
|
134
|
+
// omits it accepts `bidiPolicy: "rejct"` on gate() and validate() while the
|
|
135
|
+
// generated path refuses it.
|
|
133
136
|
intOpts: INT_OPTS,
|
|
134
137
|
nonNegativeOpts: gateContract.capKeysOf(DEFAULTS),
|
|
138
|
+
enumOpts: gateContract.charPolicyEnums(DEFAULTS, { canRepair: true }),
|
|
135
139
|
});
|
|
136
140
|
}
|
|
137
141
|
|
|
@@ -573,6 +577,7 @@ var INTEGRATION_FIXTURES = Object.freeze({
|
|
|
573
577
|
module.exports = gateContract.defineGuard({
|
|
574
578
|
name: "text",
|
|
575
579
|
kind: "content",
|
|
580
|
+
charRepair: true,
|
|
576
581
|
errorClass: GuardTextError,
|
|
577
582
|
profiles: PROFILES,
|
|
578
583
|
defaults: DEFAULTS,
|
package/lib/guard-xml.js
CHANGED
package/lib/guard-yaml.js
CHANGED
|
@@ -648,9 +648,12 @@ function parse(input, opts) {
|
|
|
648
648
|
defaults: DEFAULTS,
|
|
649
649
|
errorClass: GuardYamlError,
|
|
650
650
|
errCodePrefix: "yaml",
|
|
651
|
-
// Own resolver, own cap declaration — see guard-image.gate.
|
|
651
|
+
// Own resolver, own cap declaration — see guard-image.gate. The policy
|
|
652
|
+
// vocabulary comes with it: a hand-bound resolver that omits it accepts
|
|
653
|
+
// `bidiPolicy: "rejct"` here while the generated path refuses it.
|
|
652
654
|
intOpts: INT_OPTS,
|
|
653
655
|
nonNegativeOpts: gateContract.capKeysOf(DEFAULTS),
|
|
656
|
+
enumOpts: gateContract.charPolicyEnums(DEFAULTS, { canRepair: true }),
|
|
654
657
|
});
|
|
655
658
|
if (typeof input !== "string") {
|
|
656
659
|
throw _err("yaml.bad-input", "parse requires string input");
|
|
@@ -797,9 +800,12 @@ function gate(opts) {
|
|
|
797
800
|
defaults: DEFAULTS,
|
|
798
801
|
errorClass: GuardYamlError,
|
|
799
802
|
errCodePrefix: "yaml",
|
|
800
|
-
// Own resolver, own cap declaration — see guard-image.gate.
|
|
803
|
+
// Own resolver, own cap declaration — see guard-image.gate. The policy
|
|
804
|
+
// vocabulary comes with it: a hand-bound resolver that omits it accepts
|
|
805
|
+
// `bidiPolicy: "rejct"` here while the generated path refuses it.
|
|
801
806
|
intOpts: INT_OPTS,
|
|
802
807
|
nonNegativeOpts: gateContract.capKeysOf(DEFAULTS),
|
|
808
|
+
enumOpts: gateContract.charPolicyEnums(DEFAULTS, { canRepair: true }),
|
|
803
809
|
});
|
|
804
810
|
return gateContract.buildContentGate({
|
|
805
811
|
name: opts.name || "guardYaml:" + (opts.profile || "default"),
|
|
@@ -813,6 +819,7 @@ function gate(opts) {
|
|
|
813
819
|
module.exports = gateContract.defineGuard({
|
|
814
820
|
name: "yaml",
|
|
815
821
|
kind: "content",
|
|
822
|
+
charRepair: true,
|
|
816
823
|
errorClass: GuardYamlError,
|
|
817
824
|
profiles: PROFILES,
|
|
818
825
|
defaults: DEFAULTS,
|
package/lib/network-dns.js
CHANGED
|
@@ -2086,8 +2086,13 @@ function isNullMx(mxRecords) {
|
|
|
2086
2086
|
* Classify a DNSKEY / RRSIG algorithm number against the IANA DNS
|
|
2087
2087
|
* Security Algorithm Numbers registry, flagging SHA-1-based and
|
|
2088
2088
|
* other deprecated algorithms per RFC 9905 (Deprecating DNSSEC
|
|
2089
|
-
* SHA-1 Usage), RFC
|
|
2090
|
-
*
|
|
2089
|
+
* SHA-1 Usage), RFC 9904 (Algorithm Implementation Requirements,
|
|
2090
|
+
* which obsoletes RFC 8624), and RFC 6725 (RSAMD5 deprecation).
|
|
2091
|
+
*
|
|
2092
|
+
* `deprecated` answers "should this still be accepted", so it follows
|
|
2093
|
+
* the VALIDATION requirement. Where a spelling differs between signing
|
|
2094
|
+
* and validation — RSASHA512 is NOT RECOMMENDED to sign with but MUST
|
|
2095
|
+
* still validate — the `reason` names both, since one boolean cannot.
|
|
2091
2096
|
*
|
|
2092
2097
|
* Returns `{ algorithm, name, deprecated, reason, known }` for any
|
|
2093
2098
|
* IANA-assigned number; `known: false` for unassigned numbers
|
|
@@ -2123,18 +2128,22 @@ function isNullMx(mxRecords) {
|
|
|
2123
2128
|
// auditing DNSSEC chain-of-trust evidence know they cannot validate
|
|
2124
2129
|
// the entry against a public algorithm registry.
|
|
2125
2130
|
var DNSKEY_ALGORITHMS = Object.freeze({
|
|
2126
|
-
1: { name: "RSAMD5", deprecated: true, reason: "MD5 broken (RFC
|
|
2131
|
+
1: { name: "RSAMD5", deprecated: true, reason: "MD5 broken; deprecated for zone signing (RFC 6725 §2.2)" },
|
|
2127
2132
|
2: { name: "DH", deprecated: true, reason: "Diffie-Hellman key (RFC 2539) — never widely deployed; superseded by signature algorithms" },
|
|
2128
|
-
3: { name: "DSA", deprecated: true, reason: "DSA
|
|
2133
|
+
3: { name: "DSA", deprecated: true, reason: "DSA MUST NOT be used for signing or validation (RFC 9904 Table 2)" },
|
|
2129
2134
|
4: { name: "Reserved", deprecated: true, reason: "Reserved (RFC 4034 §A.1) — not for production use" },
|
|
2130
2135
|
5: { name: "RSASHA1", deprecated: true, reason: "SHA-1 deprecated (RFC 9905 §3)" },
|
|
2131
|
-
6: { name: "DSA-NSEC3-SHA1", deprecated: true, reason: "SHA-1 deprecated (RFC 9905 §3); DSA
|
|
2136
|
+
6: { name: "DSA-NSEC3-SHA1", deprecated: true, reason: "SHA-1 deprecated (RFC 9905 §3); DSA MUST NOT be used (RFC 9904 Table 2)" },
|
|
2132
2137
|
7: { name: "RSASHA1-NSEC3-SHA1", deprecated: true, reason: "SHA-1 deprecated (RFC 9905 §3)" },
|
|
2133
2138
|
8: { name: "RSASHA256", deprecated: false, reason: "current — RFC 5702" }, // IANA DNSKEY algorithm number
|
|
2134
2139
|
9: { name: "Reserved", deprecated: true, reason: "Reserved (RFC 5155) — not for production use" },
|
|
2135
|
-
|
|
2140
|
+
// Not deprecated — validation is still MUST, so a caller deciding whether to
|
|
2141
|
+
// ACCEPT a zone must keep accepting it. But it is NOT RECOMMENDED to sign
|
|
2142
|
+
// with, and reporting it as plainly "current" answered a signing question
|
|
2143
|
+
// with the opposite of what the current RFC says.
|
|
2144
|
+
10: { name: "RSASHA512", deprecated: false, reason: "defined in RFC 5702; RFC 9904 Table 2 — signing NOT RECOMMENDED, validation MUST" },
|
|
2136
2145
|
11: { name: "Reserved", deprecated: true, reason: "Reserved (RFC 5155) — not for production use" },
|
|
2137
|
-
12: { name: "ECC-GOST", deprecated: true, reason: "
|
|
2146
|
+
12: { name: "ECC-GOST", deprecated: true, reason: "MUST NOT be used for signing (RFC 9904 Table 2)" },
|
|
2138
2147
|
13: { name: "ECDSAP256SHA256", deprecated: false, reason: "current — RFC 6605" },
|
|
2139
2148
|
14: { name: "ECDSAP384SHA384", deprecated: false, reason: "current — RFC 6605" },
|
|
2140
2149
|
15: { name: "ED25519", deprecated: false, reason: "current — RFC 8080" },
|
|
@@ -2173,15 +2182,19 @@ var DNSKEY_ALGORITHMS = Object.freeze({
|
|
|
2173
2182
|
* // → { digestType: 2, name: "SHA-256", deprecated: false, ... }
|
|
2174
2183
|
*/
|
|
2175
2184
|
|
|
2176
|
-
// DS digest-type vocabulary (RFC 4034 §5.1 + RFC 6605 §6 + RFC
|
|
2177
|
-
//
|
|
2178
|
-
// §4
|
|
2179
|
-
//
|
|
2185
|
+
// DS digest-type vocabulary (RFC 4034 §5.1 + RFC 6605 §6 + RFC 9904
|
|
2186
|
+
// Table 3 + RFC 9558). Digest type 1 = SHA-1 is deprecated per RFC 9905
|
|
2187
|
+
// §4 — RFC 9904 Table 3 spells it MUST NOT for delegation and MUST for
|
|
2188
|
+
// validation, so `deprecated` follows the delegation side here for the
|
|
2189
|
+
// same reason it follows validation in the DNSKEY table: that is the
|
|
2190
|
+
// side an operator can still choose. Digest types 5 (GOST R 34.11-2012)
|
|
2191
|
+
// and 6 (SM3) added by RFC 9558. Reserved value 0 surfaced for
|
|
2192
|
+
// completeness.
|
|
2180
2193
|
var DS_DIGEST_TYPES = Object.freeze({
|
|
2181
2194
|
0: { name: "Reserved", deprecated: true, reason: "Reserved (RFC 3658) — not for production use" },
|
|
2182
2195
|
1: { name: "SHA-1", deprecated: true, reason: "SHA-1 deprecated (RFC 9905 §4)" },
|
|
2183
2196
|
2: { name: "SHA-256", deprecated: false, reason: "current — RFC 4509" },
|
|
2184
|
-
3: { name: "GOST R 34.11-94", deprecated: true, reason: "
|
|
2197
|
+
3: { name: "GOST R 34.11-94", deprecated: true, reason: "MUST NOT be used for delegation (RFC 9904 Table 3); superseded by GOST 2012 in RFC 9558" },
|
|
2185
2198
|
4: { name: "SHA-384", deprecated: false, reason: "current — RFC 6605 §6" },
|
|
2186
2199
|
5: { name: "GOST R 34.11-2012", deprecated: false, reason: "current — RFC 9558 §3" },
|
|
2187
2200
|
6: { name: "SM3", deprecated: false, reason: "current — RFC 9558 §3 (Chinese national standard)" },
|
package/package.json
CHANGED
package/sbom.cdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:2b85824f-8c87-4b4e-9ee7-abfe05e1b852",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-08-
|
|
8
|
+
"timestamp": "2026-08-22T12:32:06.262Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/core@0.18.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.18.47",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.18.
|
|
25
|
+
"version": "0.18.47",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "The Node framework that owns its stack.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/core@0.18.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.18.47",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/core@0.18.
|
|
57
|
+
"ref": "@blamejs/core@0.18.47",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|