@blamejs/pki 0.5.14 → 0.5.16
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 +44 -0
- package/README.md +1 -1
- package/lib/attrcert-sign.js +511 -2
- package/lib/attrcert-verify.js +21 -0
- package/lib/cmp-verify.js +425 -82
- package/lib/guard-all.js +2 -0
- package/lib/guard-async.js +14 -2
- package/lib/guard-bytes.js +183 -93
- package/lib/guard-compress.js +32 -12
- package/lib/guard-der.js +30 -7
- package/lib/guard-encoding.js +28 -12
- package/lib/guard-header.js +18 -7
- package/lib/guard-identifier.js +146 -79
- package/lib/guard-intrinsic.js +318 -0
- package/lib/guard-json.js +39 -12
- package/lib/guard-limits.js +15 -6
- package/lib/guard-name.js +72 -23
- package/lib/guard-parsed.js +83 -35
- package/lib/guard-range.js +17 -4
- package/lib/guard-secret.js +15 -1
- package/lib/guard-text.js +35 -6
- package/lib/guard-time.js +13 -4
- package/lib/path-validate.js +5 -0
- package/lib/schema-attrcert.js +19 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/guard-compress.js
CHANGED
|
@@ -36,6 +36,25 @@
|
|
|
36
36
|
// code deliberately -- a per-errno surface is attack telemetry.
|
|
37
37
|
|
|
38
38
|
var zlib = require("zlib");
|
|
39
|
+
// Taken at load. The capability probe below decides which algorithms are safe to expose, and the
|
|
40
|
+
// bounded decompress measures its cap with these, so a replacement chooses both. See
|
|
41
|
+
// guard-intrinsic for the whole captured set.
|
|
42
|
+
var _intrinsic = require("./guard-intrinsic");
|
|
43
|
+
var _bufferFrom = _intrinsic.bufferFrom;
|
|
44
|
+
var _isBuffer = _intrinsic.isBuffer;
|
|
45
|
+
var _isInteger = _intrinsic.isInteger;
|
|
46
|
+
var _objectKeys = _intrinsic.keys;
|
|
47
|
+
var _stringify = _intrinsic.stringify;
|
|
48
|
+
// This probe decides which algorithms qualify as truncation-reporting, so both the round-trip
|
|
49
|
+
// comparison and the frame-size floor below settle whether a compressor is trusted. Reading either
|
|
50
|
+
// off a prototype lets a replacement qualify one that silently accepts a cut frame.
|
|
51
|
+
var _compare = _intrinsic.compare;
|
|
52
|
+
var _sizeOf = _intrinsic.sizeOf;
|
|
53
|
+
// The cut that makes the truncated frame. A replacement returning the whole frame means the
|
|
54
|
+
// decompressor is handed something intact, so an algorithm that ignores truncation qualifies on a
|
|
55
|
+
// test that never truncated anything.
|
|
56
|
+
var _subarray = _intrinsic.uncurry(Uint8Array.prototype.subarray);
|
|
57
|
+
var _hasOwn = _intrinsic.hasOwn;
|
|
39
58
|
|
|
40
59
|
// The decompressor per algorithm: a registry, so a new algorithm is a row rather
|
|
41
60
|
// than a branch and an unrecognized name fails closed.
|
|
@@ -77,28 +96,28 @@ function _reportsTruncation(name) {
|
|
|
77
96
|
var compress = _PROBE_COMPRESS[name];
|
|
78
97
|
var decompress = DECOMPRESS[name];
|
|
79
98
|
if (typeof compress !== "function" || typeof decompress !== "function") return false;
|
|
80
|
-
var sample =
|
|
99
|
+
var sample = _bufferFrom("0123456789abcdefghijklmnopqrstuvwxyz0123456789");
|
|
81
100
|
var frame, whole;
|
|
82
101
|
try { frame = compress(sample); }
|
|
83
102
|
catch (_e) { /* allow:swallow-unverified a compressor that cannot compress 46 ASCII bytes is a broken runtime; the algorithm simply does not qualify, which is the fail-closed direction */ return false; }
|
|
84
103
|
// A cut of 2 removes the frame's end marker; the sample is long enough that what remains is
|
|
85
104
|
// still a plausible frame head and not nothing at all.
|
|
86
|
-
if (!
|
|
105
|
+
if (!_isBuffer(frame) || _sizeOf(frame) < 4) return false;
|
|
87
106
|
try { whole = decompress(frame, { maxOutputLength: 4096 }); }
|
|
88
107
|
catch (_e2) { /* allow:swallow-unverified a decompressor that rejects its own compressor's whole frame is a broken runtime; the algorithm does not qualify, again fail-closed */ return false; }
|
|
89
|
-
if (!
|
|
108
|
+
if (!_isBuffer(whole) || _compare(whole, sample) !== 0) return false;
|
|
90
109
|
// The cut frame must be refused. A returned value of any length, including the empty buffer
|
|
91
110
|
// some bindings hand back, means a truncation would pass through unseen.
|
|
92
111
|
var cutAccepted = false;
|
|
93
112
|
try {
|
|
94
|
-
decompress(frame
|
|
113
|
+
decompress(_subarray(frame, 0, _sizeOf(frame) - 2), { maxOutputLength: 4096 });
|
|
95
114
|
cutAccepted = true;
|
|
96
115
|
} catch (_e3) { /* refused, as a decompressor handed an unfinished frame must */ }
|
|
97
116
|
return !cutAccepted;
|
|
98
117
|
}
|
|
99
118
|
|
|
100
119
|
var SAFE = {};
|
|
101
|
-
|
|
120
|
+
_intrinsic.forEach(_objectKeys(DECOMPRESS), function (n) { if (_reportsTruncation(n)) SAFE[n] = true; });
|
|
102
121
|
|
|
103
122
|
// bounded(algorithm, stream, cap, E, codes, label) -> Buffer. Decompress `stream`
|
|
104
123
|
// under `algorithm`, refusing any output that would exceed `cap` bytes and any
|
|
@@ -116,19 +135,19 @@ function bounded(algorithm, stream, cap, E, codes, label) {
|
|
|
116
135
|
// fault, not untrusted input: a caller resolves a wire value to a name against algorithms()
|
|
117
136
|
// and rejects one it does not find, before reaching here. Arriving with a name outside that
|
|
118
137
|
// set means the consumer skipped the step, so it faults loudly and never silently.
|
|
119
|
-
if (!
|
|
120
|
-
throw new TypeError("guard.compress.bounded: unknown or unsafe algorithm " +
|
|
138
|
+
if (!_hasOwn(SAFE, algorithm) || !decompress) {
|
|
139
|
+
throw new TypeError("guard.compress.bounded: unknown or unsafe algorithm " + _stringify(algorithm));
|
|
121
140
|
}
|
|
122
141
|
// The byte-source normalization is guard.bytes.view's job and belongs at the
|
|
123
142
|
// consumer's boundary; reaching here with anything else means it was skipped.
|
|
124
|
-
if (!
|
|
143
|
+
if (!_isBuffer(stream)) {
|
|
125
144
|
throw new TypeError("guard.compress.bounded: stream must be a Buffer (route the input through guard.bytes.view first)");
|
|
126
145
|
}
|
|
127
146
|
// The ceiling is an authoring input: an undefined / NaN / fractional / negative
|
|
128
147
|
// cap would silently disable the bomb defense. It must also be at least 1 --
|
|
129
148
|
// node rejects maxOutputLength 0 with a different fault than a cap breach, so a
|
|
130
149
|
// zero cap would surface as a malformed-stream verdict instead of a config error.
|
|
131
|
-
if (!
|
|
150
|
+
if (!_isInteger(cap) || cap < 1) {
|
|
132
151
|
throw new TypeError("guard.compress.bounded: cap must be a positive integer");
|
|
133
152
|
}
|
|
134
153
|
var what = label || "the compressed input";
|
|
@@ -145,8 +164,9 @@ function bounded(algorithm, stream, cap, E, codes, label) {
|
|
|
145
164
|
// ends before the input does means the remainder was ignored, which is the
|
|
146
165
|
// malleability above, so the whole input must be exactly one frame.
|
|
147
166
|
var consumed = res.engine.bytesWritten;
|
|
148
|
-
|
|
149
|
-
|
|
167
|
+
var streamLen = _sizeOf(stream);
|
|
168
|
+
if (consumed !== streamLen) {
|
|
169
|
+
throw E(codes.failed, what + " carries " + (streamLen - consumed) +
|
|
150
170
|
" trailing byte(s) after the end of the compressed stream");
|
|
151
171
|
}
|
|
152
172
|
return res.buffer;
|
|
@@ -159,6 +179,6 @@ function bounded(algorithm, stream, cap, E, codes, label) {
|
|
|
159
179
|
// @enforced-by behavioral -- a read-only list of the safe registry's keys has no
|
|
160
180
|
// rename-proof code shape to re-inline; the guard's reject path is the contract,
|
|
161
181
|
// and a consumer offering an algorithm this list omits fails closed at `bounded`.
|
|
162
|
-
function algorithms() { return
|
|
182
|
+
function algorithms() { return _objectKeys(SAFE); }
|
|
163
183
|
|
|
164
184
|
module.exports = { bounded: bounded, algorithms: algorithms };
|
package/lib/guard-der.js
CHANGED
|
@@ -30,6 +30,16 @@
|
|
|
30
30
|
// therefore both deferred to first use -- the documented circular-load exception to top-of-file
|
|
31
31
|
// requires. Guards sit BELOW the codec in the dependency order; this is the one place that
|
|
32
32
|
// order is inverted, and it is inverted lazily.
|
|
33
|
+
// The byte comparison SET-OF ordering is decided by, taken at load. A replacement chooses which
|
|
34
|
+
// encodings this guard calls correctly ordered. See guard-intrinsic for the whole captured set.
|
|
35
|
+
var _compare = require("./guard-intrinsic").compare;
|
|
36
|
+
var _forEach = require("./guard-intrinsic").forEach;
|
|
37
|
+
// The emptiness gate below decides whether bytes reach the decoder at all, and on a byte view
|
|
38
|
+
// `length` is a replaceable accessor: one answering non-zero for an empty buffer sends nothing to
|
|
39
|
+
// asn1.decode as though it were an element.
|
|
40
|
+
var _sizeOf = require("./guard-intrinsic").sizeOf;
|
|
41
|
+
var _create = require("./guard-intrinsic").create;
|
|
42
|
+
|
|
33
43
|
var asn1 = null;
|
|
34
44
|
function _asn1() { if (asn1 === null) asn1 = require("./asn1-der"); return asn1; }
|
|
35
45
|
|
|
@@ -39,7 +49,10 @@ var VALUE_READERS = null;
|
|
|
39
49
|
function _readers() {
|
|
40
50
|
if (VALUE_READERS !== null) return VALUE_READERS;
|
|
41
51
|
var asn1 = _asn1();
|
|
42
|
-
|
|
52
|
+
// Null-prototype: this table IS the allowlist of universal tags with a strict content reader, and
|
|
53
|
+
// a tag it does not carry is refused. Built on a `{}`, an inherited numeric property supplies a
|
|
54
|
+
// reader for a tag that was deliberately left out, and the refusal that is the point stops.
|
|
55
|
+
var m = _create(null), R = asn1.read, T = asn1.TAGS;
|
|
43
56
|
// ENUMERATED shares INTEGER's content rules and is NOT constrained to non-negative
|
|
44
57
|
// values. X.680 (02/2021) sec. 20.2 requires each NamedNumber's SignedNumber to be
|
|
45
58
|
// distinct, and sec. 19 defines SignedNumber ::= number | "-" number; the non-negativity
|
|
@@ -55,8 +68,8 @@ function _readers() {
|
|
|
55
68
|
// it through read.string would fold it into the RFC 5280 sec. 7.1 name-comparison identity
|
|
56
69
|
// class (see asn1-der.js).
|
|
57
70
|
m[T.NUMERIC_STRING] = R.numericString;
|
|
58
|
-
[T.UTF8_STRING, T.PRINTABLE_STRING, T.IA5_STRING, T.TELETEX_STRING, T.VISIBLE_STRING,
|
|
59
|
-
T.BMP_STRING, T.UNIVERSAL_STRING]
|
|
71
|
+
_forEach([T.UTF8_STRING, T.PRINTABLE_STRING, T.IA5_STRING, T.TELETEX_STRING, T.VISIBLE_STRING,
|
|
72
|
+
T.BMP_STRING, T.UNIVERSAL_STRING], function (t) { m[t] = R.string; });
|
|
60
73
|
VALUE_READERS = m;
|
|
61
74
|
return VALUE_READERS;
|
|
62
75
|
}
|
|
@@ -79,9 +92,19 @@ function _readers() {
|
|
|
79
92
|
// security posture, which this family avoids, and tightening it for both would reject valid
|
|
80
93
|
// structured SETs that c509 round-trips today. The rule is therefore unchanged and the cost is
|
|
81
94
|
// written down rather than discovered later.
|
|
82
|
-
|
|
95
|
+
// Null-prototype: the rank decides canonical SET order, so an inherited property answering for an
|
|
96
|
+
// unrecognized tag class would rank it rather than leave it unranked, and a SET in no canonical
|
|
97
|
+
// order would compare as ordered.
|
|
98
|
+
var TAG_CLASS_RANK = _create(null);
|
|
99
|
+
TAG_CLASS_RANK.universal = 0;
|
|
100
|
+
TAG_CLASS_RANK.application = 1;
|
|
101
|
+
TAG_CLASS_RANK.context = 2;
|
|
102
|
+
TAG_CLASS_RANK["private"] = 3;
|
|
83
103
|
function setOrderOk(kids) {
|
|
84
|
-
|
|
104
|
+
// A null-prototype accumulator: on a `{}` a planted `Object.prototype["universal:16"]` answers
|
|
105
|
+
// this membership test for a tag the SET never carried, which reports a duplicate that is not
|
|
106
|
+
// there and selects the wrong canonical-order rule for the whole SET.
|
|
107
|
+
var i, dup = false, seen = _create(null);
|
|
85
108
|
for (i = 0; i < kids.length; i++) {
|
|
86
109
|
var key = kids[i].tagClass + ":" + kids[i].tagNumber;
|
|
87
110
|
if (seen[key]) { dup = true; break; }
|
|
@@ -89,7 +112,7 @@ function setOrderOk(kids) {
|
|
|
89
112
|
}
|
|
90
113
|
var octetAsc = true, tagAsc = true;
|
|
91
114
|
for (i = 1; i < kids.length; i++) {
|
|
92
|
-
if (
|
|
115
|
+
if (_compare(kids[i - 1].bytes, kids[i].bytes) > 0) octetAsc = false;
|
|
93
116
|
// Tag order ranks by CLASS first (universal < application < context < private, X.680
|
|
94
117
|
// sec. 8.6), then by tag number. Compare the class's NUMBER: the names do not sort in
|
|
95
118
|
// class order.
|
|
@@ -141,7 +164,7 @@ function element(node, E, code, label) {
|
|
|
141
164
|
// @enforced-by guard-shape-reinlined
|
|
142
165
|
// @guard-shape must be exactly one well-formed DER element
|
|
143
166
|
function tlv(content, E, code, label) {
|
|
144
|
-
if (!content || content
|
|
167
|
+
if (!content || _sizeOf(content) === 0) throw E(code, label + " must be a non-empty DER element");
|
|
145
168
|
var node;
|
|
146
169
|
try { node = _asn1().decode(content); }
|
|
147
170
|
catch (e) { throw E(code, label + " must be exactly one well-formed DER element (no trailing data)", e); }
|
package/lib/guard-encoding.js
CHANGED
|
@@ -33,10 +33,26 @@
|
|
|
33
33
|
// follows the length, which the caller already caps. It is also the more honest
|
|
34
34
|
// statement of the rule: the set of permitted characters is the rule, written out.
|
|
35
35
|
function _alphabet(chars) {
|
|
36
|
+
// allow:guard-reads-runtime-live -- the three tables below are the only callers and they are
|
|
37
|
+
// built at module load, before any caller code has run. There is no live read to remove.
|
|
36
38
|
var t = new Uint8Array(128);
|
|
37
|
-
for (var i = 0; i < chars.length; i++) t[chars
|
|
39
|
+
for (var i = 0; i < chars.length; i++) t[_charCodeAt(chars, i)] = 1;
|
|
38
40
|
return t;
|
|
39
41
|
}
|
|
42
|
+
// Both halves of the round trip, taken at load, along with the string reads the alphabet checks
|
|
43
|
+
// are built on. Canonicality here is decided by decoding the text and re-encoding the result, so
|
|
44
|
+
// either half is enough to decide the answer: a `toString` returning the input makes every
|
|
45
|
+
// non-canonical encoding canonical, and a `charCodeAt` deciding what is in the alphabet chooses
|
|
46
|
+
// which characters are there to be judged.
|
|
47
|
+
var _intrinsic = require("./guard-intrinsic");
|
|
48
|
+
var _bufferFrom = _intrinsic.bufferFrom;
|
|
49
|
+
var _toString = _intrinsic.uncurry(Buffer.prototype.toString);
|
|
50
|
+
var _charCodeAt = _intrinsic.uncurry(String.prototype.charCodeAt);
|
|
51
|
+
var _strSlice = _intrinsic.uncurry(String.prototype.slice);
|
|
52
|
+
var _toLowerCase = _intrinsic.uncurry(String.prototype.toLowerCase);
|
|
53
|
+
var _isInteger = _intrinsic.isInteger;
|
|
54
|
+
var _floor = _intrinsic.floor;
|
|
55
|
+
|
|
40
56
|
var UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ", LOWER = "abcdefghijklmnopqrstuvwxyz", DIGITS = "0123456789";
|
|
41
57
|
var B64URL_ALPHABET = _alphabet(UPPER + LOWER + DIGITS + "-_");
|
|
42
58
|
var B64_ALPHABET = _alphabet(UPPER + LOWER + DIGITS + "+/");
|
|
@@ -47,7 +63,7 @@ var HEX_ALPHABET = _alphabet(DIGITS + "abcdef" + "ABCDEF");
|
|
|
47
63
|
// bound rejects it, so no lookup can read undefined off the end of the table.
|
|
48
64
|
function _inAlphabet(text, table) {
|
|
49
65
|
for (var i = 0; i < text.length; i++) {
|
|
50
|
-
var c = text
|
|
66
|
+
var c = _charCodeAt(text, i);
|
|
51
67
|
if (c > 127 || table[c] !== 1) return false;
|
|
52
68
|
}
|
|
53
69
|
return true;
|
|
@@ -60,10 +76,10 @@ function _capBefore(nChars, perByteChars, maxBytes, E, code, label) {
|
|
|
60
76
|
// The cap is an authoring input: a NaN / fractional / negative maxBytes makes
|
|
61
77
|
// the comparison below always false -- the cap silently disabled. Config-time
|
|
62
78
|
// TypeError instead (null/undefined stays the documented uncapped mode).
|
|
63
|
-
if (!
|
|
79
|
+
if (!_isInteger(maxBytes) || maxBytes < 0) {
|
|
64
80
|
throw new TypeError("guard.encoding: maxBytes must be a non-negative integer or null");
|
|
65
81
|
}
|
|
66
|
-
if (
|
|
82
|
+
if (_floor(nChars / perByteChars) > maxBytes) {
|
|
67
83
|
throw E(code, label + " exceeds the maximum decoded size of " + maxBytes + " bytes");
|
|
68
84
|
}
|
|
69
85
|
}
|
|
@@ -76,8 +92,8 @@ function base64url(text, maxBytes, E, code, label) {
|
|
|
76
92
|
if (!_inAlphabet(text, B64URL_ALPHABET)) throw E(code, label + " is not base64url (padding or a non-alphabet character)");
|
|
77
93
|
if (text.length % 4 === 1) throw E(code, label + " has an impossible base64url length");
|
|
78
94
|
_capBefore(text.length * 3, 4, maxBytes, E, code, label);
|
|
79
|
-
var buf =
|
|
80
|
-
if (buf
|
|
95
|
+
var buf = _bufferFrom(text,"base64url");
|
|
96
|
+
if (_toString(buf, "base64url") !== text) throw E(code, label + " is not canonical base64url");
|
|
81
97
|
return buf;
|
|
82
98
|
}
|
|
83
99
|
|
|
@@ -90,12 +106,12 @@ function base64(text, maxBytes, E, code, label) {
|
|
|
90
106
|
// the body is measured first and the alphabet applies to what remains. An "="
|
|
91
107
|
// anywhere else leaves a non-alphabet character in the body and rejects there.
|
|
92
108
|
var pad = 0;
|
|
93
|
-
while (pad < 2 && text.length > pad &&
|
|
94
|
-
if (!_inAlphabet(text
|
|
109
|
+
while (pad < 2 && text.length > pad && _charCodeAt(text, text.length - 1 - pad) === 0x3d) pad++;
|
|
110
|
+
if (!_inAlphabet(_strSlice(text, 0, text.length - pad), B64_ALPHABET)) throw E(code, label + " is not base64 (a non-alphabet character)");
|
|
95
111
|
if (text.length % 4 !== 0) throw E(code, label + " must be whole 4-character base64 groups (RFC 4648 sec. 3.5)");
|
|
96
112
|
_capBefore(text.length * 3, 4, maxBytes, E, code, label);
|
|
97
|
-
var buf =
|
|
98
|
-
if (buf
|
|
113
|
+
var buf = _bufferFrom(text,"base64");
|
|
114
|
+
if (_toString(buf, "base64") !== text) throw E(code, label + " is not canonical base64 (RFC 4648 sec. 3.5)");
|
|
99
115
|
return buf;
|
|
100
116
|
}
|
|
101
117
|
|
|
@@ -111,8 +127,8 @@ function hex(text, maxBytes, E, code, label) {
|
|
|
111
127
|
if (!_inAlphabet(text, HEX_ALPHABET)) throw E(code, label + " is not hexadecimal");
|
|
112
128
|
if (text.length % 2 !== 0) throw E(code, label + " must have an even number of hex digits");
|
|
113
129
|
_capBefore(text.length, 2, maxBytes, E, code, label);
|
|
114
|
-
var buf =
|
|
115
|
-
if (buf
|
|
130
|
+
var buf = _bufferFrom(text,"hex");
|
|
131
|
+
if (_toString(buf, "hex") !== _toLowerCase(text)) throw E(code, label + " is not canonical hexadecimal");
|
|
116
132
|
return buf;
|
|
117
133
|
}
|
|
118
134
|
|
package/lib/guard-header.js
CHANGED
|
@@ -18,6 +18,17 @@
|
|
|
18
18
|
// reintroduce the injection.
|
|
19
19
|
|
|
20
20
|
var C = require("./constants");
|
|
21
|
+
// Taken at load. `byteLength` is what the RFC 5322 line limit is measured with, so a replacement
|
|
22
|
+
// decides whether an over-long header line is refused. See guard-intrinsic for the whole set.
|
|
23
|
+
var _byteLength = require("./guard-intrinsic").byteLength;
|
|
24
|
+
// The character-code read the RFC 5322 ftext and CR/LF/NUL checks are built on. One that never
|
|
25
|
+
// reports a control byte is a header-injection defense that inspects nothing.
|
|
26
|
+
var _charCodeAt = require("./guard-intrinsic").uncurry(String.prototype.charCodeAt);
|
|
27
|
+
var _stringify = require("./guard-intrinsic").stringify;
|
|
28
|
+
// The conversion that produces the text this guard then scans for CR and LF. A replacement returns
|
|
29
|
+
// a clean string for a value carrying a line break, so the injection scan runs on text that is not
|
|
30
|
+
// what gets emitted.
|
|
31
|
+
var _String = require("./guard-intrinsic").String;
|
|
21
32
|
|
|
22
33
|
// assertField(name, value, E, code) -> value | throws new E(code, ...)
|
|
23
34
|
// name: RFC 5322 ftext, printable ASCII [0x21, 0x7e] except ':' (0x3a), non-empty
|
|
@@ -33,18 +44,18 @@ var C = require("./constants");
|
|
|
33
44
|
function assertField(name, value, E, code) {
|
|
34
45
|
if (typeof name !== "string" || name.length === 0) throw new E(code, "a header field name must be a non-empty string");
|
|
35
46
|
for (var i = 0; i < name.length; i++) {
|
|
36
|
-
var nc = name
|
|
37
|
-
if (nc < 0x21 || nc > 0x7e || nc === 0x3a) throw new E(code, "a header field name must be RFC 5322 ftext (printable ASCII, no space / ':' / control): " +
|
|
47
|
+
var nc = _charCodeAt(name, i);
|
|
48
|
+
if (nc < 0x21 || nc > 0x7e || nc === 0x3a) throw new E(code, "a header field name must be RFC 5322 ftext (printable ASCII, no space / ':' / control): " + _stringify(name));
|
|
38
49
|
}
|
|
39
|
-
var v = typeof value === "string" ? value :
|
|
50
|
+
var v = typeof value === "string" ? value : _String(value);
|
|
40
51
|
for (var j = 0; j < v.length; j++) {
|
|
41
|
-
var vc = v
|
|
42
|
-
if (vc === 0x00 || vc === 0x0d || vc === 0x0a) throw new E(code, "a header field value must not contain CR / LF / NUL (header injection) in " +
|
|
52
|
+
var vc = _charCodeAt(v, j);
|
|
53
|
+
if (vc === 0x00 || vc === 0x0d || vc === 0x0a) throw new E(code, "a header field value must not contain CR / LF / NUL (header injection) in " + _stringify(name));
|
|
43
54
|
}
|
|
44
55
|
// RFC 5322 sec. 2.1.1: the serialized "Name: value" line (UTF-8 octets, excluding CRLF) must not exceed
|
|
45
56
|
// the line limit, or a relay may re-fold it and silently change the signed bytes of a protected header.
|
|
46
|
-
if (
|
|
47
|
-
throw new E(code, "a header field line exceeds RFC 5322's " + C.LIMITS.HEADER_LINE_MAX_OCTETS + "-octet limit: " +
|
|
57
|
+
if (_byteLength(name, "utf8") + 2 + _byteLength(v, "utf8") > C.LIMITS.HEADER_LINE_MAX_OCTETS) {
|
|
58
|
+
throw new E(code, "a header field line exceeds RFC 5322's " + C.LIMITS.HEADER_LINE_MAX_OCTETS + "-octet limit: " + _stringify(name));
|
|
48
59
|
}
|
|
49
60
|
return v;
|
|
50
61
|
}
|