@mavogel/awscdk-rootmail 0.1.24 → 0.1.25

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 (96) hide show
  1. package/.jsii +3 -3
  2. package/lib/rootmail.js +1 -1
  3. package/lib/ses-receive.js +1 -1
  4. package/node_modules/@zone-eu/mailsplit/README.md +1 -1
  5. package/node_modules/@zone-eu/mailsplit/lib/headers.js +10 -4
  6. package/node_modules/@zone-eu/mailsplit/lib/message-splitter.js +5 -4
  7. package/node_modules/@zone-eu/mailsplit/lib/mime-node.d.ts +4 -1
  8. package/node_modules/@zone-eu/mailsplit/lib/mime-node.js +23 -2
  9. package/node_modules/@zone-eu/mailsplit/lib/types.d.ts +3 -1
  10. package/node_modules/@zone-eu/mailsplit/package.json +4 -4
  11. package/node_modules/libbase64/CHANGELOG.md +20 -0
  12. package/node_modules/libbase64/README.md +40 -13
  13. package/node_modules/libbase64/dist/cjs/libbase64.d.ts +80 -0
  14. package/node_modules/libbase64/dist/cjs/libbase64.js +310 -0
  15. package/node_modules/libbase64/dist/cjs/package.json +3 -0
  16. package/node_modules/libbase64/dist/esm/libbase64.d.ts +80 -0
  17. package/node_modules/libbase64/{lib → dist/esm}/libbase64.js +74 -117
  18. package/node_modules/libbase64/dist/esm/package.json +3 -0
  19. package/node_modules/libbase64/package.json +59 -13
  20. package/node_modules/libmime/CHANGELOG.md +31 -0
  21. package/node_modules/libmime/README.md +55 -26
  22. package/node_modules/libmime/SECURITY.md +152 -0
  23. package/node_modules/libmime/dist/cjs/charset.d.ts +43 -0
  24. package/node_modules/libmime/dist/cjs/charset.js +135 -0
  25. package/node_modules/libmime/dist/cjs/charsets.d.ts +2 -0
  26. package/node_modules/libmime/dist/cjs/charsets.js +213 -0
  27. package/node_modules/libmime/dist/cjs/get-charset-name.d.ts +1 -0
  28. package/node_modules/libmime/dist/cjs/get-charset-name.js +224 -0
  29. package/node_modules/libmime/dist/cjs/libmime.d.ts +218 -0
  30. package/node_modules/libmime/dist/cjs/libmime.js +1013 -0
  31. package/node_modules/libmime/dist/cjs/mimetypes.d.ts +7 -0
  32. package/node_modules/libmime/dist/cjs/mimetypes.js +2072 -0
  33. package/node_modules/libmime/dist/cjs/package.json +3 -0
  34. package/node_modules/libmime/dist/cjs/types.d.ts +59 -0
  35. package/node_modules/libmime/dist/cjs/types.js +4 -0
  36. package/node_modules/libmime/dist/esm/charset.d.ts +43 -0
  37. package/node_modules/libmime/dist/esm/charset.js +125 -0
  38. package/node_modules/libmime/dist/esm/charsets.d.ts +2 -0
  39. package/node_modules/libmime/{lib → dist/esm}/charsets.js +3 -5
  40. package/node_modules/libmime/dist/esm/get-charset-name.d.ts +1 -0
  41. package/node_modules/libmime/{lib → dist/esm}/get-charset-name.js +1 -8
  42. package/node_modules/libmime/dist/esm/libmime.d.ts +219 -0
  43. package/node_modules/libmime/{lib → dist/esm}/libmime.js +256 -329
  44. package/node_modules/libmime/dist/esm/mimetypes.d.ts +7 -0
  45. package/node_modules/libmime/dist/esm/mimetypes.js +2068 -0
  46. package/node_modules/libmime/dist/esm/package.json +3 -0
  47. package/node_modules/libmime/dist/esm/types.d.ts +59 -0
  48. package/node_modules/libmime/dist/esm/types.js +3 -0
  49. package/node_modules/libmime/package.json +58 -15
  50. package/node_modules/libqp/CHANGELOG.md +18 -0
  51. package/node_modules/libqp/README.md +50 -19
  52. package/node_modules/libqp/dist/cjs/libqp.d.ts +74 -0
  53. package/node_modules/libqp/dist/cjs/libqp.js +378 -0
  54. package/node_modules/libqp/dist/cjs/package.json +3 -0
  55. package/node_modules/libqp/dist/esm/libqp.d.ts +74 -0
  56. package/node_modules/libqp/dist/esm/libqp.js +369 -0
  57. package/node_modules/libqp/dist/esm/package.json +3 -0
  58. package/node_modules/libqp/lib/libqp.js +4 -383
  59. package/node_modules/libqp/lib/package.json +3 -0
  60. package/node_modules/libqp/package.json +62 -6
  61. package/node_modules/mailparser/.release-please-manifest.json +1 -1
  62. package/node_modules/mailparser/CHANGELOG.md +42 -0
  63. package/node_modules/mailparser/package.json +5 -5
  64. package/node_modules/nodemailer/CHANGELOG.md +44 -0
  65. package/node_modules/nodemailer/dist/cjs/addressparser/index.js +695 -267
  66. package/node_modules/nodemailer/dist/cjs/mailer/mail-message.js +4 -1
  67. package/node_modules/nodemailer/dist/cjs/mime-funcs/index.js +8 -2
  68. package/node_modules/nodemailer/dist/cjs/mime-node/index.js +48 -16
  69. package/node_modules/nodemailer/dist/cjs/package-info.d.ts +1 -1
  70. package/node_modules/nodemailer/dist/cjs/package-info.js +1 -1
  71. package/node_modules/nodemailer/dist/cjs/shared/url.js +22 -5
  72. package/node_modules/nodemailer/dist/esm/addressparser/index.js +695 -267
  73. package/node_modules/nodemailer/dist/esm/mailer/mail-message.js +4 -1
  74. package/node_modules/nodemailer/dist/esm/mime-funcs/index.js +8 -2
  75. package/node_modules/nodemailer/dist/esm/mime-node/index.js +48 -16
  76. package/node_modules/nodemailer/dist/esm/package-info.d.ts +1 -1
  77. package/node_modules/nodemailer/dist/esm/package-info.js +1 -1
  78. package/node_modules/nodemailer/dist/esm/shared/url.js +22 -5
  79. package/node_modules/nodemailer/package.json +1 -1
  80. package/package.json +2 -2
  81. package/node_modules/libbase64/.eslintrc +0 -11
  82. package/node_modules/libbase64/.github/FUNDING.yml +0 -4
  83. package/node_modules/libbase64/.github/VULNERABILITY_REPORT.yml +0 -75
  84. package/node_modules/libbase64/.github/workflows/release.yaml +0 -39
  85. package/node_modules/libbase64/.github/workflows/test.yml +0 -21
  86. package/node_modules/libbase64/.ncurc.js +0 -13
  87. package/node_modules/libbase64/.prettierrc.js +0 -8
  88. package/node_modules/libmime/lib/charset.js +0 -131
  89. package/node_modules/libmime/lib/mimetypes.js +0 -2049
  90. package/node_modules/libqp/.eslintrc +0 -15
  91. package/node_modules/libqp/.github/FUNDING.yml +0 -4
  92. package/node_modules/libqp/.github/VULNERABILITY_REPORT.yml +0 -75
  93. package/node_modules/libqp/.github/workflows/release.yaml +0 -39
  94. package/node_modules/libqp/.github/workflows/test.yml +0 -21
  95. package/node_modules/libqp/.prettierrc.js +0 -8
  96. package/node_modules/libqp/test/libqp-test.js +0 -366
@@ -1,15 +0,0 @@
1
- {
2
- "rules": {
3
- "indent": 0,
4
- "no-await-in-loop": 0,
5
- "require-atomic-updates": 0
6
- },
7
- "globals": {
8
- "BigInt": true
9
- },
10
- "extends": ["nodemailer", "prettier"],
11
- "parserOptions": {
12
- "ecmaVersion": 2018,
13
- "sourceType": "script"
14
- }
15
- }
@@ -1,4 +0,0 @@
1
- # These are supported funding model platforms
2
-
3
- github: [andris9] # enable once enrolled
4
- custom: ['https://www.paypal.me/nodemailer']
@@ -1,75 +0,0 @@
1
- name: "libqp security vulnerability report"
2
- description: "Report an exploitable security vulnerability in libqp"
3
-
4
- body:
5
- - type: markdown
6
- attributes:
7
- value: |
8
- Before reporting, read our
9
- [Security Policy](https://github.com/nodemailer/libqp/security/policy).
10
-
11
- Reports must demonstrate a credible security impact.
12
- Technically valid bugs without a realistic attack
13
- scenario belong in the regular issue tracker.
14
-
15
- - type: textarea
16
- id: summary
17
- attributes:
18
- label: Vulnerability summary
19
- description: Describe the vulnerability and affected versions.
20
- validations:
21
- required: true
22
-
23
- - type: textarea
24
- id: attacker
25
- attributes:
26
- label: Attacker capabilities
27
- description: |
28
- What can an attacker control without already having
29
- privileged access to the application or its configuration?
30
- validations:
31
- required: true
32
- min_length: 100
33
-
34
- - type: textarea
35
- id: exploit
36
- attributes:
37
- label: Realistic exploitation scenario
38
- description: |
39
- Describe how this can be exploited in a real application.
40
- Explain the prerequisites and security boundary crossed.
41
- validations:
42
- required: true
43
- min_length: 150
44
-
45
- - type: textarea
46
- id: poc
47
- attributes:
48
- label: Proof of concept
49
- description: Provide reproducible steps or working code.
50
- validations:
51
- required: true
52
-
53
- - type: textarea
54
- id: impact
55
- attributes:
56
- label: Security impact
57
- description: |
58
- What can the attacker achieve? Who is affected?
59
- Explain why this is a security vulnerability rather
60
- than an ordinary bug.
61
- validations:
62
- required: true
63
- min_length: 100
64
-
65
- - type: checkboxes
66
- id: confirmation
67
- attributes:
68
- label: Reporting requirements
69
- options:
70
- - label: I have read the Security Policy.
71
- required: true
72
- - label: I have verified the finding against a supported version.
73
- required: true
74
- - label: I have described a credible attacker-controlled scenario.
75
- required: true
@@ -1,39 +0,0 @@
1
- on:
2
- push:
3
- branches:
4
- - master
5
- # Manual run: publish the version currently on master (used when a release was cut
6
- # but the publish step failed)
7
- workflow_dispatch:
8
-
9
- permissions:
10
- contents: write
11
- pull-requests: write
12
- id-token: write
13
-
14
- name: release
15
- jobs:
16
- release-please:
17
- runs-on: ubuntu-latest
18
- steps:
19
- - uses: google-github-actions/release-please-action@v3
20
- id: release
21
- if: ${{ github.event_name == 'push' }}
22
- with:
23
- release-type: node
24
- package-name: ${{vars.NPM_MODULE_NAME}}
25
- pull-request-title-pattern: 'chore${scope}: release ${version} [skip-ci]'
26
- # The logic below handles the npm publication through npm trusted publishing
27
- # (OIDC, no token secret): it runs when a new release is created, or on a
28
- # manual dispatch.
29
- - uses: actions/checkout@v4
30
- if: ${{ steps.release.outputs.release_created || github.event_name == 'workflow_dispatch' }}
31
- - uses: actions/setup-node@v4
32
- with:
33
- node-version: 24
34
- registry-url: 'https://registry.npmjs.org'
35
- if: ${{ steps.release.outputs.release_created || github.event_name == 'workflow_dispatch' }}
36
- - run: npm ci
37
- if: ${{ steps.release.outputs.release_created || github.event_name == 'workflow_dispatch' }}
38
- - run: npm publish --provenance --access public
39
- if: ${{ steps.release.outputs.release_created || github.event_name == 'workflow_dispatch' }}
@@ -1,21 +0,0 @@
1
- name: Run tests
2
-
3
- on:
4
- push:
5
- pull_request:
6
-
7
- jobs:
8
- test:
9
- strategy:
10
- matrix:
11
- node: [16.x, 18.x, 20.x, 21.x]
12
- os: [ubuntu-latest]
13
- runs-on: ${{ matrix.os }}
14
- steps:
15
- - uses: actions/checkout@v2
16
- - name: Use Node.js ${{ matrix.node }}
17
- uses: actions/setup-node@v4
18
- with:
19
- node-version: ${{ matrix.node }}
20
- - run: npm install
21
- - run: npm test
@@ -1,8 +0,0 @@
1
- module.exports = {
2
- printWidth: 160,
3
- tabWidth: 4,
4
- singleQuote: true,
5
- endOfLine: 'lf',
6
- trailingComma: 'none',
7
- arrowParens: 'avoid'
8
- };
@@ -1,366 +0,0 @@
1
- 'use strict';
2
-
3
- const { Buffer } = require('node:buffer');
4
- const test = require('node:test');
5
- const assert = require('node:assert').strict;
6
-
7
- const libqp = require('../lib/libqp');
8
-
9
- test('Encoding tests', async t => {
10
- await t.test('simple string', async () => {
11
- const encoded = libqp.encode('tere jõgeva');
12
-
13
- assert.strictEqual(encoded, 'tere j=C3=B5geva');
14
- });
15
-
16
- await t.test('stream', async () => {
17
- let input = 'tere jõgeva';
18
- let encoder = new libqp.Encoder();
19
-
20
- let encoded = await new Promise((resolve, reject) => {
21
- let chunks = [];
22
- encoder.on('readable', () => {
23
- let chunk;
24
-
25
- while ((chunk = encoder.read()) !== null) {
26
- chunks.push(chunk);
27
- }
28
- });
29
- encoder.on('end', () => {
30
- resolve(Buffer.concat(chunks).toString());
31
- });
32
- encoder.on('Error', err => {
33
- reject(err);
34
- });
35
-
36
- encoder.end(Buffer.from(input));
37
- });
38
-
39
- assert.strictEqual(encoded, 'tere j=C3=B5geva');
40
- });
41
- });
42
-
43
- test('Decoding tests', async t => {
44
- // Example taken from RFC2045 section 6.7
45
- const encoded = "Now's the time =\r\n" + 'for all folk to come=\r\n' + ' to the aid of their country.';
46
- const expectedDecoded = "Now's the time for all folk to come to the aid of their country.";
47
-
48
- await t.test('simple string', async () => {
49
- const decoded = libqp.decode(encoded).toString();
50
- assert.strictEqual(decoded, expectedDecoded);
51
- });
52
-
53
- await t.test('stream', async () => {
54
- const decoder = new libqp.Decoder();
55
-
56
- const decoded = await new Promise((resolve, reject) => {
57
- const chunks = [];
58
- decoder.on('readable', () => {
59
- let chunk;
60
-
61
- while ((chunk = decoder.read()) !== null) {
62
- chunks.push(chunk);
63
- }
64
- });
65
- decoder.on('end', () => {
66
- resolve(Buffer.concat(chunks).toString());
67
- });
68
- decoder.on('Error', err => {
69
- reject(err);
70
- });
71
-
72
- decoder.end(Buffer.from(encoded));
73
- });
74
-
75
- assert.strictEqual(decoded, expectedDecoded);
76
- });
77
-
78
- await t.test('stream, multiple chunks', async () => {
79
- const encodedChunk1Length = 3;
80
- const encodedChunk1 = encoded.substring(0, encodedChunk1Length);
81
- const encodedChunk2 = encoded.substring(encodedChunk1Length);
82
-
83
- const decoder = new libqp.Decoder();
84
-
85
- const decoded = await new Promise((resolve, reject) => {
86
- const chunks = [];
87
- decoder.on('readable', () => {
88
- let chunk;
89
-
90
- while ((chunk = decoder.read()) !== null) {
91
- chunks.push(chunk);
92
- }
93
- });
94
- decoder.on('end', () => {
95
- resolve(Buffer.concat(chunks).toString());
96
- });
97
- decoder.on('Error', err => {
98
- reject(err);
99
- });
100
-
101
- decoder.write(Buffer.from(encodedChunk1));
102
- decoder.end(Buffer.from(encodedChunk2));
103
- });
104
-
105
- assert.strictEqual(decoded, expectedDecoded);
106
- });
107
-
108
- await t.test('stream, space at end of chunk', async () => {
109
- const encodedChunk1Length = encoded.indexOf(' ') + 1;
110
- const encodedChunk1 = encoded.substring(0, encodedChunk1Length);
111
- const encodedChunk2 = encoded.substring(encodedChunk1Length);
112
-
113
- const decoder = new libqp.Decoder();
114
-
115
- const decoded = await new Promise((resolve, reject) => {
116
- const chunks = [];
117
- decoder.on('readable', () => {
118
- let chunk;
119
-
120
- while ((chunk = decoder.read()) !== null) {
121
- chunks.push(chunk);
122
- }
123
- });
124
- decoder.on('end', () => {
125
- resolve(Buffer.concat(chunks).toString());
126
- });
127
- decoder.on('Error', err => {
128
- reject(err);
129
- });
130
-
131
- decoder.write(Buffer.from(encodedChunk1));
132
- decoder.end(Buffer.from(encodedChunk2));
133
- });
134
-
135
- assert.strictEqual(decoded, expectedDecoded);
136
- });
137
-
138
- await t.test('stream, soft line break equals sign at end of chunk', async () => {
139
- const encodedChunk1Length = encoded.indexOf('=') + 1;
140
- const encodedChunk1 = encoded.substring(0, encodedChunk1Length);
141
- const encodedChunk2 = encoded.substring(encodedChunk1Length);
142
-
143
- const decoder = new libqp.Decoder();
144
-
145
- const decoded = await new Promise((resolve, reject) => {
146
- const chunks = [];
147
- decoder.on('readable', () => {
148
- let chunk;
149
-
150
- while ((chunk = decoder.read()) !== null) {
151
- chunks.push(chunk);
152
- }
153
- });
154
- decoder.on('end', () => {
155
- resolve(Buffer.concat(chunks).toString());
156
- });
157
- decoder.on('Error', err => {
158
- reject(err);
159
- });
160
-
161
- decoder.write(Buffer.from(encodedChunk1));
162
- decoder.end(Buffer.from(encodedChunk2));
163
- });
164
-
165
- assert.strictEqual(decoded, expectedDecoded);
166
- });
167
-
168
- await t.test('stream, CR at end of chunk', async () => {
169
- const encodedChunk1Length = encoded.indexOf('\r') + 1;
170
- const encodedChunk1 = encoded.substring(0, encodedChunk1Length);
171
- const encodedChunk2 = encoded.substring(encodedChunk1Length);
172
-
173
- const decoder = new libqp.Decoder();
174
-
175
- const decoded = await new Promise((resolve, reject) => {
176
- const chunks = [];
177
- decoder.on('readable', () => {
178
- let chunk;
179
-
180
- while ((chunk = decoder.read()) !== null) {
181
- chunks.push(chunk);
182
- }
183
- });
184
- decoder.on('end', () => {
185
- resolve(Buffer.concat(chunks).toString());
186
- });
187
- decoder.on('Error', err => {
188
- reject(err);
189
- });
190
-
191
- decoder.write(Buffer.from(encodedChunk1));
192
- decoder.end(Buffer.from(encodedChunk2));
193
- });
194
-
195
- assert.strictEqual(decoded, expectedDecoded);
196
- });
197
- });
198
-
199
- test('Raw 8-bit bytes in Quoted-Printable input', async t => {
200
- const decodeStream = async chunks =>
201
- await new Promise((resolve, reject) => {
202
- const decoder = new libqp.Decoder();
203
- const output = [];
204
- decoder.on('data', chunk => output.push(chunk));
205
- decoder.on('end', () => resolve(Buffer.concat(output)));
206
- decoder.on('error', reject);
207
- for (const chunk of chunks) {
208
- decoder.write(chunk);
209
- }
210
- decoder.end();
211
- });
212
-
213
- // "caf\xC3\xA9 \xE2\x82\xAC =C3=A9", raw UTF-8 bytes mixed with an escape
214
- const utf8Input = Buffer.concat([Buffer.from('caf'), Buffer.from([0xc3, 0xa9, 0x20, 0xe2, 0x82, 0xac]), Buffer.from(' =C3=A9')]);
215
- const utf8Expected = Buffer.from('café € é');
216
-
217
- // "caf\xE9 =E9", a raw latin1 byte next to the same byte escaped
218
- const latin1Input = Buffer.concat([Buffer.from('caf'), Buffer.from([0xe9]), Buffer.from(' =E9')]);
219
- const latin1Expected = Buffer.from([0x63, 0x61, 0x66, 0xe9, 0x20, 0xe9]);
220
-
221
- await t.test('decode() keeps raw UTF-8 bytes from a Buffer', async () => {
222
- assert.deepStrictEqual(libqp.decode(utf8Input), utf8Expected);
223
- });
224
-
225
- await t.test('decode() keeps raw latin1 bytes from a Buffer', async () => {
226
- assert.deepStrictEqual(libqp.decode(latin1Input), latin1Expected);
227
- });
228
-
229
- await t.test('decode() of a binary string still maps char codes to bytes', async () => {
230
- assert.deepStrictEqual(libqp.decode(latin1Input.toString('binary')), latin1Expected);
231
- });
232
-
233
- await t.test('Decoder keeps raw UTF-8 bytes', async () => {
234
- assert.deepStrictEqual(await decodeStream([utf8Input]), utf8Expected);
235
- });
236
-
237
- await t.test('Decoder keeps raw latin1 bytes', async () => {
238
- assert.deepStrictEqual(await decodeStream([latin1Input]), latin1Expected);
239
- });
240
-
241
- await t.test('Decoder keeps raw bytes and escapes split byte by byte', async () => {
242
- const chunks = [];
243
- for (let i = 0; i < utf8Input.length; i++) {
244
- chunks.push(utf8Input.subarray(i, i + 1));
245
- }
246
- assert.deepStrictEqual(await decodeStream(chunks), utf8Expected);
247
- });
248
-
249
- await t.test('Decoder handles an escape split across chunks', async () => {
250
- // split inside "=C3", once after "=" and once after "=C"
251
- const input = Buffer.from('ab=C3=A9=\r\ncd');
252
- const expected = Buffer.from('abécd');
253
- assert.deepStrictEqual(await decodeStream([input.subarray(0, 3), input.subarray(3)]), expected);
254
- assert.deepStrictEqual(await decodeStream([input.subarray(0, 4), input.subarray(4)]), expected);
255
- // soft line break split between "=" and CRLF
256
- assert.deepStrictEqual(await decodeStream([input.subarray(0, 9), input.subarray(9)]), expected);
257
- });
258
- });
259
-
260
- test('Hyphens at the start of a line', async t => {
261
- const encodeStream = async (chunks, options) =>
262
- await new Promise((resolve, reject) => {
263
- const encoder = new libqp.Encoder(options);
264
- const output = [];
265
- encoder.on('data', chunk => output.push(chunk));
266
- encoder.on('end', () => resolve(Buffer.concat(output).toString()));
267
- encoder.on('error', reject);
268
- for (const chunk of chunks) {
269
- encoder.write(chunk);
270
- }
271
- encoder.end();
272
- });
273
-
274
- await t.test('encode() escapes a hyphen that starts a line', async () => {
275
- // two hyphens at the start of a line would read as a MIME boundary delimiter
276
- assert.strictEqual(libqp.encode('--boundary\r\n-x'), '=2D-boundary\r\n=2Dx');
277
- assert.strictEqual(libqp.encode('\n-'), '\n=2D');
278
- });
279
-
280
- await t.test('encode() keeps a hyphen inside a line', async () => {
281
- assert.strictEqual(libqp.encode('a-b --c'), 'a-b --c');
282
- });
283
-
284
- await t.test('wrap() escapes a hyphen that a soft line break would move to the start of a line', async () => {
285
- const input = 'a'.repeat(75) + '-x';
286
- const wrapped = libqp.wrap(libqp.encode(input));
287
- assert.strictEqual(wrapped, 'a'.repeat(75) + '=\r\n=2Dx');
288
- assert.strictEqual(libqp.decode(wrapped).toString(), input);
289
- });
290
-
291
- await t.test('wrap() keeps every line within the line length when escaping', async () => {
292
- const input = '-'.repeat(300);
293
- const wrapped = libqp.wrap(libqp.encode(input));
294
- const lines = wrapped.split('\r\n');
295
- assert.ok(lines.length > 3);
296
- for (const line of lines) {
297
- assert.ok(line.length <= 76, `line too long: ${line.length}`);
298
- assert.notStrictEqual(line.charAt(0), '-');
299
- }
300
- assert.strictEqual(libqp.decode(wrapped).toString(), input);
301
- });
302
-
303
- await t.test('Encoder escapes a hyphen at the start of a chunk only when it starts a line', async () => {
304
- for (const options of [{}, { lineLength: false }]) {
305
- assert.strictEqual(await encodeStream(['-a'], options), '=2Da');
306
- assert.strictEqual(await encodeStream(['ab\n', '-c'], options), 'ab\n=2Dc');
307
- assert.strictEqual(await encodeStream(['ab', '-c'], options), 'ab-c');
308
- }
309
- });
310
-
311
- await t.test('Encoder output never starts a line with a hyphen', async () => {
312
- // a body that decodes to a boundary delimiter followed by the headers of a part
313
- const input = 'text\r\n--boundary\r\nContent-Type: application/x-evil; name=evil.exe\r\n\r\n' + '-'.repeat(200) + '\r\n';
314
- const chunks = [];
315
- for (let i = 0; i < input.length; i += 7) {
316
- chunks.push(Buffer.from(input.substring(i, i + 7)));
317
- }
318
- for (const options of [{}, { lineLength: false }]) {
319
- const encoded = await encodeStream(chunks, options);
320
- assert.ok(!/(^|\n)-/.test(encoded), `line starts with a hyphen: ${JSON.stringify(encoded)}`);
321
- assert.strictEqual(libqp.decode(encoded).toString(), input);
322
- }
323
- });
324
- });
325
-
326
- test('Trailing whitespace', async t => {
327
- await t.test('decode() removes whitespace from the end of lines', async () => {
328
- assert.strictEqual(libqp.decode('abc \r\ndef \t\nghi ').toString(), 'abc\r\ndef\nghi');
329
- assert.strictEqual(libqp.decode(' \n ').toString(), '\n');
330
- assert.strictEqual(libqp.decode('a b').toString(), 'a b');
331
- });
332
-
333
- await t.test('decode() treats a soft line break with trailing whitespace as a soft line break', async () => {
334
- assert.strictEqual(libqp.decode('foo= \t\r\nbar').toString(), 'foobar');
335
- });
336
-
337
- await t.test('decode() handles a long run of whitespace in linear time', async () => {
338
- // the trim used to be quadratic on a run that does not end the line, 50 KB took 2 seconds
339
- for (const chr of [' ', '\t']) {
340
- const input = chr.repeat(500 * 1024) + 'x';
341
- const start = Date.now();
342
- const decoded = libqp.decode(input).toString('binary');
343
- const duration = Date.now() - start;
344
- assert.strictEqual(decoded, input);
345
- assert.ok(duration < 500, `decoding took ${duration} ms`);
346
- }
347
- });
348
- });
349
-
350
- test('Escape sequences', async t => {
351
- await t.test('decode() decodes valid escapes and keeps invalid ones', async () => {
352
- assert.strictEqual(libqp.decode('=41=42=43').toString(), 'ABC');
353
- assert.strictEqual(libqp.decode('=c3=a9').toString(), 'é');
354
- assert.strictEqual(libqp.decode('=4G=4').toString(), '=4G=4');
355
- assert.strictEqual(libqp.decode('==41').toString(), '=A');
356
- assert.strictEqual(libqp.decode('a=\r\nb=\nc=').toString(), 'abc');
357
- });
358
-
359
- await t.test('decode() sizes the output for a body made of escapes', async () => {
360
- const count = 1024 * 1024;
361
- const decoded = libqp.decode('=41'.repeat(count));
362
- assert.strictEqual(decoded.length, count);
363
- assert.strictEqual(decoded[0], 0x41);
364
- assert.strictEqual(decoded[count - 1], 0x41);
365
- });
366
- });