imapflow 1.3.4 → 1.3.6
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/.github/codeql/codeql-config.yml +12 -0
- package/.github/workflows/codeql.yml +102 -0
- package/.github/workflows/stale.yml +5 -0
- package/.github/workflows/test.yml +3 -0
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +15 -0
- package/SECURITY.md +88 -0
- package/SECURITY.txt +27 -0
- package/lib/commands/starttls.js +3 -0
- package/lib/commands/store.js +1 -1
- package/lib/handler/imap-stream.js +53 -6
- package/lib/handler/token-parser.js +7 -0
- package/lib/imap-flow.d.ts +11 -0
- package/lib/imap-flow.js +217 -28
- package/lib/proxy-connection.js +26 -1
- package/lib/tools.js +8 -2
- package/package.json +2 -2
- package/test/commands-integration-test.js +45 -0
- package/test/connection-edge-cases-test.js +206 -1
- package/test/imap-stream-edge-cases-test.js +126 -0
- package/test/proxy-connection-test.js +86 -0
- package/test/reliability-improvements-test.js +88 -0
- package/test/starttls-injection-test.js +181 -0
- package/test/token-parser-test.js +18 -0
- package/test/tools-test.js +42 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
name: "ImapFlow CodeQL config"
|
|
2
|
+
|
|
3
|
+
# Exclude code that is not part of the published library runtime from analysis.
|
|
4
|
+
# These paths generate only false-positive noise:
|
|
5
|
+
# - test fixtures intentionally feed malformed/hostile input and exercise
|
|
6
|
+
# edge cases that are not representative of production usage
|
|
7
|
+
# - the example scripts hardcode demo credentials and connection details for
|
|
8
|
+
# local experimentation and are not maintained as production code
|
|
9
|
+
paths-ignore:
|
|
10
|
+
- test
|
|
11
|
+
- '**/test/**'
|
|
12
|
+
- examples
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# For most projects, this workflow file will not need changing; you simply need
|
|
2
|
+
# to commit it to your repository.
|
|
3
|
+
#
|
|
4
|
+
# You may wish to alter this file to override the set of languages analyzed,
|
|
5
|
+
# or to provide custom queries or build logic.
|
|
6
|
+
#
|
|
7
|
+
# ******** NOTE ********
|
|
8
|
+
# We have attempted to detect the languages in your repository. Please check
|
|
9
|
+
# the `language` matrix defined below to confirm you have the correct set of
|
|
10
|
+
# supported CodeQL languages.
|
|
11
|
+
#
|
|
12
|
+
name: "CodeQL Advanced"
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
push:
|
|
16
|
+
branches: [ "master" ]
|
|
17
|
+
pull_request:
|
|
18
|
+
branches: [ "master" ]
|
|
19
|
+
schedule:
|
|
20
|
+
- cron: '40 17 * * 6'
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
analyze:
|
|
24
|
+
name: Analyze (${{ matrix.language }})
|
|
25
|
+
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
|
26
|
+
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
|
27
|
+
# - https://gh.io/supported-runners-and-hardware-resources
|
|
28
|
+
# - https://gh.io/using-larger-runners (GitHub.com only)
|
|
29
|
+
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
|
30
|
+
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
|
31
|
+
permissions:
|
|
32
|
+
# required for all workflows
|
|
33
|
+
security-events: write
|
|
34
|
+
|
|
35
|
+
# required to fetch internal or private CodeQL packs
|
|
36
|
+
packages: read
|
|
37
|
+
|
|
38
|
+
# only required for workflows in private repositories
|
|
39
|
+
actions: read
|
|
40
|
+
contents: read
|
|
41
|
+
|
|
42
|
+
strategy:
|
|
43
|
+
fail-fast: false
|
|
44
|
+
matrix:
|
|
45
|
+
include:
|
|
46
|
+
- language: actions
|
|
47
|
+
build-mode: none
|
|
48
|
+
- language: javascript-typescript
|
|
49
|
+
build-mode: none
|
|
50
|
+
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
|
|
51
|
+
# Use `c-cpp` to analyze code written in C, C++ or both
|
|
52
|
+
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
|
53
|
+
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
|
54
|
+
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
|
55
|
+
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
|
56
|
+
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
|
57
|
+
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
|
58
|
+
steps:
|
|
59
|
+
- name: Checkout repository
|
|
60
|
+
uses: actions/checkout@v4
|
|
61
|
+
|
|
62
|
+
# Add any setup steps before running the `github/codeql-action/init` action.
|
|
63
|
+
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
|
64
|
+
# or others). This is typically only required for manual builds.
|
|
65
|
+
# - name: Setup runtime (example)
|
|
66
|
+
# uses: actions/setup-example@v1
|
|
67
|
+
|
|
68
|
+
# Initializes the CodeQL tools for scanning.
|
|
69
|
+
- name: Initialize CodeQL
|
|
70
|
+
uses: github/codeql-action/init@v4
|
|
71
|
+
with:
|
|
72
|
+
languages: ${{ matrix.language }}
|
|
73
|
+
build-mode: ${{ matrix.build-mode }}
|
|
74
|
+
config-file: ./.github/codeql/codeql-config.yml
|
|
75
|
+
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
76
|
+
# By default, queries listed here will override any specified in a config file.
|
|
77
|
+
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
78
|
+
|
|
79
|
+
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
|
80
|
+
# queries: security-extended,security-and-quality
|
|
81
|
+
|
|
82
|
+
# If the analyze step fails for one of the languages you are analyzing with
|
|
83
|
+
# "We were unable to automatically build your code", modify the matrix above
|
|
84
|
+
# to set the build mode to "manual" for that language. Then modify this step
|
|
85
|
+
# to build your code.
|
|
86
|
+
# ℹ️ Command-line programs to run using the OS shell.
|
|
87
|
+
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
|
88
|
+
- name: Run manual build steps
|
|
89
|
+
if: matrix.build-mode == 'manual'
|
|
90
|
+
shell: bash
|
|
91
|
+
run: |
|
|
92
|
+
echo 'If you are using a "manual" build mode for one or more of the' \
|
|
93
|
+
'languages you are analyzing, replace this with the commands to build' \
|
|
94
|
+
'your code, for example:'
|
|
95
|
+
echo ' make bootstrap'
|
|
96
|
+
echo ' make release'
|
|
97
|
+
exit 1
|
|
98
|
+
|
|
99
|
+
- name: Perform CodeQL Analysis
|
|
100
|
+
uses: github/codeql-action/analyze@v4
|
|
101
|
+
with:
|
|
102
|
+
category: "/language:${{matrix.language}}"
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.3.6](https://github.com/postalsys/imapflow/compare/v1.3.5...v1.3.6) (2026-06-05)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* harden STARTTLS upgrade, literal bounds, and connection error paths ([35eca81](https://github.com/postalsys/imapflow/commit/35eca81075401e89f0b3aca84e26c41710a62c28))
|
|
9
|
+
|
|
10
|
+
## [1.3.5](https://github.com/postalsys/imapflow/compare/v1.3.4...v1.3.5) (2026-06-01)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* correct flag removal, literal parsing, path normalization, and harden the parser ([d15c053](https://github.com/postalsys/imapflow/commit/d15c0536f4b7db2ebca5e258fc885da0d194b5fd))
|
|
16
|
+
* prevent host process crash from socket errors after unbind() when COMPRESS=DEFLATE is active ([e30fca1](https://github.com/postalsys/imapflow/commit/e30fca1a42a4d2ab6f7a6a766effafe3df6b1aff))
|
|
17
|
+
|
|
3
18
|
## [1.3.4](https://github.com/postalsys/imapflow/compare/v1.3.3...v1.3.4) (2026-05-29)
|
|
4
19
|
|
|
5
20
|
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
ImapFlow is a Node.js IMAP client library. It opens TLS/cleartext connections to
|
|
4
|
+
IMAP servers, sends account credentials, and parses untrusted protocol responses
|
|
5
|
+
from those servers. Because it handles credentials and processes data from
|
|
6
|
+
remote servers that may be malicious or buggy, we take security reports
|
|
7
|
+
seriously and aim to respond quickly.
|
|
8
|
+
|
|
9
|
+
## Supported Versions
|
|
10
|
+
|
|
11
|
+
Security fixes are released only against the latest version. We do not backport
|
|
12
|
+
patches to older releases - upgrading to the current release line is the
|
|
13
|
+
supported way to receive security updates.
|
|
14
|
+
|
|
15
|
+
| Version | Supported |
|
|
16
|
+
| ------- | ------------------ |
|
|
17
|
+
| 1.x | :white_check_mark: |
|
|
18
|
+
| < 1.0 | :x: |
|
|
19
|
+
|
|
20
|
+
If you are on an older version, please upgrade. See the release notes at
|
|
21
|
+
<https://github.com/postalsys/imapflow/releases> before updating.
|
|
22
|
+
|
|
23
|
+
## Reporting a Vulnerability
|
|
24
|
+
|
|
25
|
+
**Please do not report security vulnerabilities through public GitHub issues,
|
|
26
|
+
pull requests, or discussions.**
|
|
27
|
+
|
|
28
|
+
Report privately through one of the following channels:
|
|
29
|
+
|
|
30
|
+
1. **GitHub Security Advisories (preferred).** Open a private report at
|
|
31
|
+
<https://github.com/postalsys/imapflow/security/advisories/new>. This keeps
|
|
32
|
+
the discussion private until a fix is published and lets us credit you.
|
|
33
|
+
2. **Email.** Send details to **andris@postalsys.com** (the contact listed in
|
|
34
|
+
[`SECURITY.txt`](SECURITY.txt)). Encrypt sensitive details with the PGP key
|
|
35
|
+
referenced there if possible.
|
|
36
|
+
|
|
37
|
+
When reporting, please include as much of the following as you can:
|
|
38
|
+
|
|
39
|
+
- The affected version(s) and environment (ImapFlow version, Node.js version,
|
|
40
|
+
OS).
|
|
41
|
+
- The component involved (e.g. the IMAP response stream/parser, literal and line
|
|
42
|
+
handling, TLS/STARTTLS upgrade, credential handling, the command compiler, or
|
|
43
|
+
the public client API).
|
|
44
|
+
- A clear description of the issue and its impact (e.g. memory exhaustion or
|
|
45
|
+
denial of service from a malicious server, parser crash, credential
|
|
46
|
+
disclosure, TLS verification bypass, injection into the IMAP command stream,
|
|
47
|
+
prototype pollution, information disclosure).
|
|
48
|
+
- A minimal proof of concept or reproduction steps - ideally a sample server
|
|
49
|
+
response or a short script that triggers the issue.
|
|
50
|
+
- Any suggested remediation, if you have one.
|
|
51
|
+
|
|
52
|
+
We are a small team, so there is no guaranteed response time - sometimes reports
|
|
53
|
+
are handled within hours, sometimes they take longer. Accepted issues are fixed
|
|
54
|
+
in a new release and coordinated through a GitHub Security Advisory, and
|
|
55
|
+
reporters who wish to be named are credited.
|
|
56
|
+
|
|
57
|
+
## CVEs
|
|
58
|
+
|
|
59
|
+
We track and disclose vulnerabilities through GitHub Security Advisories. We do
|
|
60
|
+
not request or manage CVE identifiers ourselves. If you need a CVE assigned for a
|
|
61
|
+
reported issue, please request one yourself - for example, through GitHub's own
|
|
62
|
+
CVE request flow on the published advisory, or another CNA.
|
|
63
|
+
|
|
64
|
+
## Scope
|
|
65
|
+
|
|
66
|
+
In scope: the ImapFlow library source in this repository - the IMAP response
|
|
67
|
+
stream and token parser (including handling of hostile or malformed server
|
|
68
|
+
responses, literals, line lengths, and resource/DoS bounds), the TLS and
|
|
69
|
+
STARTTLS upgrade path and certificate handling, credential handling during
|
|
70
|
+
authentication, the command compiler that builds outgoing IMAP commands, and the
|
|
71
|
+
public client API.
|
|
72
|
+
|
|
73
|
+
Out of scope:
|
|
74
|
+
|
|
75
|
+
- Vulnerabilities in your own application code that uses ImapFlow.
|
|
76
|
+
- Misconfiguration of your usage - for example, disabling TLS certificate
|
|
77
|
+
verification (`tls.rejectUnauthorized: false`), connecting over cleartext when
|
|
78
|
+
TLS is available, or passing untrusted input directly into mailbox paths or
|
|
79
|
+
command arguments without validation.
|
|
80
|
+
- Vulnerabilities in the IMAP servers you connect to, or in third-party
|
|
81
|
+
dependencies (please report those to their respective maintainers; we will
|
|
82
|
+
upgrade once a fix is available).
|
|
83
|
+
- Issues that require an already-compromised host or local access to the machine
|
|
84
|
+
running ImapFlow.
|
|
85
|
+
- Social-engineering reports and theoretical issues without a demonstrated,
|
|
86
|
+
concrete impact.
|
|
87
|
+
|
|
88
|
+
Thank you for helping keep ImapFlow and its users safe.
|
package/SECURITY.txt
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-----BEGIN PGP SIGNED MESSAGE-----
|
|
2
|
+
Hash: SHA256
|
|
3
|
+
|
|
4
|
+
Contact: https://github.com/postalsys/imapflow/security/advisories/new
|
|
5
|
+
Contact: mailto:andris@postalsys.com
|
|
6
|
+
Expires: 2027-06-01T00:00:00.000Z
|
|
7
|
+
Encryption: https://keys.openpgp.org/vks/v1/by-fingerprint/5D952A46E1D8C931F6364E01DC6C83F4D584D364
|
|
8
|
+
Preferred-Languages: en, et
|
|
9
|
+
Canonical: https://github.com/postalsys/imapflow/blob/master/SECURITY.txt
|
|
10
|
+
Policy: https://github.com/postalsys/imapflow/blob/master/SECURITY.md
|
|
11
|
+
-----BEGIN PGP SIGNATURE-----
|
|
12
|
+
|
|
13
|
+
iQJiBAEBCABMFiEEXZUqRuHYyTH2Nk4B3GyD9NWE02QFAmodTFwbFIAAAAAABAAO
|
|
14
|
+
bWFudTIsMi41KzEuMTIsMCwzEhxhbmRyaXNAcmVpbm1hbi5ldQAKCRDcbIP01YTT
|
|
15
|
+
ZGcgD/42Wdd07wEclXDm/lM0Ax+aX9sPjwV33DAICqhDoILf8yG4yPkCRuuTCayw
|
|
16
|
+
QLTB1pCi+tk8xVs9TH88bDwinfGVqzbmUyHSa2h32TDx/0/b+rg2LO2Ru8JtJkX8
|
|
17
|
+
CMY8/UhkouaQnHKDjkbLniF8HpQsBzOiPzvkfX91BY4HBwzE8aCC8MXC0zyjBkQX
|
|
18
|
+
dqVJ/fYMQKPkblKemnrMB/XM1jui2M7vgzpqilLcZqYv5oNFQhgpd+JOEVKMDUZv
|
|
19
|
+
0WaLrFWRegFvFApS/cIvzu5RLIhDWuL0ao577Cpdj1QE7nznfbBqBfGEzZotOShs
|
|
20
|
+
CdzdGloSt9C1gVLWItsncXByXVuNrF8l67hoOAnRwO4c8PKg3b4CvzBelmDiCWrS
|
|
21
|
+
73LGWmFwp1luyLrfZREUYsF5hqHCr6ULmDEAVp0MRtzxL56YjiS20d1sa9v96P4O
|
|
22
|
+
KFCvOT5LbCK/KqAFzhRzRXd3sawt5SXcR2FGgjFrRYNriyHNSk0k1h5IYv1kNoPb
|
|
23
|
+
VN+DxsOhORd2EsqqOBqHoEES472OOsmLTYa0A5z+zyA0IcaY5hXsMDrX9OMIUHUW
|
|
24
|
+
4JAOviDYyejefVJd8SsLrAmQ82p1jPY0LnuIyLz/sXDoe7QduNZIFKoUYw3KVybM
|
|
25
|
+
y0X7yP92/buzcZY41FOQT8dBFGDzboWQo+RPEh16qXEV75ss2A==
|
|
26
|
+
=Wlrz
|
|
27
|
+
-----END PGP SIGNATURE-----
|
package/lib/commands/starttls.js
CHANGED
|
@@ -15,6 +15,9 @@ module.exports = async connection => {
|
|
|
15
15
|
let response;
|
|
16
16
|
try {
|
|
17
17
|
response = await connection.exec('STARTTLS');
|
|
18
|
+
// Whether the server sent anything after the STARTTLS OK and before the TLS
|
|
19
|
+
// handshake. upgradeToSTARTTLS() uses this to reject a plaintext injection.
|
|
20
|
+
connection._starttlsHadTrailingData = !!(response && response.hasTrailingData);
|
|
18
21
|
response.next();
|
|
19
22
|
return true;
|
|
20
23
|
} catch (err) {
|
package/lib/commands/store.js
CHANGED
|
@@ -57,7 +57,7 @@ module.exports = async (connection, range, flags, options) => {
|
|
|
57
57
|
.map(flag => {
|
|
58
58
|
flag = formatFlag(flag);
|
|
59
59
|
|
|
60
|
-
if (!canUseFlag(connection.mailbox, flag) && operation !== 'remove') {
|
|
60
|
+
if (!canUseFlag(connection.mailbox, flag) && options.operation !== 'remove') {
|
|
61
61
|
return false;
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -16,6 +16,11 @@ const CURLY_CLOSE = 0x7d;
|
|
|
16
16
|
// Maximum allowed literal size: 1GB (1073741824 bytes)
|
|
17
17
|
const MAX_LITERAL_SIZE = 1024 * 1024 * 1024;
|
|
18
18
|
|
|
19
|
+
// Default maximum length of a single line (a response without a literal). Matches the literal cap:
|
|
20
|
+
// large literal-free responses (e.g. big SEARCH/LIST results) are legitimate, so this bound exists
|
|
21
|
+
// only to stop a server that never sends a line terminator, not to constrain normal traffic.
|
|
22
|
+
const MAX_LINE_SIZE = MAX_LITERAL_SIZE;
|
|
23
|
+
|
|
19
24
|
/**
|
|
20
25
|
* A Transform stream that parses raw IMAP protocol data from a socket into structured
|
|
21
26
|
* command/response objects. Reads binary input, splits it into lines delimited by LF,
|
|
@@ -34,6 +39,13 @@ class ImapStream extends Transform {
|
|
|
34
39
|
* @param {Object} [options.logger] - A pino-compatible logger instance. If not provided, a default child logger is created.
|
|
35
40
|
* @param {boolean} [options.logRaw] - If true, logs raw socket data at trace level.
|
|
36
41
|
* @param {boolean} [options.secureConnection] - Whether the connection uses TLS.
|
|
42
|
+
* @param {number} [options.maxLineLength] - Maximum allowed length (in bytes) of a single
|
|
43
|
+
* line (a response without a literal). Defaults to MAX_LITERAL_SIZE (1GB). Guards against a
|
|
44
|
+
* malicious or broken server that never sends a line terminator, which would otherwise grow
|
|
45
|
+
* the internal line buffer without bound.
|
|
46
|
+
* @param {number} [options.maxLiteralSize] - Maximum allowed size (in bytes) of a single
|
|
47
|
+
* literal block. Defaults to MAX_LITERAL_SIZE (1GB). Lower it to bound peak memory
|
|
48
|
+
* allocation against a malicious or broken server announcing an oversized literal.
|
|
37
49
|
*/
|
|
38
50
|
constructor(options) {
|
|
39
51
|
super({
|
|
@@ -55,10 +67,23 @@ class ImapStream extends Transform {
|
|
|
55
67
|
|
|
56
68
|
this.readBytesCounter = 0;
|
|
57
69
|
|
|
70
|
+
// Maximum length of a single line (response without a literal). Bounds the line buffer
|
|
71
|
+
// so a server that never sends a line terminator cannot exhaust memory. A non-negative
|
|
72
|
+
// integer is honored as-is (including 0); anything else falls back to the default, so an
|
|
73
|
+
// explicit 0 is not silently swallowed into the 1GB default the way `|| MAX_LINE_SIZE` was.
|
|
74
|
+
this.maxLineLength = Number.isInteger(this.options.maxLineLength) && this.options.maxLineLength >= 0 ? this.options.maxLineLength : MAX_LINE_SIZE;
|
|
75
|
+
|
|
76
|
+
// Maximum size of a single literal block. Bounds peak memory allocation so a server
|
|
77
|
+
// announcing an oversized literal cannot exhaust memory. As above, a non-negative integer
|
|
78
|
+
// (including an explicit 0, meaning "reject all non-empty literals") is honored as-is.
|
|
79
|
+
this.maxLiteralSize =
|
|
80
|
+
Number.isInteger(this.options.maxLiteralSize) && this.options.maxLiteralSize >= 0 ? this.options.maxLiteralSize : MAX_LITERAL_SIZE;
|
|
81
|
+
|
|
58
82
|
this.state = LINE;
|
|
59
83
|
this.literalWaiting = 0;
|
|
60
84
|
this.inputBuffer = []; // lines
|
|
61
85
|
this.lineBuffer = []; // current line
|
|
86
|
+
this.lineBytes = 0; // bytes currently buffered for the in-progress line
|
|
62
87
|
this.literalBuffer = [];
|
|
63
88
|
this.literals = [];
|
|
64
89
|
|
|
@@ -102,7 +127,7 @@ class ImapStream extends Transform {
|
|
|
102
127
|
// Scan backwards through the line to find an IMAP literal marker: {size}\r\n
|
|
103
128
|
// The format is: '{' followed by one or more ASCII digits followed by '}'
|
|
104
129
|
let numBytes = [];
|
|
105
|
-
for (; pos
|
|
130
|
+
for (; pos >= 0; pos--) {
|
|
106
131
|
let c = line[pos];
|
|
107
132
|
if (c >= NUM_0 && c <= NUM_9) {
|
|
108
133
|
numBytes.unshift(c);
|
|
@@ -111,11 +136,11 @@ class ImapStream extends Transform {
|
|
|
111
136
|
if (c === CURLY_OPEN && numBytes.length) {
|
|
112
137
|
const literalSize = Number(Buffer.from(numBytes).toString());
|
|
113
138
|
|
|
114
|
-
if (literalSize >
|
|
115
|
-
const err = new Error(`Literal size ${literalSize} exceeds maximum allowed size of ${
|
|
139
|
+
if (literalSize > this.maxLiteralSize) {
|
|
140
|
+
const err = new Error(`Literal size ${literalSize} exceeds maximum allowed size of ${this.maxLiteralSize} bytes`);
|
|
116
141
|
err.code = 'LiteralTooLarge';
|
|
117
142
|
err.literalSize = literalSize;
|
|
118
|
-
err.maxSize =
|
|
143
|
+
err.maxSize = this.maxLiteralSize;
|
|
119
144
|
this.emit('error', err);
|
|
120
145
|
return false;
|
|
121
146
|
}
|
|
@@ -158,6 +183,7 @@ class ImapStream extends Transform {
|
|
|
158
183
|
|
|
159
184
|
this.inputBuffer.push(line);
|
|
160
185
|
this.lineBuffer = [];
|
|
186
|
+
this.lineBytes = 0;
|
|
161
187
|
|
|
162
188
|
// try to detect if this is a literal start
|
|
163
189
|
if (this.checkLiteralMarker(line)) {
|
|
@@ -182,15 +208,35 @@ class ImapStream extends Transform {
|
|
|
182
208
|
}
|
|
183
209
|
|
|
184
210
|
if (payload.length) {
|
|
211
|
+
// Whether more buffered input already followed this command on the
|
|
212
|
+
// wire — more bytes in this chunk or another queued chunk. Captured
|
|
213
|
+
// per emitted command (immutable on the pushed object) so a later
|
|
214
|
+
// command cannot overwrite it; consumers that care about pipelining
|
|
215
|
+
// boundaries can read it from the pushed object.
|
|
216
|
+
let trailingAfterLine = lineStart < chunk.length || this.inputQueue.length > 0;
|
|
185
217
|
await new Promise(resolve => {
|
|
186
|
-
this.push({ payload, literals, next: resolve });
|
|
218
|
+
this.push({ payload, literals, next: resolve, trailingAfterLine });
|
|
187
219
|
});
|
|
188
220
|
}
|
|
189
221
|
}
|
|
190
222
|
}
|
|
191
223
|
}
|
|
192
224
|
if (lineStart < chunk.length) {
|
|
193
|
-
|
|
225
|
+
// No line terminator was found in the remaining bytes; carry the tail over to
|
|
226
|
+
// the next chunk. Enforce the line-length cap here, since this is the only
|
|
227
|
+
// path that grows the line buffer across chunks.
|
|
228
|
+
let tail = chunk.slice(lineStart);
|
|
229
|
+
let lineLength = this.lineBytes + tail.length;
|
|
230
|
+
if (lineLength > this.maxLineLength) {
|
|
231
|
+
const err = new Error(`Line length ${lineLength} exceeds maximum allowed size of ${this.maxLineLength} bytes`);
|
|
232
|
+
err.code = 'LineTooLarge';
|
|
233
|
+
err.lineLength = lineLength;
|
|
234
|
+
err.maxSize = this.maxLineLength;
|
|
235
|
+
this.emit('error', err);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
this.lineBytes = lineLength;
|
|
239
|
+
this.lineBuffer.push(tail);
|
|
194
240
|
}
|
|
195
241
|
break;
|
|
196
242
|
}
|
|
@@ -303,6 +349,7 @@ class ImapStream extends Transform {
|
|
|
303
349
|
_destroy(err, callback) {
|
|
304
350
|
this.inputBuffer = [];
|
|
305
351
|
this.lineBuffer = [];
|
|
352
|
+
this.lineBytes = 0;
|
|
306
353
|
this.literalBuffer = [];
|
|
307
354
|
this.literals = [];
|
|
308
355
|
// Clear inputQueue and call any pending callbacks
|
|
@@ -349,6 +349,13 @@ class TokenParser {
|
|
|
349
349
|
this.currentNode.type = 'ATOM';
|
|
350
350
|
// jump i to the ']'
|
|
351
351
|
i = this.str.indexOf(']', i + 10);
|
|
352
|
+
if (i < 0) {
|
|
353
|
+
// Malformed REFERRAL with no closing ']'. Consume the rest
|
|
354
|
+
// of the string (there is no ']' to exclude) instead of
|
|
355
|
+
// computing a negative-index substring, which would yield
|
|
356
|
+
// garbage.
|
|
357
|
+
i = this.str.length;
|
|
358
|
+
}
|
|
352
359
|
this.currentNode.endPos = this.pos + i - 1;
|
|
353
360
|
this.currentNode.value = this.str.substring(this.currentNode.startPos - this.pos, this.currentNode.endPos - this.pos + 1);
|
|
354
361
|
this.currentNode = this.currentNode.parentNode;
|
package/lib/imap-flow.d.ts
CHANGED
|
@@ -60,6 +60,17 @@ export interface ImapFlowOptions {
|
|
|
60
60
|
greetingTimeout?: number;
|
|
61
61
|
/** How long to wait for socket inactivity before timing out the connection. Defaults to 5 minutes */
|
|
62
62
|
socketTimeout?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Maximum allowed length in bytes of a single response line (a response without a literal).
|
|
65
|
+
* Guards against a malicious or broken server that never sends a line terminator. Defaults to
|
|
66
|
+
* 1GB.
|
|
67
|
+
*/
|
|
68
|
+
maxLineLength?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Maximum allowed size in bytes of a single IMAP literal block. Bounds peak memory allocation
|
|
71
|
+
* against a malicious or broken server announcing an oversized literal. Defaults to 1GB.
|
|
72
|
+
*/
|
|
73
|
+
maxLiteralSize?: number;
|
|
63
74
|
/**
|
|
64
75
|
* Threshold in milliseconds for warning that a mailbox lock has been held
|
|
65
76
|
* for a long time (diagnostic for forgotten release() calls). Defaults to
|